Adds support to convert iterators to arrays (#928)

This commit is contained in:
jmlidbetter 2019-08-05 17:25:37 +01:00 коммит произвёл Benedikt Reinartz
Родитель c97a380bd3
Коммит 51a1868580
4 изменённых файлов: 53 добавлений и 17 удалений

Просмотреть файл

@ -34,6 +34,7 @@
- Jan Krivanek ([@jakrivan](https://github.com/jakrivan))
- Jeff Reback ([@jreback](https://github.com/jreback))
- Joe Frayne ([@jfrayne](https://github.com/jfrayne))
- Joe Lidbetter ([@jmlidbetter](https://github.com/jmlidbetter))
- John Burnett ([@johnburnett](https://github.com/johnburnett))
- John Wilkes ([@jbw3](https://github.com/jbw3))
- Luke Stratman ([@lstratman](https://github.com/lstratman))

Просмотреть файл

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

Просмотреть файл

@ -837,17 +837,20 @@ namespace Python.Runtime
/// <summary>
/// Convert a Python value to a correctly typed managed array instance.
/// The Python value must support the Python sequence protocol and the
/// The Python value must support the Python iterator protocol or and the
/// items in the sequence must be convertible to the target array type.
/// </summary>
private static bool ToArray(IntPtr value, Type obType, out object result, bool setError)
{
Type elementType = obType.GetElementType();
var size = Runtime.PySequence_Size(value);
result = null;
if (size < 0)
{
bool IsSeqObj = Runtime.PySequence_Check(value);
var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1;
IntPtr IterObject = Runtime.PyObject_GetIter(value);
if(IterObject==IntPtr.Zero) {
if (setError)
{
SetConversionError(value, obType);
@ -855,21 +858,17 @@ namespace Python.Runtime
return false;
}
Array items = Array.CreateInstance(elementType, size);
Array items;
// XXX - is there a better way to unwrap this if it is a real array?
for (var i = 0; i < size; i++)
var listType = typeof(List<>);
var constructedListType = listType.MakeGenericType(elementType);
IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] {(int) len}) :
(IList) Activator.CreateInstance(constructedListType);
IntPtr item;
while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero)
{
object obj = null;
IntPtr item = Runtime.PySequence_GetItem(value, i);
if (item == IntPtr.Zero)
{
if (setError)
{
SetConversionError(value, obType);
return false;
}
}
if (!Converter.ToManaged(item, elementType, out obj, true))
{
@ -877,10 +876,14 @@ namespace Python.Runtime
return false;
}
items.SetValue(obj, i);
list.Add(obj);
Runtime.XDecref(item);
}
Runtime.XDecref(IterObject);
items = Array.CreateInstance(elementType, list.Count);
list.CopyTo(items, 0);
result = items;
return true;
}

Просмотреть файл

@ -1337,3 +1337,34 @@ def test_array_abuse():
with pytest.raises(TypeError):
desc = Test.PublicArrayTest.__dict__['__setitem__']
desc(0, 0, 0)
@pytest.mark.skipif(PY2, reason="Only applies in Python 3")
def test_iterator_to_array():
from System import Array, String
d = {"a": 1, "b": 2, "c": 3}
keys_iterator = iter(d.keys())
arr = Array[String](keys_iterator)
Array.Sort(arr)
assert arr[0] == "a"
assert arr[1] == "b"
assert arr[2] == "c"
@pytest.mark.skipif(PY2, reason="Only applies in Python 3")
def test_dict_keys_to_array():
from System import Array, String
d = {"a": 1, "b": 2, "c": 3}
d_keys = d.keys()
arr = Array[String](d_keys)
Array.Sort(arr)
assert arr[0] == "a"
assert arr[1] == "b"
assert arr[2] == "c"