diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51859c060..964fc77b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,7 @@ details about the cause of the failure
- Fixed a bug where all .NET class instances were considered Iterable
- Fix incorrect choice of method to invoke when using keyword arguments.
- Fix non-delegate types incorrectly appearing as callable.
+- Indexers can now be used with interface objects
## [2.5.0][] - 2020-06-14
diff --git a/src/runtime/arrayobject.cs b/src/runtime/arrayobject.cs
index 2d50b3a1d..364d91969 100644
--- a/src/runtime/arrayobject.cs
+++ b/src/runtime/arrayobject.cs
@@ -40,7 +40,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
///
/// Implements __getitem__ for array types.
///
- public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
+ public new static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
{
var obj = (CLRObject)GetManagedObject(ob);
var arrObj = (ArrayObject)GetManagedObjectType(ob);
@@ -133,7 +133,7 @@ public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
///
/// Implements __setitem__ for array types.
///
- public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
+ public static new int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
{
var obj = (CLRObject)GetManagedObject(ob);
var items = obj.inst as Array;
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index 43ec6ea72..972380928 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -302,5 +302,129 @@ public static void tp_dealloc(IntPtr ob)
Runtime.XDecref(self.tpHandle);
self.gcHandle.Free();
}
+
+
+ ///
+ /// Implements __getitem__ for reflected classes and value types.
+ ///
+ public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
+ {
+ IntPtr tp = Runtime.PyObject_TYPE(ob);
+ var cls = (ClassBase)GetManagedObject(tp);
+
+ if (cls.indexer == null || !cls.indexer.CanGet)
+ {
+ Exceptions.SetError(Exceptions.TypeError, "unindexable object");
+ return IntPtr.Zero;
+ }
+
+ // Arg may be a tuple in the case of an indexer with multiple
+ // parameters. If so, use it directly, else make a new tuple
+ // with the index arg (method binders expect arg tuples).
+ IntPtr args = idx;
+ var free = false;
+
+ if (!Runtime.PyTuple_Check(idx))
+ {
+ args = Runtime.PyTuple_New(1);
+ Runtime.XIncref(idx);
+ Runtime.PyTuple_SetItem(args, 0, idx);
+ free = true;
+ }
+
+ IntPtr value;
+
+ try
+ {
+ value = cls.indexer.GetItem(ob, args);
+ }
+ finally
+ {
+ if (free)
+ {
+ Runtime.XDecref(args);
+ }
+ }
+ return value;
+ }
+
+
+ ///
+ /// Implements __setitem__ for reflected classes and value types.
+ ///
+ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
+ {
+ IntPtr tp = Runtime.PyObject_TYPE(ob);
+ var cls = (ClassBase)GetManagedObject(tp);
+
+ if (cls.indexer == null || !cls.indexer.CanSet)
+ {
+ Exceptions.SetError(Exceptions.TypeError, "object doesn't support item assignment");
+ return -1;
+ }
+
+ // Arg may be a tuple in the case of an indexer with multiple
+ // parameters. If so, use it directly, else make a new tuple
+ // with the index arg (method binders expect arg tuples).
+ IntPtr args = idx;
+ var free = false;
+
+ if (!Runtime.PyTuple_Check(idx))
+ {
+ args = Runtime.PyTuple_New(1);
+ Runtime.XIncref(idx);
+ Runtime.PyTuple_SetItem(args, 0, idx);
+ free = true;
+ }
+
+ // Get the args passed in.
+ var i = Runtime.PyTuple_Size(args);
+ IntPtr defaultArgs = cls.indexer.GetDefaultArgs(args);
+ var numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs);
+ var temp = i + numOfDefaultArgs;
+ IntPtr real = Runtime.PyTuple_New(temp + 1);
+ for (var n = 0; n < i; n++)
+ {
+ IntPtr item = Runtime.PyTuple_GetItem(args, n);
+ Runtime.XIncref(item);
+ Runtime.PyTuple_SetItem(real, n, item);
+ }
+
+ // Add Default Args if needed
+ for (var n = 0; n < numOfDefaultArgs; n++)
+ {
+ IntPtr item = Runtime.PyTuple_GetItem(defaultArgs, n);
+ Runtime.XIncref(item);
+ Runtime.PyTuple_SetItem(real, n + i, item);
+ }
+ // no longer need defaultArgs
+ Runtime.XDecref(defaultArgs);
+ i = temp;
+
+ // Add value to argument list
+ Runtime.XIncref(v);
+ Runtime.PyTuple_SetItem(real, i, v);
+
+ try
+ {
+ cls.indexer.SetItem(ob, real);
+ }
+ finally
+ {
+ Runtime.XDecref(real);
+
+ if (free)
+ {
+ Runtime.XDecref(args);
+ }
+ }
+
+ if (Exceptions.ErrorOccurred())
+ {
+ return -1;
+ }
+
+ return 0;
+ }
}
}
diff --git a/src/runtime/classobject.cs b/src/runtime/classobject.cs
index ada24358a..d7624ed6e 100644
--- a/src/runtime/classobject.cs
+++ b/src/runtime/classobject.cs
@@ -152,131 +152,5 @@ public override IntPtr type_subscript(IntPtr idx)
}
return Exceptions.RaiseTypeError("unsubscriptable object");
}
-
-
- ///
- /// Implements __getitem__ for reflected classes and value types.
- ///
- public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
- {
- //ManagedType self = GetManagedObject(ob);
- IntPtr tp = Runtime.PyObject_TYPE(ob);
- var cls = (ClassBase)GetManagedObject(tp);
-
- if (cls.indexer == null || !cls.indexer.CanGet)
- {
- Exceptions.SetError(Exceptions.TypeError, "unindexable object");
- return IntPtr.Zero;
- }
-
- // Arg may be a tuple in the case of an indexer with multiple
- // parameters. If so, use it directly, else make a new tuple
- // with the index arg (method binders expect arg tuples).
- IntPtr args = idx;
- var free = false;
-
- if (!Runtime.PyTuple_Check(idx))
- {
- args = Runtime.PyTuple_New(1);
- Runtime.XIncref(idx);
- Runtime.PyTuple_SetItem(args, 0, idx);
- free = true;
- }
-
- IntPtr value;
-
- try
- {
- value = cls.indexer.GetItem(ob, args);
- }
- finally
- {
- if (free)
- {
- Runtime.XDecref(args);
- }
- }
- return value;
- }
-
-
- ///
- /// Implements __setitem__ for reflected classes and value types.
- ///
- public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
- {
- //ManagedType self = GetManagedObject(ob);
- IntPtr tp = Runtime.PyObject_TYPE(ob);
- var cls = (ClassBase)GetManagedObject(tp);
-
- if (cls.indexer == null || !cls.indexer.CanSet)
- {
- Exceptions.SetError(Exceptions.TypeError, "object doesn't support item assignment");
- return -1;
- }
-
- // Arg may be a tuple in the case of an indexer with multiple
- // parameters. If so, use it directly, else make a new tuple
- // with the index arg (method binders expect arg tuples).
- IntPtr args = idx;
- var free = false;
-
- if (!Runtime.PyTuple_Check(idx))
- {
- args = Runtime.PyTuple_New(1);
- Runtime.XIncref(idx);
- Runtime.PyTuple_SetItem(args, 0, idx);
- free = true;
- }
-
- // Get the args passed in.
- var i = Runtime.PyTuple_Size(args);
- IntPtr defaultArgs = cls.indexer.GetDefaultArgs(args);
- var numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs);
- var temp = i + numOfDefaultArgs;
- IntPtr real = Runtime.PyTuple_New(temp + 1);
- for (var n = 0; n < i; n++)
- {
- IntPtr item = Runtime.PyTuple_GetItem(args, n);
- Runtime.XIncref(item);
- Runtime.PyTuple_SetItem(real, n, item);
- }
-
- // Add Default Args if needed
- for (var n = 0; n < numOfDefaultArgs; n++)
- {
- IntPtr item = Runtime.PyTuple_GetItem(defaultArgs, n);
- Runtime.XIncref(item);
- Runtime.PyTuple_SetItem(real, n + i, item);
- }
- // no longer need defaultArgs
- Runtime.XDecref(defaultArgs);
- i = temp;
-
- // Add value to argument list
- Runtime.XIncref(v);
- Runtime.PyTuple_SetItem(real, i, v);
-
- try
- {
- cls.indexer.SetItem(ob, real);
- }
- finally
- {
- Runtime.XDecref(real);
-
- if (free)
- {
- Runtime.XDecref(args);
- }
- }
-
- if (Exceptions.ErrorOccurred())
- {
- return -1;
- }
-
- return 0;
- }
}
}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 985f7f19a..a64f91e75 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -171,6 +171,28 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
Marshal.WriteIntPtr(type, TypeOffset.tp_iter, IntPtr.Zero);
}
+
+ // Only set mp_subscript and mp_ass_subscript for types with indexers
+ if (impl is ClassBase cb)
+ {
+ if (!(impl is ArrayObject))
+ {
+ if (cb.indexer == null || !cb.indexer.CanGet)
+ {
+ Marshal.WriteIntPtr(type, TypeOffset.mp_subscript, IntPtr.Zero);
+ }
+ if (cb.indexer == null || !cb.indexer.CanSet)
+ {
+ Marshal.WriteIntPtr(type, TypeOffset.mp_ass_subscript, IntPtr.Zero);
+ }
+ }
+ }
+ else
+ {
+ Marshal.WriteIntPtr(type, TypeOffset.mp_subscript, IntPtr.Zero);
+ Marshal.WriteIntPtr(type, TypeOffset.mp_ass_subscript, IntPtr.Zero);
+ }
+
if (base_ != IntPtr.Zero)
{
Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_);
diff --git a/src/tests/test_array.py b/src/tests/test_array.py
index 990af550a..015b5bdde 100644
--- a/src/tests/test_array.py
+++ b/src/tests/test_array.py
@@ -1321,16 +1321,14 @@ def test_array_abuse():
with pytest.raises(TypeError):
Test.PublicArrayTest.__getitem__(0, 0)
- with pytest.raises(TypeError):
+ with pytest.raises(AttributeError):
Test.PublicArrayTest.__setitem__(0, 0, 0)
- with pytest.raises(TypeError):
- desc = Test.PublicArrayTest.__dict__['__getitem__']
- desc(0, 0)
+ with pytest.raises(KeyError):
+ Test.PublicArrayTest.__dict__['__getitem__']
- with pytest.raises(TypeError):
- desc = Test.PublicArrayTest.__dict__['__setitem__']
- desc(0, 0, 0)
+ with pytest.raises(KeyError):
+ Test.PublicArrayTest.__dict__['__setitem__']
def test_iterator_to_array():
diff --git a/src/tests/test_indexer.py b/src/tests/test_indexer.py
index 6a36a2519..6da8e1386 100644
--- a/src/tests/test_indexer.py
+++ b/src/tests/test_indexer.py
@@ -42,7 +42,7 @@ def test_internal_indexer():
with pytest.raises(TypeError):
Test.InternalIndexerTest.__getitem__(ob, 0)
- with pytest.raises(TypeError):
+ with pytest.raises(AttributeError):
ob.__getitem__(0)
@@ -56,7 +56,7 @@ def test_private_indexer():
with pytest.raises(TypeError):
Test.PrivateIndexerTest.__getitem__(ob, 0)
- with pytest.raises(TypeError):
+ with pytest.raises(AttributeError):
ob.__getitem__(0)
@@ -595,3 +595,22 @@ def test_indexer_abuse():
with pytest.raises(AttributeError):
del ob.__setitem__
+
+
+def test_indexer_accessed_through_interface():
+ """Test that indexers can be accessed through interfaces"""
+ from System.Collections.Generic import Dictionary, IDictionary
+ d = IDictionary[str, str](Dictionary[str, str]())
+ d["one"] = "1"
+ assert d["one"] == "1"
+
+
+def test_using_indexer_on_object_without_indexer():
+ """Test using subscript syntax on an object an without indexer raises"""
+ from System import Object
+ o = Object()
+ with pytest.raises(TypeError):
+ o[0]
+
+ with pytest.raises(TypeError):
+ o[0] = 1
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy