Skip to content

Commit 51a1868

Browse files
jmlidbetterfilmor
authored andcommitted
Adds support to convert iterators to arrays (pythonnet#928)
1 parent c97a380 commit 51a1868

File tree

4 files changed

+53
-17
lines changed

4 files changed

+53
-17
lines changed

AUTHORS.md

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
- Jan Krivanek ([@jakrivan](https://github.com/jakrivan))
3535
- Jeff Reback ([@jreback](https://github.com/jreback))
3636
- Joe Frayne ([@jfrayne](https://github.com/jfrayne))
37+
- Joe Lidbetter ([@jmlidbetter](https://github.com/jmlidbetter))
3738
- John Burnett ([@johnburnett](https://github.com/johnburnett))
3839
- John Wilkes ([@jbw3](https://github.com/jbw3))
3940
- Luke Stratman ([@lstratman](https://github.com/lstratman))

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1616
- Added argument types information to "No method matches given arguments" message
1717
- Moved wheel import in setup.py inside of a try/except to prevent pip collection failures
1818
- Removes PyLong_GetMax and PyClass_New when targetting Python3
19+
- Added support for converting python iterators to C# arrays
1920

2021
### Fixed
2122

src/runtime/converter.cs

+20-17
Original file line numberDiff line numberDiff line change
@@ -837,50 +837,53 @@ private static void SetConversionError(IntPtr value, Type target)
837837

838838
/// <summary>
839839
/// Convert a Python value to a correctly typed managed array instance.
840-
/// The Python value must support the Python sequence protocol and the
840+
/// The Python value must support the Python iterator protocol or and the
841841
/// items in the sequence must be convertible to the target array type.
842842
/// </summary>
843843
private static bool ToArray(IntPtr value, Type obType, out object result, bool setError)
844844
{
845845
Type elementType = obType.GetElementType();
846-
var size = Runtime.PySequence_Size(value);
847846
result = null;
848847

849-
if (size < 0)
850-
{
848+
bool IsSeqObj = Runtime.PySequence_Check(value);
849+
var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1;
850+
851+
IntPtr IterObject = Runtime.PyObject_GetIter(value);
852+
853+
if(IterObject==IntPtr.Zero) {
851854
if (setError)
852855
{
853856
SetConversionError(value, obType);
854857
}
855858
return false;
856859
}
857860

858-
Array items = Array.CreateInstance(elementType, size);
861+
Array items;
862+
863+
var listType = typeof(List<>);
864+
var constructedListType = listType.MakeGenericType(elementType);
865+
IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] {(int) len}) :
866+
(IList) Activator.CreateInstance(constructedListType);
867+
IntPtr item;
859868

860-
// XXX - is there a better way to unwrap this if it is a real array?
861-
for (var i = 0; i < size; i++)
869+
while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero)
862870
{
863871
object obj = null;
864-
IntPtr item = Runtime.PySequence_GetItem(value, i);
865-
if (item == IntPtr.Zero)
866-
{
867-
if (setError)
868-
{
869-
SetConversionError(value, obType);
870-
return false;
871-
}
872-
}
873872

874873
if (!Converter.ToManaged(item, elementType, out obj, true))
875874
{
876875
Runtime.XDecref(item);
877876
return false;
878877
}
879878

880-
items.SetValue(obj, i);
879+
list.Add(obj);
881880
Runtime.XDecref(item);
882881
}
882+
Runtime.XDecref(IterObject);
883883

884+
items = Array.CreateInstance(elementType, list.Count);
885+
list.CopyTo(items, 0);
886+
884887
result = items;
885888
return true;
886889
}

src/tests/test_array.py

+31
Original file line numberDiff line numberDiff line change
@@ -1337,3 +1337,34 @@ def test_array_abuse():
13371337
with pytest.raises(TypeError):
13381338
desc = Test.PublicArrayTest.__dict__['__setitem__']
13391339
desc(0, 0, 0)
1340+
1341+
1342+
@pytest.mark.skipif(PY2, reason="Only applies in Python 3")
1343+
def test_iterator_to_array():
1344+
from System import Array, String
1345+
1346+
d = {"a": 1, "b": 2, "c": 3}
1347+
keys_iterator = iter(d.keys())
1348+
arr = Array[String](keys_iterator)
1349+
1350+
Array.Sort(arr)
1351+
1352+
assert arr[0] == "a"
1353+
assert arr[1] == "b"
1354+
assert arr[2] == "c"
1355+
1356+
1357+
@pytest.mark.skipif(PY2, reason="Only applies in Python 3")
1358+
def test_dict_keys_to_array():
1359+
from System import Array, String
1360+
1361+
d = {"a": 1, "b": 2, "c": 3}
1362+
d_keys = d.keys()
1363+
arr = Array[String](d_keys)
1364+
1365+
Array.Sort(arr)
1366+
1367+
assert arr[0] == "a"
1368+
assert arr[1] == "b"
1369+
assert arr[2] == "c"
1370+

0 commit comments

Comments
 (0)