#nullable enable using System; using System.Runtime.InteropServices; using Python.Runtime.Native; namespace Python.Runtime { [Serializable] public class PyType : PyObject { /// Creates heap type object from the . public PyType(TypeSpec spec, PyTuple? bases = null) : base(FromSpec(spec, bases)) { } /// Wraps an existing type object. public PyType(PyObject o) : base(FromObject(o)) { } internal PyType(BorrowedReference reference) : base(reference) { if (!Runtime.PyType_Check(this.Handle)) throw new ArgumentException("object is not a type"); } internal PyType(StolenReference reference) : base(EnsureIsType(in reference)) { } internal new static PyType? FromNullableReference(BorrowedReference reference) => reference == null ? null : new PyType(new NewReference(reference).Steal()); internal static PyType FromReference(BorrowedReference reference) => FromNullableReference(reference) ?? throw new ArgumentNullException(nameof(reference)); public string Name { get { var namePtr = new StrPtr { RawPointer = Marshal.ReadIntPtr(Handle, TypeOffset.tp_name), }; return namePtr.ToString(System.Text.Encoding.UTF8)!; } } /// Returns true when type is fully initialized public bool IsReady => Flags.HasFlag(TypeFlags.Ready); internal TypeFlags Flags { get => (TypeFlags)Util.ReadCLong(Handle, TypeOffset.tp_flags); set => Util.WriteCLong(Handle, TypeOffset.tp_flags, (long)value); } /// Checks if specified object is a Python type. public static bool IsType(PyObject value) { if (value is null) throw new ArgumentNullException(nameof(value)); return Runtime.PyType_Check(value.obj); } /// /// Gets , which represents the specified CLR type. /// Must be called after the CLR type was mapped to its Python type. /// internal static PyType Get(Type clrType) { if (clrType == null) { throw new ArgumentNullException(nameof(clrType)); } ClassBase pyClass = ClassManager.GetClass(clrType); return new PyType(pyClass.ObjectReference); } internal BorrowedReference BaseReference { get { return new(Marshal.ReadIntPtr(Handle, TypeOffset.tp_base)); } set { var old = BaseReference.DangerousGetAddressOrNull(); IntPtr @new = value.DangerousGetAddress(); Runtime.XIncref(@new); Marshal.WriteIntPtr(Handle, TypeOffset.tp_base, @new); Runtime.XDecref(old); } } internal IntPtr GetSlot(TypeSlotID slot) { IntPtr result = Runtime.PyType_GetSlot(this.Reference, slot); return Exceptions.ErrorCheckIfNull(result); } private static IntPtr EnsureIsType(in StolenReference reference) { IntPtr address = reference.DangerousGetAddressOrNull(); if (address == IntPtr.Zero) throw new ArgumentNullException(nameof(reference)); return EnsureIsType(address); } private static IntPtr EnsureIsType(IntPtr ob) => Runtime.PyType_Check(ob) ? ob : throw new ArgumentException("object is not a type"); private static BorrowedReference FromObject(PyObject o) { if (o is null) throw new ArgumentNullException(nameof(o)); if (!IsType(o)) throw new ArgumentException("object is not a type"); return o.Reference; } private static IntPtr FromSpec(TypeSpec spec, PyTuple? bases = null) { if (spec is null) throw new ArgumentNullException(nameof(spec)); if ((spec.Flags & TypeFlags.HeapType) == 0) throw new NotSupportedException("Only heap types are supported"); var nativeSpec = new NativeTypeSpec(spec); var basesRef = bases is null ? default : bases.Reference; var result = Runtime.PyType_FromSpecWithBases(in nativeSpec, basesRef); PythonException.ThrowIfIsNull(result); nativeSpec.Dispose(); return result.DangerousMoveToPointer(); } } }