Skip to content

Commit 00c22a5

Browse files
danabrlostmsu
authored andcommitted
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" ```
1 parent 03cf4ac commit 00c22a5

File tree

5 files changed

+135
-128
lines changed

5 files changed

+135
-128
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ details about the cause of the failure
2929
- Fixed a bug where all .NET class instances were considered Iterable
3030
- Fix incorrect choice of method to invoke when using keyword arguments.
3131
- Fix non-delegate types incorrectly appearing as callable.
32+
- Indexers can now be used with interface objects
3233

3334
## [2.5.0][] - 2020-06-14
3435

src/runtime/arrayobject.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
4040
/// <summary>
4141
/// Implements __getitem__ for array types.
4242
/// </summary>
43-
public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
43+
public new static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
4444
{
4545
var obj = (CLRObject)GetManagedObject(ob);
4646
var arrObj = (ArrayObject)GetManagedObjectType(ob);
@@ -133,7 +133,7 @@ public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
133133
/// <summary>
134134
/// Implements __setitem__ for array types.
135135
/// </summary>
136-
public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
136+
public static new int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
137137
{
138138
var obj = (CLRObject)GetManagedObject(ob);
139139
var items = obj.inst as Array;

src/runtime/classbase.cs

+124
Original file line numberDiff line numberDiff line change
@@ -302,5 +302,129 @@ public static void tp_dealloc(IntPtr ob)
302302
Runtime.XDecref(self.tpHandle);
303303
self.gcHandle.Free();
304304
}
305+
306+
307+
/// <summary>
308+
/// Implements __getitem__ for reflected classes and value types.
309+
/// </summary>
310+
public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
311+
{
312+
IntPtr tp = Runtime.PyObject_TYPE(ob);
313+
var cls = (ClassBase)GetManagedObject(tp);
314+
315+
if (cls.indexer == null || !cls.indexer.CanGet)
316+
{
317+
Exceptions.SetError(Exceptions.TypeError, "unindexable object");
318+
return IntPtr.Zero;
319+
}
320+
321+
// Arg may be a tuple in the case of an indexer with multiple
322+
// parameters. If so, use it directly, else make a new tuple
323+
// with the index arg (method binders expect arg tuples).
324+
IntPtr args = idx;
325+
var free = false;
326+
327+
if (!Runtime.PyTuple_Check(idx))
328+
{
329+
args = Runtime.PyTuple_New(1);
330+
Runtime.XIncref(idx);
331+
Runtime.PyTuple_SetItem(args, 0, idx);
332+
free = true;
333+
}
334+
335+
IntPtr value;
336+
337+
try
338+
{
339+
value = cls.indexer.GetItem(ob, args);
340+
}
341+
finally
342+
{
343+
if (free)
344+
{
345+
Runtime.XDecref(args);
346+
}
347+
}
348+
return value;
349+
}
350+
351+
352+
/// <summary>
353+
/// Implements __setitem__ for reflected classes and value types.
354+
/// </summary>
355+
public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
356+
{
357+
IntPtr tp = Runtime.PyObject_TYPE(ob);
358+
var cls = (ClassBase)GetManagedObject(tp);
359+
360+
if (cls.indexer == null || !cls.indexer.CanSet)
361+
{
362+
Exceptions.SetError(Exceptions.TypeError, "object doesn't support item assignment");
363+
return -1;
364+
}
365+
366+
// Arg may be a tuple in the case of an indexer with multiple
367+
// parameters. If so, use it directly, else make a new tuple
368+
// with the index arg (method binders expect arg tuples).
369+
IntPtr args = idx;
370+
var free = false;
371+
372+
if (!Runtime.PyTuple_Check(idx))
373+
{
374+
args = Runtime.PyTuple_New(1);
375+
Runtime.XIncref(idx);
376+
Runtime.PyTuple_SetItem(args, 0, idx);
377+
free = true;
378+
}
379+
380+
// Get the args passed in.
381+
var i = Runtime.PyTuple_Size(args);
382+
IntPtr defaultArgs = cls.indexer.GetDefaultArgs(args);
383+
var numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs);
384+
var temp = i + numOfDefaultArgs;
385+
IntPtr real = Runtime.PyTuple_New(temp + 1);
386+
for (var n = 0; n < i; n++)
387+
{
388+
IntPtr item = Runtime.PyTuple_GetItem(args, n);
389+
Runtime.XIncref(item);
390+
Runtime.PyTuple_SetItem(real, n, item);
391+
}
392+
393+
// Add Default Args if needed
394+
for (var n = 0; n < numOfDefaultArgs; n++)
395+
{
396+
IntPtr item = Runtime.PyTuple_GetItem(defaultArgs, n);
397+
Runtime.XIncref(item);
398+
Runtime.PyTuple_SetItem(real, n + i, item);
399+
}
400+
// no longer need defaultArgs
401+
Runtime.XDecref(defaultArgs);
402+
i = temp;
403+
404+
// Add value to argument list
405+
Runtime.XIncref(v);
406+
Runtime.PyTuple_SetItem(real, i, v);
407+
408+
try
409+
{
410+
cls.indexer.SetItem(ob, real);
411+
}
412+
finally
413+
{
414+
Runtime.XDecref(real);
415+
416+
if (free)
417+
{
418+
Runtime.XDecref(args);
419+
}
420+
}
421+
422+
if (Exceptions.ErrorOccurred())
423+
{
424+
return -1;
425+
}
426+
427+
return 0;
428+
}
305429
}
306430
}

src/runtime/classobject.cs

-126
Original file line numberDiff line numberDiff line change
@@ -152,131 +152,5 @@ public override IntPtr type_subscript(IntPtr idx)
152152
}
153153
return Exceptions.RaiseTypeError("unsubscriptable object");
154154
}
155-
156-
157-
/// <summary>
158-
/// Implements __getitem__ for reflected classes and value types.
159-
/// </summary>
160-
public static IntPtr mp_subscript(IntPtr ob, IntPtr idx)
161-
{
162-
//ManagedType self = GetManagedObject(ob);
163-
IntPtr tp = Runtime.PyObject_TYPE(ob);
164-
var cls = (ClassBase)GetManagedObject(tp);
165-
166-
if (cls.indexer == null || !cls.indexer.CanGet)
167-
{
168-
Exceptions.SetError(Exceptions.TypeError, "unindexable object");
169-
return IntPtr.Zero;
170-
}
171-
172-
// Arg may be a tuple in the case of an indexer with multiple
173-
// parameters. If so, use it directly, else make a new tuple
174-
// with the index arg (method binders expect arg tuples).
175-
IntPtr args = idx;
176-
var free = false;
177-
178-
if (!Runtime.PyTuple_Check(idx))
179-
{
180-
args = Runtime.PyTuple_New(1);
181-
Runtime.XIncref(idx);
182-
Runtime.PyTuple_SetItem(args, 0, idx);
183-
free = true;
184-
}
185-
186-
IntPtr value;
187-
188-
try
189-
{
190-
value = cls.indexer.GetItem(ob, args);
191-
}
192-
finally
193-
{
194-
if (free)
195-
{
196-
Runtime.XDecref(args);
197-
}
198-
}
199-
return value;
200-
}
201-
202-
203-
/// <summary>
204-
/// Implements __setitem__ for reflected classes and value types.
205-
/// </summary>
206-
public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v)
207-
{
208-
//ManagedType self = GetManagedObject(ob);
209-
IntPtr tp = Runtime.PyObject_TYPE(ob);
210-
var cls = (ClassBase)GetManagedObject(tp);
211-
212-
if (cls.indexer == null || !cls.indexer.CanSet)
213-
{
214-
Exceptions.SetError(Exceptions.TypeError, "object doesn't support item assignment");
215-
return -1;
216-
}
217-
218-
// Arg may be a tuple in the case of an indexer with multiple
219-
// parameters. If so, use it directly, else make a new tuple
220-
// with the index arg (method binders expect arg tuples).
221-
IntPtr args = idx;
222-
var free = false;
223-
224-
if (!Runtime.PyTuple_Check(idx))
225-
{
226-
args = Runtime.PyTuple_New(1);
227-
Runtime.XIncref(idx);
228-
Runtime.PyTuple_SetItem(args, 0, idx);
229-
free = true;
230-
}
231-
232-
// Get the args passed in.
233-
var i = Runtime.PyTuple_Size(args);
234-
IntPtr defaultArgs = cls.indexer.GetDefaultArgs(args);
235-
var numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs);
236-
var temp = i + numOfDefaultArgs;
237-
IntPtr real = Runtime.PyTuple_New(temp + 1);
238-
for (var n = 0; n < i; n++)
239-
{
240-
IntPtr item = Runtime.PyTuple_GetItem(args, n);
241-
Runtime.XIncref(item);
242-
Runtime.PyTuple_SetItem(real, n, item);
243-
}
244-
245-
// Add Default Args if needed
246-
for (var n = 0; n < numOfDefaultArgs; n++)
247-
{
248-
IntPtr item = Runtime.PyTuple_GetItem(defaultArgs, n);
249-
Runtime.XIncref(item);
250-
Runtime.PyTuple_SetItem(real, n + i, item);
251-
}
252-
// no longer need defaultArgs
253-
Runtime.XDecref(defaultArgs);
254-
i = temp;
255-
256-
// Add value to argument list
257-
Runtime.XIncref(v);
258-
Runtime.PyTuple_SetItem(real, i, v);
259-
260-
try
261-
{
262-
cls.indexer.SetItem(ob, real);
263-
}
264-
finally
265-
{
266-
Runtime.XDecref(real);
267-
268-
if (free)
269-
{
270-
Runtime.XDecref(args);
271-
}
272-
}
273-
274-
if (Exceptions.ErrorOccurred())
275-
{
276-
return -1;
277-
}
278-
279-
return 0;
280-
}
281155
}
282156
}

src/tests/test_indexer.py

+8
Original file line numberDiff line numberDiff line change
@@ -595,3 +595,11 @@ def test_indexer_abuse():
595595

596596
with pytest.raises(AttributeError):
597597
del ob.__setitem__
598+
599+
600+
def test_indexer_accessed_through_interface():
601+
"""Test that indexers can be accessed through interfaces"""
602+
from System.Collections.Generic import Dictionary, IDictionary
603+
d = IDictionary[str, str](Dictionary[str, str]())
604+
d["one"] = "1"
605+
assert d["one"] == "1"

0 commit comments

Comments
 (0)