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

Skip to content

Commit 73661fd

Browse files
committed
added new class PyType to wrap Python type objects and enable new type construction from PyType_Spec (TypeSpec class)
1 parent 16b4df7 commit 73661fd

File tree

6 files changed

+266
-0
lines changed

6 files changed

+266
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1313
- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]).
1414
- Add GetPythonThreadID and Interrupt methods in PythonEngine
1515
- Ability to implement delegates with `ref` and `out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355])
16+
- `PyType` - a wrapper for Python type objects, that also permits creating new heap types from `TypeSpec`
1617

1718
### Changed
1819
- Drop support for Python 2, 3.4, and 3.5

src/embed_tests/TestPyType.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System.Text;
2+
3+
using NUnit.Framework;
4+
5+
using Python.Runtime;
6+
using Python.Runtime.Native;
7+
8+
namespace Python.EmbeddingTest
9+
{
10+
public class TestPyType
11+
{
12+
[OneTimeSetUp]
13+
public void SetUp()
14+
{
15+
PythonEngine.Initialize();
16+
}
17+
18+
[OneTimeTearDown]
19+
public void Dispose()
20+
{
21+
PythonEngine.Shutdown();
22+
}
23+
24+
[Test]
25+
public void CanCreateHeapType()
26+
{
27+
const string name = "nÁmæ";
28+
const string docStr = "dÁcæ";
29+
30+
using var doc = new StrPtr(docStr, Encoding.UTF8);
31+
var spec = new TypeSpec(
32+
name: name,
33+
basicSize: ObjectOffset.Size(Runtime.Runtime.PyTypeType),
34+
slots: new TypeSpec.Slot[] {
35+
new (TypeSlotID.tp_doc, doc.RawPointer),
36+
},
37+
TypeFlags.Default | TypeFlags.HeapType
38+
);
39+
40+
using var type = new PyType(spec);
41+
Assert.AreEqual(name, type.GetAttr("__name__").As<string>());
42+
Assert.AreEqual(docStr, type.GetAttr("__doc__").As<string>());
43+
}
44+
}
45+
}

src/runtime/TypeSpec.cs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#nullable enable
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Runtime.InteropServices;
6+
7+
namespace Python.Runtime
8+
{
9+
public class TypeSpec
10+
{
11+
public TypeSpec(string name, int basicSize, IEnumerable<Slot> slots, TypeFlags flags, int itemSize = 0)
12+
{
13+
this.Name = name ?? throw new ArgumentNullException(nameof(name));
14+
this.BasicSize = basicSize;
15+
this.Slots = slots.ToArray();
16+
this.Flags = flags;
17+
this.ItemSize = itemSize;
18+
}
19+
public string Name { get; }
20+
public int BasicSize { get; }
21+
public int ItemSize { get; }
22+
public TypeFlags Flags { get; }
23+
public IReadOnlyList<Slot> Slots { get; }
24+
25+
[StructLayout(LayoutKind.Sequential)]
26+
public struct Slot
27+
{
28+
public Slot(TypeSlotID id, IntPtr value)
29+
{
30+
ID = id;
31+
Value = value;
32+
}
33+
34+
public TypeSlotID ID { get; }
35+
public IntPtr Value { get; }
36+
}
37+
}
38+
39+
public enum TypeSlotID : int
40+
{
41+
mp_ass_subscript = 3,
42+
mp_length = 4,
43+
mp_subscript = 5,
44+
nb_absolute = 6,
45+
nb_add = 7,
46+
nb_and = 8,
47+
nb_bool = 9,
48+
nb_divmod = 10,
49+
nb_float = 11,
50+
nb_floor_divide = 12,
51+
nb_index = 13,
52+
nb_inplace_add = 14,
53+
nb_inplace_and = 15,
54+
nb_inplace_floor_divide = 16,
55+
nb_inplace_lshift = 17,
56+
nb_inplace_multiply = 18,
57+
nb_inplace_or = 19,
58+
nb_inplace_power = 20,
59+
nb_inplace_remainder = 21,
60+
nb_inplace_rshift = 22,
61+
nb_inplace_subtract = 23,
62+
nb_inplace_true_divide = 24,
63+
nb_inplace_xor = 25,
64+
nb_int = 26,
65+
nb_invert = 27,
66+
nb_lshift = 28,
67+
nb_multiply = 29,
68+
nb_negative = 30,
69+
nb_or = 31,
70+
nb_positive = 32,
71+
nb_power = 33,
72+
nb_remainder = 34,
73+
nb_rshift = 35,
74+
nb_subtract = 36,
75+
nb_true_divide = 37,
76+
nb_xor = 38,
77+
sq_ass_item = 39,
78+
sq_concat = 40,
79+
sq_contains = 41,
80+
sq_inplace_concat = 42,
81+
sq_inplace_repeat = 43,
82+
sq_item = 44,
83+
sq_length = 45,
84+
sq_repeat = 46,
85+
tp_alloc = 47,
86+
tp_base = 48,
87+
tp_bases = 49,
88+
tp_call = 50,
89+
tp_clear = 51,
90+
tp_dealloc = 52,
91+
tp_del = 53,
92+
tp_descr_get = 54,
93+
tp_descr_set = 55,
94+
tp_doc = 56,
95+
tp_getattr = 57,
96+
tp_getattro = 58,
97+
tp_hash = 59,
98+
tp_init = 60,
99+
tp_is_gc = 61,
100+
tp_iter = 62,
101+
tp_iternext = 63,
102+
tp_methods = 64,
103+
tp_new = 65,
104+
tp_repr = 66,
105+
tp_richcompare = 67,
106+
tp_setattr = 68,
107+
tp_setattro = 69,
108+
tp_str = 70,
109+
tp_traverse = 71,
110+
tp_members = 72,
111+
tp_getset = 73,
112+
tp_free = 74,
113+
nb_matrix_multiply = 75,
114+
nb_inplace_matrix_multiply = 76,
115+
am_await = 77,
116+
am_aiter = 78,
117+
am_anext = 79,
118+
/// <remarks>New in 3.5</remarks>
119+
tp_finalize = 80,
120+
}
121+
}

src/runtime/native/NativeTypeSpec.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#nullable enable
2+
using System;
3+
using System.Runtime.InteropServices;
4+
using System.Text;
5+
6+
namespace Python.Runtime.Native
7+
{
8+
[StructLayout(LayoutKind.Sequential)]
9+
struct NativeTypeSpec : IDisposable
10+
{
11+
public readonly StrPtr Name;
12+
public readonly int BasicSize;
13+
public readonly int ItemSize;
14+
public readonly TypeFlags Flags;
15+
public IntPtr Slots;
16+
17+
public NativeTypeSpec(TypeSpec spec)
18+
{
19+
if (spec is null) throw new ArgumentNullException(nameof(spec));
20+
21+
this.Name = new StrPtr(spec.Name, Encoding.UTF8);
22+
this.BasicSize = spec.BasicSize;
23+
this.ItemSize = spec.ItemSize;
24+
this.Flags = spec.Flags;
25+
26+
unsafe
27+
{
28+
int slotsBytes = checked((spec.Slots.Count + 1) * Marshal.SizeOf<TypeSpec.Slot>());
29+
var slots = (TypeSpec.Slot*)Marshal.AllocHGlobal(slotsBytes);
30+
for (int slotIndex = 0; slotIndex < spec.Slots.Count; slotIndex++)
31+
slots[slotIndex] = spec.Slots[slotIndex];
32+
slots[spec.Slots.Count] = default;
33+
this.Slots = (IntPtr)slots;
34+
}
35+
}
36+
37+
public void Dispose()
38+
{
39+
// we have to leak the name
40+
// this.Name.Dispose();
41+
Marshal.FreeHGlobal(this.Slots);
42+
this.Slots = IntPtr.Zero;
43+
}
44+
}
45+
}

src/runtime/pytype.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#nullable enable
2+
using System;
3+
using System.Runtime.InteropServices;
4+
5+
using Python.Runtime.Native;
6+
7+
namespace Python.Runtime
8+
{
9+
public class PyType : PyObject
10+
{
11+
/// <summary>Creates heap type object from the <paramref name="spec"/>.</summary>
12+
public PyType(TypeSpec spec, PyTuple? bases = null) : base(FromSpec(spec, bases)) { }
13+
/// <summary>Wraps an existing type object.</summary>
14+
public PyType(PyObject o) : base(FromObject(o)) { }
15+
16+
/// <summary>Checks if specified object is a Python type.</summary>
17+
public static bool IsType(PyObject value)
18+
{
19+
if (value is null) throw new ArgumentNullException(nameof(value));
20+
21+
return Runtime.PyType_Check(value.obj);
22+
}
23+
24+
private static BorrowedReference FromObject(PyObject o)
25+
{
26+
if (o is null) throw new ArgumentNullException(nameof(o));
27+
if (!IsType(o)) throw new ArgumentException("object is not a type");
28+
29+
return o.Reference;
30+
}
31+
32+
private static IntPtr FromSpec(TypeSpec spec, PyTuple? bases = null)
33+
{
34+
if (spec is null) throw new ArgumentNullException(nameof(spec));
35+
36+
if ((spec.Flags & TypeFlags.HeapType) == 0)
37+
throw new NotSupportedException("Only heap types are supported");
38+
39+
var nativeSpec = new NativeTypeSpec(spec);
40+
var basesRef = bases is null ? default : bases.Reference;
41+
var result = Runtime.PyType_FromSpecWithBases(in nativeSpec, basesRef);
42+
43+
PythonException.ThrowIfIsNull(result);
44+
45+
nativeSpec.Dispose();
46+
47+
return result.DangerousMoveToPointer();
48+
}
49+
}
50+
}

src/runtime/runtime.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,6 +2006,8 @@ internal static IntPtr PyType_GenericAlloc(IntPtr type, long n)
20062006

20072007

20082008
private static IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n) => Delegates.PyType_GenericAlloc(type, n);
2009+
2010+
internal static NewReference PyType_FromSpecWithBases(in NativeTypeSpec spec, BorrowedReference bases) => Delegates.PyType_FromSpecWithBases(in spec, bases);
20092011

20102012
/// <summary>
20112013
/// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a type’s base class. Return 0 on success, or return -1 and sets an exception on error.
@@ -2510,6 +2512,7 @@ static Delegates()
25102512
PyException_SetCause = (delegate* unmanaged[Cdecl]<IntPtr, IntPtr, void>)GetFunctionByName(nameof(PyException_SetCause), GetUnmanagedDll(_PythonDll));
25112513
PyThreadState_SetAsyncExcLLP64 = (delegate* unmanaged[Cdecl]<uint, IntPtr, int>)GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll));
25122514
PyThreadState_SetAsyncExcLP64 = (delegate* unmanaged[Cdecl]<ulong, IntPtr, int>)GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll));
2515+
PyType_FromSpecWithBases = (delegate* unmanaged[Cdecl]<in NativeTypeSpec, BorrowedReference, NewReference>)GetFunctionByName(nameof(PyType_FromSpecWithBases), GetUnmanagedDll(PythonDLL));
25132516
}
25142517

25152518
static global::System.IntPtr GetUnmanagedDll(string libraryName)
@@ -2775,6 +2778,7 @@ static Delegates()
27752778
internal static delegate* unmanaged[Cdecl]<IntPtr, IntPtr, void> PyException_SetCause { get; }
27762779
internal static delegate* unmanaged[Cdecl]<uint, IntPtr, int> PyThreadState_SetAsyncExcLLP64 { get; }
27772780
internal static delegate* unmanaged[Cdecl]<ulong, IntPtr, int> PyThreadState_SetAsyncExcLP64 { get; }
2781+
internal static delegate* unmanaged[Cdecl]<in NativeTypeSpec, BorrowedReference, NewReference> PyType_FromSpecWithBases { get; }
27782782
}
27792783
}
27802784

0 commit comments

Comments
 (0)