diff --git a/AUTHORS.md b/AUTHORS.md index eeafd98e4..69e7b5c4a 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -60,6 +60,7 @@ - Sean Freitag ([@cowboygneox](https://github.com/cowboygneox)) - Serge Weinstock ([@sweinst](https://github.com/sweinst)) - Simon Mourier ([@smourier](https://github.com/smourier)) +- Tom Minka ([@tminka](https://github.com/tminka)) - Viktoria Kovescses ([@vkovec](https://github.com/vkovec)) - Ville M. Vainio ([@vivainio](https://github.com/vivainio)) - Virgil Dupras ([@hsoft](https://github.com/hsoft)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27df6dcbe..2420d40ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ details about the cause of the failure - Made it possible to use `__len__` also on `ICollection<>` interface objects - Made it possible to call `ToString`, `GetHashCode`, and `GetType` on inteface objects - Fixed objects returned by enumerating `PyObject` being disposed too soon +- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException ### Removed diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs index a62e76050..7cb6938bc 100644 --- a/src/runtime/classbase.cs +++ b/src/runtime/classbase.cs @@ -54,7 +54,7 @@ public virtual IntPtr type_subscript(IntPtr idx) return c.pyHandle; } - return Exceptions.RaiseTypeError("no type matches params"); + return Exceptions.RaiseTypeError($"{type.Namespace}.{type.Name} does not accept {types.Length} generic parameters"); } /// diff --git a/src/runtime/genericutil.cs b/src/runtime/genericutil.cs index c583e64e2..92b847e98 100644 --- a/src/runtime/genericutil.cs +++ b/src/runtime/genericutil.cs @@ -9,14 +9,13 @@ namespace Python.Runtime /// This class is responsible for efficiently maintaining the bits /// of information we need to support aliases with 'nice names'. /// - internal class GenericUtil + internal static class GenericUtil { + /// + /// Maps namespace -> generic base name -> list of generic type names + /// private static Dictionary>> mapping; - private GenericUtil() - { - } - public static void Reset() { mapping = new Dictionary>>(); @@ -25,6 +24,7 @@ public static void Reset() /// /// Register a generic type that appears in a given namespace. /// + /// A generic type definition (t.IsGenericTypeDefinition must be true) internal static void Register(Type t) { if (null == t.Namespace || null == t.Name) @@ -32,22 +32,15 @@ internal static void Register(Type t) return; } - Dictionary> nsmap = null; - mapping.TryGetValue(t.Namespace, out nsmap); - if (nsmap == null) + Dictionary> nsmap; + if (!mapping.TryGetValue(t.Namespace, out nsmap)) { nsmap = new Dictionary>(); mapping[t.Namespace] = nsmap; } - string basename = t.Name; - int tick = basename.IndexOf("`"); - if (tick > -1) - { - basename = basename.Substring(0, tick); - } - List gnames = null; - nsmap.TryGetValue(basename, out gnames); - if (gnames == null) + string basename = GetBasename(t.Name); + List gnames; + if (!nsmap.TryGetValue(basename, out gnames)) { gnames = new List(); nsmap[basename] = gnames; @@ -60,9 +53,8 @@ internal static void Register(Type t) /// public static List GetGenericBaseNames(string ns) { - Dictionary> nsmap = null; - mapping.TryGetValue(ns, out nsmap); - if (nsmap == null) + Dictionary> nsmap; + if (!mapping.TryGetValue(ns, out nsmap)) { return null; } @@ -75,64 +67,41 @@ public static List GetGenericBaseNames(string ns) } /// - /// xxx + /// Finds a generic type with the given number of generic parameters and the same name and namespace as . /// public static Type GenericForType(Type t, int paramCount) { return GenericByName(t.Namespace, t.Name, paramCount); } - public static Type GenericByName(string ns, string name, int paramCount) - { - foreach (Type t in GenericsByName(ns, name)) - { - if (t.GetGenericArguments().Length == paramCount) - { - return t; - } - } - return null; - } - - public static List GenericsForType(Type t) - { - return GenericsByName(t.Namespace, t.Name); - } - - public static List GenericsByName(string ns, string basename) + /// + /// Finds a generic type in the given namespace with the given name and number of generic parameters. + /// + public static Type GenericByName(string ns, string basename, int paramCount) { - Dictionary> nsmap = null; - mapping.TryGetValue(ns, out nsmap); - if (nsmap == null) + Dictionary> nsmap; + if (!mapping.TryGetValue(ns, out nsmap)) { return null; } - int tick = basename.IndexOf("`"); - if (tick > -1) - { - basename = basename.Substring(0, tick); - } - - List names = null; - nsmap.TryGetValue(basename, out names); - if (names == null) + List names; + if (!nsmap.TryGetValue(GetBasename(basename), out names)) { return null; } - var result = new List(); foreach (string name in names) { string qname = ns + "." + name; Type o = AssemblyManager.LookupTypes(qname).FirstOrDefault(); - if (o != null) + if (o != null && o.GetGenericArguments().Length == paramCount) { - result.Add(o); + return o; } } - return result; + return null; } /// @@ -140,13 +109,12 @@ public static List GenericsByName(string ns, string basename) /// public static string GenericNameForBaseName(string ns, string name) { - Dictionary> nsmap = null; - mapping.TryGetValue(ns, out nsmap); - if (nsmap == null) + Dictionary> nsmap; + if (!mapping.TryGetValue(ns, out nsmap)) { return null; } - List gnames = null; + List gnames; nsmap.TryGetValue(name, out gnames); if (gnames?.Count > 0) { @@ -154,5 +122,18 @@ public static string GenericNameForBaseName(string ns, string name) } return null; } + + private static string GetBasename(string name) + { + int tick = name.IndexOf("`"); + if (tick > -1) + { + return name.Substring(0, tick); + } + else + { + return name; + } + } } } diff --git a/src/tests/test_generic.py b/src/tests/test_generic.py index c7e5a8d4d..c865ab32f 100644 --- a/src/tests/test_generic.py +++ b/src/tests/test_generic.py @@ -745,3 +745,8 @@ def test_nested_generic_class(): """Check nested generic classes.""" # TODO NotImplemented pass + +def test_missing_generic_type(): + from System.Collections import IList + with pytest.raises(TypeError): + IList[bool]