Skip to content

PyType class, wrapper for Python types #1395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]).
- Add GetPythonThreadID and Interrupt methods in PythonEngine
- Ability to implement delegates with `ref` and `out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355])
- `PyType` - a wrapper for Python type objects, that also permits creating new heap types from `TypeSpec`

### Changed
- Drop support for Python 2, 3.4, and 3.5
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<AssemblyCopyright>Copyright (c) 2006-2020 The Contributors of the Python.NET Project</AssemblyCopyright>
<AssemblyCompany>pythonnet</AssemblyCompany>
<AssemblyProduct>Python.NET</AssemblyProduct>
<LangVersion>7.3</LangVersion>
<LangVersion>9.0</LangVersion>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
Expand Down
45 changes: 45 additions & 0 deletions src/embed_tests/TestPyType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Text;

using NUnit.Framework;

using Python.Runtime;
using Python.Runtime.Native;

namespace Python.EmbeddingTest
{
public class TestPyType
{
[OneTimeSetUp]
public void SetUp()
{
PythonEngine.Initialize();
}

[OneTimeTearDown]
public void Dispose()
{
PythonEngine.Shutdown();
}

[Test]
public void CanCreateHeapType()
{
const string name = "nÁmæ";
const string docStr = "dÁcæ";

using var doc = new StrPtr(docStr, Encoding.UTF8);
var spec = new TypeSpec(
name: name,
basicSize: ObjectOffset.Size(Runtime.Runtime.PyTypeType),
slots: new TypeSpec.Slot[] {
new (TypeSlotID.tp_doc, doc.RawPointer),
},
TypeFlags.Default | TypeFlags.HeapType
);

using var type = new PyType(spec);
Assert.AreEqual(name, type.GetAttr("__name__").As<string>());
Assert.AreEqual(docStr, type.GetAttr("__doc__").As<string>());
}
}
}
121 changes: 121 additions & 0 deletions src/runtime/TypeSpec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace Python.Runtime
{
public class TypeSpec
{
public TypeSpec(string name, int basicSize, IEnumerable<Slot> slots, TypeFlags flags, int itemSize = 0)
{
this.Name = name ?? throw new ArgumentNullException(nameof(name));
this.BasicSize = basicSize;
this.Slots = slots.ToArray();
this.Flags = flags;
this.ItemSize = itemSize;
}
public string Name { get; }
public int BasicSize { get; }
public int ItemSize { get; }
public TypeFlags Flags { get; }
public IReadOnlyList<Slot> Slots { get; }

[StructLayout(LayoutKind.Sequential)]
public struct Slot
{
public Slot(TypeSlotID id, IntPtr value)
{
ID = id;
Value = value;
}

public TypeSlotID ID { get; }
public IntPtr Value { get; }
}
}

public enum TypeSlotID : int
{
mp_ass_subscript = 3,
mp_length = 4,
mp_subscript = 5,
nb_absolute = 6,
nb_add = 7,
nb_and = 8,
nb_bool = 9,
nb_divmod = 10,
nb_float = 11,
nb_floor_divide = 12,
nb_index = 13,
nb_inplace_add = 14,
nb_inplace_and = 15,
nb_inplace_floor_divide = 16,
nb_inplace_lshift = 17,
nb_inplace_multiply = 18,
nb_inplace_or = 19,
nb_inplace_power = 20,
nb_inplace_remainder = 21,
nb_inplace_rshift = 22,
nb_inplace_subtract = 23,
nb_inplace_true_divide = 24,
nb_inplace_xor = 25,
nb_int = 26,
nb_invert = 27,
nb_lshift = 28,
nb_multiply = 29,
nb_negative = 30,
nb_or = 31,
nb_positive = 32,
nb_power = 33,
nb_remainder = 34,
nb_rshift = 35,
nb_subtract = 36,
nb_true_divide = 37,
nb_xor = 38,
sq_ass_item = 39,
sq_concat = 40,
sq_contains = 41,
sq_inplace_concat = 42,
sq_inplace_repeat = 43,
sq_item = 44,
sq_length = 45,
sq_repeat = 46,
tp_alloc = 47,
tp_base = 48,
tp_bases = 49,
tp_call = 50,
tp_clear = 51,
tp_dealloc = 52,
tp_del = 53,
tp_descr_get = 54,
tp_descr_set = 55,
tp_doc = 56,
tp_getattr = 57,
tp_getattro = 58,
tp_hash = 59,
tp_init = 60,
tp_is_gc = 61,
tp_iter = 62,
tp_iternext = 63,
tp_methods = 64,
tp_new = 65,
tp_repr = 66,
tp_richcompare = 67,
tp_setattr = 68,
tp_setattro = 69,
tp_str = 70,
tp_traverse = 71,
tp_members = 72,
tp_getset = 73,
tp_free = 74,
nb_matrix_multiply = 75,
nb_inplace_matrix_multiply = 76,
am_await = 77,
am_aiter = 78,
am_anext = 79,
/// <remarks>New in 3.5</remarks>
tp_finalize = 80,
}
}
2 changes: 1 addition & 1 deletion src/runtime/clrobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal CLRObject(object ob, IntPtr tp)
System.Diagnostics.Debug.Assert(tp != IntPtr.Zero);
IntPtr py = Runtime.PyType_GenericAlloc(tp, 0);

long flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
var flags = (TypeFlags)Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.Subclass) != 0)
{
IntPtr dict = Marshal.ReadIntPtr(py, ObjectOffset.TypeDictOffset(tp));
Expand Down
56 changes: 30 additions & 26 deletions src/runtime/interop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,38 +344,42 @@ public static void FreeModuleDef(IntPtr ptr)
/// Note that the two values reserved for stackless have been put
/// to good use as PythonNet specific flags (Managed and Subclass)
/// </summary>
internal class TypeFlags
// Py_TPFLAGS_*
[Flags]
public enum TypeFlags: int
{
public const int HeapType = (1 << 9);
public const int BaseType = (1 << 10);
public const int Ready = (1 << 12);
public const int Readying = (1 << 13);
public const int HaveGC = (1 << 14);
HeapType = (1 << 9),
BaseType = (1 << 10),
Ready = (1 << 12),
Readying = (1 << 13),
HaveGC = (1 << 14),
// 15 and 16 are reserved for stackless
public const int HaveStacklessExtension = 0;
HaveStacklessExtension = 0,
/* XXX Reusing reserved constants */
public const int Managed = (1 << 15); // PythonNet specific
public const int Subclass = (1 << 16); // PythonNet specific
public const int HaveIndex = (1 << 17);
/// <remarks>PythonNet specific</remarks>
Managed = (1 << 15),
/// <remarks>PythonNet specific</remarks>
Subclass = (1 << 16),
HaveIndex = (1 << 17),
/* Objects support nb_index in PyNumberMethods */
public const int HaveVersionTag = (1 << 18);
public const int ValidVersionTag = (1 << 19);
public const int IsAbstract = (1 << 20);
public const int HaveNewBuffer = (1 << 21);
HaveVersionTag = (1 << 18),
ValidVersionTag = (1 << 19),
IsAbstract = (1 << 20),
HaveNewBuffer = (1 << 21),
// TODO: Implement FastSubclass functions
public const int IntSubclass = (1 << 23);
public const int LongSubclass = (1 << 24);
public const int ListSubclass = (1 << 25);
public const int TupleSubclass = (1 << 26);
public const int StringSubclass = (1 << 27);
public const int UnicodeSubclass = (1 << 28);
public const int DictSubclass = (1 << 29);
public const int BaseExceptionSubclass = (1 << 30);
public const int TypeSubclass = (1 << 31);

public const int Default = (
IntSubclass = (1 << 23),
LongSubclass = (1 << 24),
ListSubclass = (1 << 25),
TupleSubclass = (1 << 26),
StringSubclass = (1 << 27),
UnicodeSubclass = (1 << 28),
DictSubclass = (1 << 29),
BaseExceptionSubclass = (1 << 30),
TypeSubclass = (1 << 31),

Default = (
HaveStacklessExtension |
HaveVersionTag);
HaveVersionTag),
}


Expand Down
6 changes: 3 additions & 3 deletions src/runtime/managedtype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ internal static ManagedType GetManagedObject(IntPtr ob)
tp = ob;
}

var flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
var flags = (TypeFlags)Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.Managed) != 0)
{
IntPtr op = tp == ob
Expand All @@ -117,7 +117,7 @@ internal static ManagedType GetManagedObjectType(IntPtr ob)
if (ob != IntPtr.Zero)
{
IntPtr tp = Runtime.PyObject_TYPE(ob);
var flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
var flags = (TypeFlags)Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.Managed) != 0)
{
tp = Marshal.ReadIntPtr(tp, TypeOffset.magic());
Expand Down Expand Up @@ -152,7 +152,7 @@ internal static bool IsManagedType(IntPtr ob)
tp = ob;
}

var flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
var flags = (TypeFlags)Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.Managed) != 0)
{
return true;
Expand Down
6 changes: 3 additions & 3 deletions src/runtime/metatype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,13 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
return IntPtr.Zero;
}

int flags = TypeFlags.Default;
var flags = TypeFlags.Default;
flags |= TypeFlags.Managed;
flags |= TypeFlags.HeapType;
flags |= TypeFlags.BaseType;
flags |= TypeFlags.Subclass;
flags |= TypeFlags.HaveGC;
Util.WriteCLong(type, TypeOffset.tp_flags, flags);
Util.WriteCLong(type, TypeOffset.tp_flags, (int)flags);

TypeManager.CopySlot(base_type, type, TypeOffset.tp_dealloc);

Expand Down Expand Up @@ -285,7 +285,7 @@ public static void tp_dealloc(IntPtr tp)
{
// Fix this when we dont cheat on the handle for subclasses!

var flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
var flags = (TypeFlags)Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.Subclass) == 0)
{
IntPtr gc = Marshal.ReadIntPtr(tp, TypeOffset.magic());
Expand Down
45 changes: 45 additions & 0 deletions src/runtime/native/NativeTypeSpec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#nullable enable
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace Python.Runtime.Native
{
[StructLayout(LayoutKind.Sequential)]
struct NativeTypeSpec : IDisposable
{
public readonly StrPtr Name;
public readonly int BasicSize;
public readonly int ItemSize;
public readonly TypeFlags Flags;
public IntPtr Slots;

public NativeTypeSpec(TypeSpec spec)
{
if (spec is null) throw new ArgumentNullException(nameof(spec));

this.Name = new StrPtr(spec.Name, Encoding.UTF8);
this.BasicSize = spec.BasicSize;
this.ItemSize = spec.ItemSize;
this.Flags = spec.Flags;

unsafe
{
int slotsBytes = checked((spec.Slots.Count + 1) * Marshal.SizeOf<TypeSpec.Slot>());
var slots = (TypeSpec.Slot*)Marshal.AllocHGlobal(slotsBytes);
for (int slotIndex = 0; slotIndex < spec.Slots.Count; slotIndex++)
slots[slotIndex] = spec.Slots[slotIndex];
slots[spec.Slots.Count] = default;
this.Slots = (IntPtr)slots;
}
}

public void Dispose()
{
// we have to leak the name
// this.Name.Dispose();
Marshal.FreeHGlobal(this.Slots);
this.Slots = IntPtr.Zero;
}
}
}
Loading