Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0bd4a10

Browse files
Joe LidbetterJoe Lidbetter
Joe Lidbetter
authored and
Joe Lidbetter
committed
Adds support to convert iterators to arrays
1 parent 6f635a4 commit 0bd4a10

File tree

4 files changed

+92
-13
lines changed

4 files changed

+92
-13
lines changed

AUTHORS.md

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

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1515

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
18+
- Added support for converting python iterators to C# arrays
1819

1920
### Fixed
2021

src/runtime/converter.cs

+63-13
Original file line numberDiff line numberDiff line change
@@ -837,48 +837,98 @@ 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
841-
/// items in the sequence must be convertible to the target array type.
840+
/// The Python value must support either the Python sequence protocol or
841+
/// the Python iterator protocol and the items in the sequence must be
842+
/// convertible to the target array type.
842843
/// </summary>
843844
private static bool ToArray(IntPtr value, Type obType, out object result, bool setError)
844845
{
845846
Type elementType = obType.GetElementType();
846-
var size = Runtime.PySequence_Size(value);
847847
result = null;
848848

849-
if (size < 0)
849+
bool IsSeqObj = Runtime.PySequence_Check(value);
850+
851+
// If object does not support Sequence Protocol check if it supports iterator protocol
852+
IntPtr IterObject = IntPtr.Zero;
853+
if(!IsSeqObj)
850854
{
855+
IterObject = Runtime.PyObject_GetIter(value);
856+
}
857+
858+
if(!IsSeqObj && IterObject==IntPtr.Zero) {
859+
// Bail if both sequence check and iterator conversion attempt fail
851860
if (setError)
852861
{
853862
SetConversionError(value, obType);
854863
}
855864
return false;
856865
}
857866

858-
Array items = Array.CreateInstance(elementType, size);
867+
Array items;
859868

860869
// XXX - is there a better way to unwrap this if it is a real array?
861-
for (var i = 0; i < size; i++)
870+
if (IsSeqObj)
862871
{
863-
object obj = null;
864-
IntPtr item = Runtime.PySequence_GetItem(value, i);
865-
if (item == IntPtr.Zero)
872+
// If we have a sequence object use the sequence protocol
873+
var size = Runtime.PySequence_Size(value);
874+
if (size < 0)
866875
{
867876
if (setError)
868877
{
869878
SetConversionError(value, obType);
879+
}
880+
return false;
881+
}
882+
883+
items = Array.CreateInstance(elementType, size);
884+
for (var i = 0; i < size; i++)
885+
{
886+
object obj = null;
887+
IntPtr item = Runtime.PySequence_GetItem(value, i);
888+
if (item == IntPtr.Zero)
889+
{
890+
if (setError)
891+
{
892+
SetConversionError(value, obType);
893+
}
870894
return false;
871895
}
896+
897+
if (!Converter.ToManaged(item, elementType, out obj, true))
898+
{
899+
Runtime.XDecref(item);
900+
return false;
901+
}
902+
903+
items.SetValue(obj, i);
904+
Runtime.XDecref(item);
872905
}
906+
}
907+
else
908+
{
909+
// Otherwise use iterator protocol
910+
var listType = typeof(List<>);
911+
var constructedListType = listType.MakeGenericType(elementType);
912+
IList list = (IList) Activator.CreateInstance(constructedListType);
913+
IntPtr item;
873914

874-
if (!Converter.ToManaged(item, elementType, out obj, true))
915+
while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero)
875916
{
917+
object obj = null;
918+
919+
if (!Converter.ToManaged(item, elementType, out obj, true))
920+
{
921+
Runtime.XDecref(item);
922+
return false;
923+
}
924+
925+
list.Add(obj);
876926
Runtime.XDecref(item);
877-
return false;
878927
}
928+
Runtime.XDecref(IterObject);
879929

880-
items.SetValue(obj, i);
881-
Runtime.XDecref(item);
930+
items = Array.CreateInstance(elementType, list.Count);
931+
list.CopyTo(items, 0);
882932
}
883933

884934
result = items;

src/tests/test_array.py

+27
Original file line numberDiff line numberDiff line change
@@ -1337,3 +1337,30 @@ 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+
assert arr[0] == "a"
1351+
assert arr[1] == "b"
1352+
assert arr[2] == "c"
1353+
1354+
1355+
@pytest.mark.skipif(PY2, reason="Only applies in Python 3")
1356+
def test_dict_keys_to_array():
1357+
from System import Array, String
1358+
1359+
d = {"a": 1, "b": 2, "c": 3}
1360+
d_keys = d.keys()
1361+
arr = Array[String](d_keys)
1362+
1363+
assert arr[0] == "a"
1364+
assert arr[1] == "b"
1365+
assert arr[2] == "c"
1366+

0 commit comments

Comments
 (0)