Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Make indexers work for interface objects
Makes the following work instead of throwing an exception:

```python
from System.Collections.Generic import Dictionary, IDictionary
d = IDictionary[str, str](Dictionary[str, str]())
d["one"] = "1"
assert d["one"] == "1"
```
  • Loading branch information
danabr committed Oct 6, 2020
commit 9c870c120718ce8c7cb7729cf20189587a32c402
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions src/runtime/arrayobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
/// <summary>
/// Implements __getitem__ for array types.
/// </summary>
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);
Expand Down Expand Up @@ -133,7 +133,7 @@ public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
/// <summary>
/// Implements __setitem__ for array types.
/// </summary>
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;
Expand Down
124 changes: 124 additions & 0 deletions src/runtime/classbase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,5 +302,129 @@ public static void tp_dealloc(IntPtr ob)
Runtime.XDecref(self.tpHandle);
self.gcHandle.Free();
}


/// <summary>
/// Implements __getitem__ for reflected classes and value types.
/// </summary>
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;
}


/// <summary>
/// Implements __setitem__ for reflected classes and value types.
/// </summary>
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;
}
}
}
126 changes: 0 additions & 126 deletions src/runtime/classobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,131 +152,5 @@ public override IntPtr type_subscript(IntPtr idx)
}
return Exceptions.RaiseTypeError("unsubscriptable object");
}


/// <summary>
/// Implements __getitem__ for reflected classes and value types.
/// </summary>
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;
}


/// <summary>
/// Implements __setitem__ for reflected classes and value types.
/// </summary>
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;
}
}
}
8 changes: 8 additions & 0 deletions src/tests/test_indexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,3 +595,11 @@ 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"