Skip to content

Commit 46e7c65

Browse files
committed
allow creating new .NET arrays from Python using Array[T](dim1, dim2, ...) syntax
fixes #251
1 parent c81c3c3 commit 46e7c65

File tree

3 files changed

+95
-2
lines changed

3 files changed

+95
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
99

1010
### Added
1111

12+
- Ability to instantiate new .NET arrays using `Array[T](dim1, dim2, ...)` syntax
13+
1214
### Changed
1315
- Drop support for Python 2, 3.4, and 3.5
1416
- `clr.AddReference` may now throw errors besides `FileNotFoundException`, that provide more

src/runtime/arrayobject.cs

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,98 @@ internal override bool CanSubclass()
2222

2323
public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
2424
{
25+
if (kw != IntPtr.Zero)
26+
{
27+
return Exceptions.RaiseTypeError("array constructor takes no keyword arguments");
28+
}
29+
2530
var self = GetManagedObject(tp) as ArrayObject;
26-
if (Runtime.PyTuple_Size(args) != 1)
31+
32+
long[] dimensions = new long[Runtime.PyTuple_Size(args)];
33+
if (dimensions.Length == 0)
34+
{
35+
return Exceptions.RaiseTypeError("array constructor requires at least one integer argument or an object convertible to array");
36+
}
37+
if (dimensions.Length != 1)
2738
{
28-
return Exceptions.RaiseTypeError("array expects 1 argument");
39+
return CreateMultidimensional(self.type.GetElementType(), dimensions, args, tp);
2940
}
41+
3042
IntPtr op = Runtime.PyTuple_GetItem(args, 0);
43+
44+
// create single dimensional array
45+
if (Runtime.PyInt_Check(op))
46+
{
47+
dimensions[0] = Runtime.PyLong_AsLongLong(op);
48+
if (dimensions[0] == -1 && Exceptions.ErrorOccurred())
49+
{
50+
Exceptions.Clear();
51+
}
52+
else
53+
{
54+
return NewInstance(self.type.GetElementType(), tp, dimensions);
55+
}
56+
}
3157
object result;
3258

59+
// this implements casting to Array[T]
3360
if (!Converter.ToManaged(op, self.type, out result, true))
3461
{
3562
return IntPtr.Zero;
3663
}
3764
return CLRObject.GetInstHandle(result, tp);
3865
}
3966

67+
static IntPtr CreateMultidimensional(Type elementType, long[] dimensions, IntPtr shapeTuple, IntPtr pyType)
68+
{
69+
for (int dimIndex = 0; dimIndex < dimensions.Length; dimIndex++)
70+
{
71+
IntPtr dimObj = Runtime.PyTuple_GetItem(shapeTuple, dimIndex);
72+
PythonException.ThrowIfIsNull(dimObj);
73+
74+
if (!Runtime.PyInt_Check(dimObj))
75+
return Exceptions.RaiseTypeError("array constructor expects integer dimensions");
76+
77+
dimensions[dimIndex] = Runtime.PyLong_AsLongLong(dimObj);
78+
if (dimensions[dimIndex] == -1 && Exceptions.ErrorOccurred())
79+
{
80+
return Exceptions.RaiseTypeError("array constructor expects integer dimensions");
81+
}
82+
}
83+
84+
return NewInstance(elementType, pyType, dimensions);
85+
}
86+
87+
static IntPtr NewInstance(Type elementType, IntPtr tp, long[] dimensions)
88+
{
89+
object result;
90+
try
91+
{
92+
result = Array.CreateInstance(elementType, dimensions);
93+
}
94+
catch (ArgumentException badArgument)
95+
{
96+
Exceptions.SetError(Exceptions.ValueError, badArgument.Message);
97+
return IntPtr.Zero;
98+
}
99+
catch (OverflowException overflow)
100+
{
101+
Exceptions.SetError(overflow);
102+
return IntPtr.Zero;
103+
}
104+
catch (NotSupportedException notSupported)
105+
{
106+
Exceptions.SetError(notSupported);
107+
return IntPtr.Zero;
108+
}
109+
catch (OutOfMemoryException oom)
110+
{
111+
Exceptions.SetError(Exceptions.MemoryError, oom.Message);
112+
return IntPtr.Zero;
113+
}
114+
return CLRObject.GetInstHandle(result, tp);
115+
}
116+
40117

41118
/// <summary>
42119
/// Implements __getitem__ for array types.

src/tests/test_array.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,20 @@ def test_boxed_value_type_mutation_result():
11741174
assert items[i].X == i + 1
11751175
assert items[i].Y == i + 1
11761176

1177+
def test_create_array_from_shape():
1178+
from System import Array
1179+
1180+
value = Array[int](3)
1181+
assert value[1] == 0
1182+
assert value.Length == 3
1183+
1184+
value = Array[int](3, 4)
1185+
assert value[1, 1] == 0
1186+
assert value.GetLength(0) == 3
1187+
assert value.GetLength(1) == 4
1188+
1189+
with pytest.raises(ValueError):
1190+
Array[int](-1)
11771191

11781192
def test_special_array_creation():
11791193
"""Test using the Array[<type>] syntax for creating arrays."""

0 commit comments

Comments
 (0)