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]