/// <summary> /// DelegateObject __new__ implementation. The result of this is a new /// PyObject whose type is DelegateObject and whose ob_data is a handle /// to an actual delegate instance. The method wrapped by the actual /// delegate instance belongs to an object generated to relay the call /// to the Python callable passed in. /// </summary> public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { var self = (DelegateObject)GetManagedObject(tp); if (!self.type.Valid) { return(Exceptions.RaiseTypeError(self.type.DeletedMessage)); } Type type = self.type.Value; if (Runtime.PyTuple_Size(args) != 1) { return(Exceptions.RaiseTypeError("class takes exactly one argument")); } IntPtr method = Runtime.PyTuple_GetItem(args, 0); if (Runtime.PyCallable_Check(method) != 1) { return(Exceptions.RaiseTypeError("argument must be callable")); } Delegate d = PythonEngine.DelegateManager.GetDelegate(type, method); return(CLRObject.GetInstHandle(d, self.pyHandle)); }
/// <summary> /// Set the 'args' slot on a python exception object that wraps /// a CLR exception. This is needed for pickling CLR exceptions as /// BaseException_reduce will only check the slots, bypassing the /// __getattr__ implementation, and thus dereferencing a NULL /// pointer. /// </summary> /// <param name="ob">The python object wrapping </param> internal static void SetArgsAndCause(Exception e, IntPtr ob) { IntPtr args; if (!string.IsNullOrEmpty(e.Message)) { args = Runtime.PyTuple_New(1); IntPtr msg = Runtime.PyUnicode_FromString(e.Message); Runtime.PyTuple_SetItem(args, 0, msg); } else { args = Runtime.PyTuple_New(0); } if (Runtime.PyObject_SetAttrString(ob, "args", args) != 0) { throw new PythonException(); } if (e.InnerException != null) { // Note: For an AggregateException, InnerException is only the first of the InnerExceptions. using var cause = CLRObject.GetReference(e.InnerException); Runtime.PyException_SetCause(ob, cause.DangerousMoveToPointer()); } }
internal static IntPtr GetInstHandle(object ob, Type type) { ClassBase cc = ClassManager.GetClass(type); CLRObject co = GetInstance(ob, cc.tpHandle); return(co.pyHandle); }
/// <summary> /// SetError Method /// </summary> /// /// <remarks> /// Sets the current Python exception given a CLR exception /// object. The CLR exception instance is wrapped as a Python /// object, allowing it to be handled naturally from Python. /// </remarks> public static void SetError(Exception e) { // Because delegates allow arbitrary nestings of Python calling // managed calling Python calling... etc. it is possible that we // might get a managed exception raised that is a wrapper for a // Python exception. In that case we'd rather have the real thing. PythonException pe = e as PythonException; if (pe != null) { Runtime.PyErr_SetObject(pe.PyType, pe.PyValue); return; } IntPtr op = CLRObject.GetInstHandle(e); // XXX - hack to raise a compatible old-style exception ;( if (Runtime.wrap_exceptions) { op = GetExceptionInstanceWrapper(op); } IntPtr etype = Runtime.PyObject_GetAttrString(op, "__class__"); Runtime.PyErr_SetObject(etype, op); Runtime.Decref(etype); Runtime.Decref(op); }
//==================================================================== // This is a hack. Generally, no managed class is considered callable // from Python - with the exception of System.Delegate. It is useful // to be able to call a System.Delegate instance directly, especially // when working with multicast delegates. //==================================================================== public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { //ManagedType self = GetManagedObject(ob); IntPtr tp = Runtime.PyObject_TYPE(ob); ClassBase cb = (ClassBase)GetManagedObject(tp); if (cb.type != typeof(System.Delegate)) { Exceptions.SetError(Exceptions.TypeError, "object is not callable"); return(IntPtr.Zero); } CLRObject co = (CLRObject)ManagedType.GetManagedObject(ob); Delegate d = co.inst as Delegate; BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; MethodInfo method = d.GetType().GetMethod("Invoke", flags); MethodBinder binder = new MethodBinder(method); return(binder.Invoke(ob, args, kw)); }
// Called from Converter.ToPython for types that are python subclasses of managed types. // The referenced python object is returned instead of a new wrapper. internal static IntPtr ToPython(IPythonDerivedType obj) { // derived types have a __pyobj__ field that gets set to the python // object in the overriden constructor FieldInfo fi = obj.GetType().GetField("__pyobj__"); CLRObject self = (CLRObject)fi.GetValue(obj); Runtime.Incref(self.pyHandle); // when the C# constructor creates the python object it starts as a weak // reference with a reference count of 0. Now we're passing this object // to Python the reference count needs to be incremented and the reference // needs to be replaced with a strong reference to stop the C# object being // collected while Python still has a reference to it. if (Runtime.Refcount(self.pyHandle) == 1) { GCHandle gc = GCHandle.Alloc(self, GCHandleType.Normal); Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc); self.gcHandle.Free(); self.gcHandle = gc; // now the object has a python reference it's safe for the python GC to track it Runtime.PyObject_GC_Track(self.pyHandle); } return(self.pyHandle); }
/// <summary> /// SetError Method /// </summary> /// <remarks> /// Sets the current Python exception given a CLR exception /// object. The CLR exception instance is wrapped as a Python /// object, allowing it to be handled naturally from Python. /// </remarks> public static void SetError(Exception e) { // Because delegates allow arbitrary nesting of Python calling // managed calling Python calling... etc. it is possible that we // might get a managed exception raised that is a wrapper for a // Python exception. In that case we'd rather have the real thing. var pe = e as PythonException; if (pe != null) { Runtime.XIncref(pe.PyType); Runtime.XIncref(pe.PyValue); Runtime.XIncref(pe.PyTB); Runtime.PyErr_Restore(pe.PyType, pe.PyValue, pe.PyTB); return; } IntPtr op = CLRObject.GetInstHandle(e); IntPtr etype = Runtime.PyObject_GetAttr(op, PyIdentifier.__class__); Runtime.PyErr_SetObject(new BorrowedReference(etype), new BorrowedReference(op)); Runtime.XDecref(etype); Runtime.XDecref(op); }
/// <summary> /// DelegateObject __new__ implementation. The result of this is a new /// PyObject whose type is DelegateObject and whose ob_data is a handle /// to an actual delegate instance. The method wrapped by the actual /// delegate instance belongs to an object generated to relay the call /// to the Python callable passed in. /// </summary> public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) { var self = (DelegateObject)GetManagedObject(tp) !; if (!self.type.Valid) { return(Exceptions.RaiseTypeError(self.type.DeletedMessage)); } Type type = self.type.Value; if (Runtime.PyTuple_Size(args) != 1) { return(Exceptions.RaiseTypeError("class takes exactly one argument")); } BorrowedReference method = Runtime.PyTuple_GetItem(args, 0); if (Runtime.PyCallable_Check(method) != 1) { return(Exceptions.RaiseTypeError("argument must be callable")); } Delegate d = PythonEngine.DelegateManager.GetDelegate(type, new PyObject(method)); return(CLRObject.GetReference(d, ClassManager.GetClass(type))); }
//==================================================================== // Standard iteration support for instances of reflected types. This // allows natural iteration over objects that either are IEnumerable // or themselves support IEnumerator directly. //==================================================================== public static IntPtr tp_iter(IntPtr ob) { CLRObject co = GetManagedObject(ob) as CLRObject; if (co == null) { return(Exceptions.RaiseTypeError("invalid object")); } IEnumerable e = co.inst as IEnumerable; IEnumerator o; if (e != null) { o = e.GetEnumerator(); } else { o = co.inst as IEnumerator; if (o == null) { string message = "iteration over non-sequence"; return(Exceptions.RaiseTypeError(message)); } } return(new Iterator(o).pyHandle); }
public static void Finalize(IPythonDerivedType obj) { FieldInfo fi = obj.GetType().GetField("__pyobj__"); CLRObject self = (CLRObject)fi.GetValue(obj); // delete the python object in an asnyc task as we may not be able to acquire // the GIL immediately and we don't want to block the GC thread. var t = Task.Factory.StartNew(() => { // If python's been terminated then there's nothing to do if (0 == Runtime.Py_IsInitialized()) { return; } IntPtr gs = Runtime.PyGILState_Ensure(); try { // the C# object is being destroyed which must mean there are no more // references to the Python object as well so now we can dealloc the // python object. IntPtr dict = Marshal.ReadIntPtr(self.pyHandle, ObjectOffset.DictOffset(self.pyHandle)); if (dict != IntPtr.Zero) { Runtime.Decref(dict); } Runtime.PyObject_GC_Del(self.pyHandle); self.gcHandle.Free(); } finally { Runtime.PyGILState_Release(gs); } }); }
/// <summary> /// Set the 'args' slot on a python exception object that wraps /// a CLR exception. This is needed for pickling CLR exceptions as /// BaseException_reduce will only check the slots, bypassing the /// __getattr__ implementation, and thus dereferencing a NULL /// pointer. /// </summary> /// <param name="ob">The python object wrapping </param> internal static void SetArgsAndCause(IntPtr ob) { // e: A CLR Exception Exception e = ExceptionClassObject.ToException(ob); if (e == null) { return; } IntPtr args; if (!string.IsNullOrEmpty(e.Message)) { args = Runtime.PyTuple_New(1); IntPtr msg = Runtime.PyUnicode_FromString(e.Message); Runtime.PyTuple_SetItem(args, 0, msg); } else { args = Runtime.PyTuple_New(0); } Marshal.WriteIntPtr(ob, ExceptionOffset.args, args); #if PYTHON3 if (e.InnerException != null) { IntPtr cause = CLRObject.GetInstHandle(e.InnerException); Marshal.WriteIntPtr(ob, ExceptionOffset.cause, cause); } #endif }
//==================================================================== // Implements __len__ for array types. //==================================================================== public static int mp_length(IntPtr ob) { CLRObject self = (CLRObject)ManagedType.GetManagedObject(ob); Array items = self.inst as Array; return(items.Length); }
/// <summary> /// Set the 'args' slot on a python exception object that wraps /// a CLR exception. This is needed for pickling CLR exceptions as /// BaseException_reduce will only check the slots, bypassing the /// __getattr__ implementation, and thus dereferencing a NULL /// pointer. /// </summary> internal static bool SetArgsAndCause(BorrowedReference ob, Exception e) { NewReference args; if (!string.IsNullOrEmpty(e.Message)) { args = Runtime.PyTuple_New(1); using var msg = Runtime.PyString_FromString(e.Message); Runtime.PyTuple_SetItem(args.Borrow(), 0, msg.StealOrThrow()); } else { args = Runtime.PyTuple_New(0); } using (args) { if (Runtime.PyObject_SetAttrString(ob, "args", args.Borrow()) != 0) { return(false); } } if (e.InnerException != null) { // Note: For an AggregateException, InnerException is only the first of the InnerExceptions. using var cause = CLRObject.GetReference(e.InnerException); Runtime.PyException_SetCause(ob, cause.Steal()); } return(true); }
/// <summary> /// Set the 'args' slot on a python exception object that wraps /// a CLR exception. This is needed for pickling CLR exceptions as /// BaseException_reduce will only check the slots, bypassing the /// __getattr__ implementation, and thus dereferencing a NULL /// pointer. /// </summary> internal static void SetArgsAndCause(BorrowedReference ob, Exception e) { IntPtr args; if (!string.IsNullOrEmpty(e.Message)) { args = Runtime.PyTuple_New(1); IntPtr msg = Runtime.PyString_FromString(e.Message); Runtime.PyTuple_SetItem(args, 0, msg); } else { args = Runtime.PyTuple_New(0); } using var argsTuple = NewReference.DangerousFromPointer(args); if (Runtime.PyObject_SetAttrString(ob, "args", argsTuple) != 0) { throw PythonException.ThrowLastAsClrException(); } if (e.InnerException != null) { // Note: For an AggregateException, InnerException is only the first of the InnerExceptions. using var cause = CLRObject.GetReference(e.InnerException); Runtime.PyException_SetCause(ob, cause.Steal()); } }
public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, string origMethodName, Object[] args) { FieldInfo fi = obj.GetType().GetField("__pyobj__"); CLRObject self = (CLRObject)fi.GetValue(obj); if (null != self) { List <PyObject> disposeList = new List <PyObject>(); IntPtr gs = Runtime.PyGILState_Ensure(); try { Runtime.Incref(self.pyHandle); PyObject pyself = new PyObject(self.pyHandle); disposeList.Add(pyself); Runtime.Incref(Runtime.PyNone); PyObject pynone = new PyObject(Runtime.PyNone); disposeList.Add(pynone); PyObject method = pyself.GetAttr(methodName, pynone); disposeList.Add(method); if (method.Handle != Runtime.PyNone) { // if the method hasn't been overriden then it will be a managed object ManagedType managedMethod = ManagedType.GetManagedObject(method.Handle); if (null == managedMethod) { PyObject[] pyargs = new PyObject[args.Length]; for (int i = 0; i < args.Length; ++i) { pyargs[i] = new PyObject(Converter.ToPython(args[i], args[i].GetType())); disposeList.Add(pyargs[i]); } PyObject py_result = method.Invoke(pyargs); disposeList.Add(py_result); return; } } } finally { foreach (PyObject x in disposeList) { if (x != null) { x.Dispose(); } } Runtime.PyGILState_Release(gs); } } obj.GetType().InvokeMember(origMethodName, BindingFlags.InvokeMethod, null, obj, args); }
//==================================================================== // Remove the given Python object event handler. //==================================================================== internal bool RemoveEventHandler(IntPtr target, IntPtr handler) { Object obj = null; if (target != IntPtr.Zero) { CLRObject co = (CLRObject)ManagedType.GetManagedObject(target); obj = co.inst; } IntPtr hash = Runtime.PyObject_Hash(handler); if (Exceptions.ErrorOccurred() || (reg == null)) { Exceptions.SetError(Exceptions.ValueError, "unknown event handler" ); return(false); } object key = (obj != null) ? obj : this.info.ReflectedType; ArrayList list = reg[key] as ArrayList; if (list == null) { Exceptions.SetError(Exceptions.ValueError, "unknown event handler" ); return(false); } object[] args = { null }; MethodInfo mi = this.info.GetRemoveMethod(true); for (int i = 0; i < list.Count; i++) { Handler item = (Handler)list[i]; if (item.hash != hash) { continue; } args[0] = item.del; try { mi.Invoke(obj, BindingFlags.Default, null, args, null); } catch { continue; } list.RemoveAt(i); return(true); } Exceptions.SetError(Exceptions.ValueError, "unknown event handler" ); return(false); }
/// <summary> /// Gets raw Python proxy for this object (bypasses all conversions, /// except <c>null</c> <==> <c>None</c>) /// </summary> public static PyObject GetRawPythonProxy(this object o) { if (o is null) { return(new PyObject(new BorrowedReference(Runtime.PyNone))); } return(CLRObject.MakeNewReference(o).MoveToPyObject()); }
/// <summary> /// Implements __new__ for reflected classes and value types. /// </summary> public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { var self = GetManagedObject(tp) as ClassObject; // Sanity check: this ensures a graceful error if someone does // something intentially wrong like use the managed metatype for // a class that is not really derived from a managed class. if (self == null) { return(Exceptions.RaiseTypeError("invalid object")); } Type type = self.type; // Primitive types do not have constructors, but they look like // they do from Python. If the ClassObject represents one of the // convertible primitive types, just convert the arg directly. if (type.IsPrimitive || type == typeof(string)) { if (Runtime.PyTuple_Size(args) != 1) { Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); return(IntPtr.Zero); } IntPtr op = Runtime.PyTuple_GetItem(args, 0); object result; if (!Converter.ToManaged(op, type, out result, true)) { return(IntPtr.Zero); } return(CLRObject.GetInstHandle(result, tp)); } if (type.IsAbstract) { Exceptions.SetError(Exceptions.TypeError, "cannot instantiate abstract class"); return(IntPtr.Zero); } if (type.IsEnum) { Exceptions.SetError(Exceptions.TypeError, "cannot instantiate enumeration"); return(IntPtr.Zero); } object obj = self.binder.InvokeRaw(IntPtr.Zero, args, kw); if (obj == null) { return(IntPtr.Zero); } return(CLRObject.GetInstHandle(obj, tp)); }
//==================================================================== // Descriptor __get__ implementation. This method returns the // value of the property on the given object. The returned value // is converted to an appropriately typed Python object. //==================================================================== public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { PropertyObject self = (PropertyObject)GetManagedObject(ds); MethodInfo getter = self.getter; Object result; if (getter == null) { return(Exceptions.RaiseTypeError("property cannot be read")); } if ((ob == IntPtr.Zero) || (ob == Runtime.PyNone)) { if (!(getter.IsStatic)) { Exceptions.SetError(Exceptions.TypeError, "instance property must be accessed through " + "a class instance" ); return(IntPtr.Zero); } try { result = self.info.GetValue(null, null); return(Converter.ToPython(result, self.info.PropertyType)); } catch (Exception e) { return(Exceptions.RaiseTypeError(e.Message)); } } CLRObject co = GetManagedObject(ob) as CLRObject; if (co == null) { return(Exceptions.RaiseTypeError("invalid target")); } try { result = self.info.GetValue(co.inst, null); return(Converter.ToPython(result, self.info.PropertyType)); } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } Exceptions.SetError(e); return(IntPtr.Zero); } }
public static IntPtr tp_new(IntPtr tpRaw, IntPtr args, IntPtr kw) { if (kw != IntPtr.Zero) { return(Exceptions.RaiseTypeError("array constructor takes no keyword arguments")); } var tp = new BorrowedReference(tpRaw); var self = GetManagedObject(tp) as ArrayObject; if (!self.type.Valid) { return(Exceptions.RaiseTypeError(self.type.DeletedMessage)); } Type arrType = self.type.Value; long[] dimensions = new long[Runtime.PyTuple_Size(args)]; if (dimensions.Length == 0) { return(Exceptions.RaiseTypeError("array constructor requires at least one integer argument or an object convertible to array")); } if (dimensions.Length != 1) { return(CreateMultidimensional(arrType.GetElementType(), dimensions, shapeTuple: new BorrowedReference(args), pyType: tp) .DangerousMoveToPointerOrNull()); } IntPtr op = Runtime.PyTuple_GetItem(args, 0); // create single dimensional array if (Runtime.PyInt_Check(op)) { dimensions[0] = Runtime.PyLong_AsSignedSize_t(op); if (dimensions[0] == -1 && Exceptions.ErrorOccurred()) { Exceptions.Clear(); } else { return(NewInstance(arrType.GetElementType(), tp, dimensions) .DangerousMoveToPointerOrNull()); } } object result; // this implements casting to Array[T] if (!Converter.ToManaged(op, arrType, out result, true)) { return(IntPtr.Zero); } return(CLRObject.GetInstHandle(result, tp) .DangerousGetAddress()); }
//==================================================================== // Standard __str__ implementation for instances of reflected types. //==================================================================== public static IntPtr tp_str(IntPtr ob) { CLRObject co = GetManagedObject(ob) as CLRObject; if (co == null) { return(Exceptions.RaiseTypeError("invalid object")); } return(Runtime.PyString_FromString(co.inst.ToString())); }
//==================================================================== // Standard __hash__ implementation for instances of reflected types. //==================================================================== public static IntPtr tp_hash(IntPtr ob) { CLRObject co = GetManagedObject(ob) as CLRObject; if (co == null) { return(Exceptions.RaiseTypeError("unhashable type")); } return(new IntPtr(co.inst.GetHashCode())); }
//==================================================================== // Given a PyObject pointer to an instance of a delegate type, return // the true managed delegate the Python object represents (or null). //==================================================================== private static Delegate GetTrueDelegate(IntPtr op) { CLRObject o = GetManagedObject(op) as CLRObject; if (o != null) { Delegate d = o.inst as Delegate; return(d); } return(null); }
internal static CLRObject Restore(object ob, IntPtr pyHandle, InterDomainContext context) { CLRObject co = new CLRObject() { inst = ob, pyHandle = pyHandle, tpHandle = Runtime.PyObject_TYPE(pyHandle) }; co.Load(context); return(co); }
/// <summary> /// FromManagedObject Method /// </summary> /// <remarks> /// Given an arbitrary managed object, return a Python instance that /// reflects the managed object. /// </remarks> public static PyObject FromManagedObject(object ob) { // Special case: if ob is null, we return None. if (ob == null) { Runtime.XIncref(Runtime.PyNone); return(new PyObject(Runtime.PyNone)); } IntPtr op = CLRObject.GetInstHandle(ob); return(new PyObject(op)); }
internal static Exception ToException(IntPtr ob) { CLRObject co = GetManagedObject(ob) as CLRObject; if (co == null) { return(null); } Exception e = co.inst as Exception; if (e == null) { return(null); } return(e); }
//==================================================================== // Descriptor __get__ implementation. This method returns the // value of the field on the given object. The returned value // is converted to an appropriately typed Python object. //==================================================================== public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { FieldObject self = (FieldObject)GetManagedObject(ds); Object result; if (self == null) { return(IntPtr.Zero); } FieldInfo info = self.info; if ((ob == IntPtr.Zero) || (ob == Runtime.PyNone)) { if (!info.IsStatic) { Exceptions.SetError(Exceptions.TypeError, "instance attribute must be accessed " + "through a class instance" ); return(IntPtr.Zero); } try { result = info.GetValue(null); return(Converter.ToPython(result, info.FieldType)); } catch (Exception e) { Exceptions.SetError(Exceptions.TypeError, e.Message); return(IntPtr.Zero); } } try { CLRObject co = (CLRObject)GetManagedObject(ob); result = info.GetValue(co.inst); return(Converter.ToPython(result, info.FieldType)); } catch (Exception e) { Exceptions.SetError(Exceptions.TypeError, e.Message); return(IntPtr.Zero); } }
public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { var self = GetManagedObject(tp) as ArrayObject; if (Runtime.Interop.PyTuple_Size(args) != 1) { return(Exceptions.RaiseTypeError("array expects 1 argument")); } IntPtr op = Runtime.Interop.PyTuple_GetItem(args, 0); object result; if (!Converter.ToManaged(op, self.type, out result, true)) { return(IntPtr.Zero); } return(CLRObject.GetInstHandle(result, tp)); }
//==================================================================== // Register a new Python object event handler with the event. //==================================================================== internal bool AddEventHandler(IntPtr target, IntPtr handler) { Object obj = null; if (target != IntPtr.Zero) { CLRObject co = (CLRObject)ManagedType.GetManagedObject(target); obj = co.inst; } // Create a true delegate instance of the appropriate type to // wrap the Python handler. Note that wrapper delegate creation // always succeeds, though calling the wrapper may fail. Type type = this.info.EventHandlerType; Delegate d = PythonEngine.DelegateManager.GetDelegate(type, handler); // Now register the handler in a mapping from instance to pairs // of (handler hash, delegate) so we can lookup to remove later. // All this is done lazily to avoid overhead until an event is // actually subscribed to by a Python event handler. if (reg == null) { reg = new Hashtable(); } object key = (obj != null) ? obj : this.info.ReflectedType; ArrayList list = reg[key] as ArrayList; if (list == null) { list = new ArrayList(); reg[key] = list; } list.Add(new Handler(Runtime.PyObject_Hash(handler), d)); // Note that AddEventHandler helper only works for public events, // so we have to get the underlying add method explicitly. object[] args = { d }; MethodInfo mi = this.info.GetAddMethod(true); mi.Invoke(obj, BindingFlags.Default, null, args, null); return(true); }
new public static void tp_dealloc(IntPtr ob) { CLRObject self = (CLRObject)GetManagedObject(ob); // don't let the python GC destroy this object Runtime.PyObject_GC_UnTrack(self.pyHandle); // The python should now have a ref count of 0, but we don't actually want to // deallocate the object until the C# object that references it is destroyed. // So we don't call PyObject_GC_Del here and instead we set the python // reference to a weak reference so that the C# object can be collected. GCHandle gc = GCHandle.Alloc(self, GCHandleType.Weak); Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc); self.gcHandle.Free(); self.gcHandle = gc; }