diff --git a/src/embed_tests/GlobalTestsSetup.cs b/src/embed_tests/GlobalTestsSetup.cs index 458ab6a99..9a832cb0c 100644 --- a/src/embed_tests/GlobalTestsSetup.cs +++ b/src/embed_tests/GlobalTestsSetup.cs @@ -7,7 +7,7 @@ namespace Python.EmbeddingTest // As the SetUpFixture, the OneTimeTearDown of this class is executed after // all tests have run. [SetUpFixture] - public class GlobalTestsSetup + public partial class GlobalTestsSetup { [OneTimeTearDown] public void FinalCleanup() diff --git a/src/embed_tests/TestFinalizer.cs b/src/embed_tests/TestFinalizer.cs index 46e2fcdf1..c040e6930 100644 --- a/src/embed_tests/TestFinalizer.cs +++ b/src/embed_tests/TestFinalizer.cs @@ -101,7 +101,17 @@ public void CollectOnShutdown() PythonEngine.Shutdown(); garbage = Finalizer.Instance.GetCollectedObjects(); - Assert.IsEmpty(garbage); + + if (garbage.Count > 0) + { + PythonEngine.Initialize(); + string objects = string.Join("\n", garbage.Select(ob => + { + var obj = new PyObject(new BorrowedReference(ob)); + return $"{obj} [{obj.GetPythonType()}@{obj.Handle}]"; + })); + Assert.Fail("Garbage is not empty:\n" + objects); + } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to obj @@ -173,7 +183,7 @@ public void SimpleTestMemory() bool oldState = Finalizer.Instance.Enable; try { - using (PyObject gcModule = PythonEngine.ImportModule("gc")) + using (PyModule gcModule = PyModule.Import("gc")) using (PyObject pyCollect = gcModule.GetAttr("collect")) { long span1 = CompareWithFinalizerOn(pyCollect, false); diff --git a/src/embed_tests/TestNativeTypeOffset.cs b/src/embed_tests/TestNativeTypeOffset.cs index 7dd5a765e..8efd16e02 100644 --- a/src/embed_tests/TestNativeTypeOffset.cs +++ b/src/embed_tests/TestNativeTypeOffset.cs @@ -9,7 +9,7 @@ using Python.Runtime; -namespace Python.EmbeddingPythonTest +namespace Python.EmbeddingTest { public class TestNativeTypeOffset { diff --git a/src/embed_tests/TestPythonException.cs b/src/embed_tests/TestPythonException.cs index 31addfba1..a74fc3a8b 100644 --- a/src/embed_tests/TestPythonException.cs +++ b/src/embed_tests/TestPythonException.cs @@ -46,7 +46,7 @@ public void TestPythonErrorTypeName() { try { - var module = PythonEngine.ImportModule("really____unknown___module"); + var module = PyModule.Import("really____unknown___module"); Assert.Fail("Unknown module should not be loaded"); } catch (PythonException ex) @@ -95,7 +95,7 @@ public void TestPythonExceptionFormatNoTraceback() { try { - var module = PythonEngine.ImportModule("really____unknown___module"); + var module = PyModule.Import("really____unknown___module"); Assert.Fail("Unknown module should not be loaded"); } catch (PythonException ex) diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs index 59c66cc5e..32369190c 100644 --- a/src/embed_tests/TestRuntime.cs +++ b/src/embed_tests/TestRuntime.cs @@ -96,7 +96,7 @@ public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test() // TypeFlags.HaveIter set in Python 2. This tests a different code path in PyObject_IsIterable and PyIter_Check. var threading = Runtime.Runtime.PyImport_ImportModule("threading"); Exceptions.ErrorCheck(threading); - var threadingDict = Runtime.Runtime.PyModule_GetDict(new BorrowedReference(threading)); + var threadingDict = Runtime.Runtime.PyModule_GetDict(threading); Exceptions.ErrorCheck(threadingDict); var lockType = Runtime.Runtime.PyDict_GetItemString(threadingDict, "Lock"); if (lockType.IsNull) @@ -110,6 +110,8 @@ public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test() Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(lockInstance)); Assert.IsFalse(Runtime.Runtime.PyIter_Check(lockInstance)); + threading.Dispose(); + Runtime.Runtime.Py_Finalize(); } } diff --git a/src/embed_tests/pyimport.cs b/src/embed_tests/pyimport.cs index 24f31acff..b34a5d25b 100644 --- a/src/embed_tests/pyimport.cs +++ b/src/embed_tests/pyimport.cs @@ -52,7 +52,7 @@ public void Dispose() [Test] public void TestDottedName() { - PyObject module = PythonEngine.ImportModule("PyImportTest.test.one"); + var module = PyModule.Import("PyImportTest.test.one"); Assert.IsNotNull(module); } @@ -62,7 +62,7 @@ public void TestDottedName() [Test] public void TestSysArgsImportException() { - PyObject module = PythonEngine.ImportModule("PyImportTest.sysargv"); + var module = PyModule.Import("PyImportTest.sysargv"); Assert.IsNotNull(module); } diff --git a/src/embed_tests/pyinitialize.cs b/src/embed_tests/pyinitialize.cs index c774680dd..66a9a3f7c 100644 --- a/src/embed_tests/pyinitialize.cs +++ b/src/embed_tests/pyinitialize.cs @@ -175,7 +175,8 @@ public static void TestRunExitFuncs() { called = true; }; - atexit.InvokeMethod("register", callback.ToPython()); + atexit.InvokeMethod("register", callback.ToPython()).Dispose(); + atexit.Dispose(); Runtime.Runtime.Shutdown(); Assert.True(called); } diff --git a/src/runtime/BorrowedReference.cs b/src/runtime/BorrowedReference.cs index 2f5c347c7..d49f52fe9 100644 --- a/src/runtime/BorrowedReference.cs +++ b/src/runtime/BorrowedReference.cs @@ -28,6 +28,14 @@ public BorrowedReference(IntPtr pointer) => a.pointer == b.pointer; public static bool operator !=(BorrowedReference a, BorrowedReference b) => a.pointer != b.pointer; + public static bool operator ==(BorrowedReference reference, NullOnly @null) + => reference.IsNull; + public static bool operator !=(BorrowedReference reference, NullOnly @null) + => !reference.IsNull; + public static bool operator ==(NullOnly @null, BorrowedReference reference) + => reference.IsNull; + public static bool operator !=(NullOnly @null, BorrowedReference reference) + => !reference.IsNull; public override bool Equals(object obj) { if (obj is IntPtr ptr) diff --git a/src/runtime/CustomMarshaler.cs b/src/runtime/CustomMarshaler.cs index 4814e6c0b..3ef5cd662 100644 --- a/src/runtime/CustomMarshaler.cs +++ b/src/runtime/CustomMarshaler.cs @@ -189,49 +189,4 @@ public static ICustomMarshaler GetInstance(string cookie) return Instance; } } - - - /// - /// Custom Marshaler to deal with Managed String to Native - /// conversion on UTF-8. Use on functions that expect UTF-8 encoded - /// strings like `PyUnicode_FromStringAndSize` - /// - /// - /// If instead we used `MarshalAs(UnmanagedType.LPWStr)` the output to - /// `foo` would be `f\x00o\x00o\x00`. - /// - internal class Utf8Marshaler : MarshalerBase - { - private static readonly MarshalerBase Instance = new Utf8Marshaler(); - private static readonly Encoding PyEncoding = Encoding.UTF8; - - public override IntPtr MarshalManagedToNative(object managedObj) - { - var s = managedObj as string; - - if (s == null) - { - return IntPtr.Zero; - } - - byte[] bStr = PyEncoding.GetBytes(s + "\0"); - IntPtr mem = Marshal.AllocHGlobal(bStr.Length); - try - { - Marshal.Copy(bStr, 0, mem, bStr.Length); - } - catch (Exception) - { - Marshal.FreeHGlobal(mem); - throw; - } - - return mem; - } - - public static ICustomMarshaler GetInstance(string cookie) - { - return Instance; - } - } } diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index afd0bc14e..da8653853 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -95,8 +95,8 @@ internal static Exception ToException(IntPtr ob) /// public static class Exceptions { - internal static IntPtr warnings_module; - internal static IntPtr exceptions_module; + internal static PyModule warnings_module; + internal static PyModule exceptions_module; /// /// Initialization performed on startup of the Python runtime. @@ -104,15 +104,12 @@ public static class Exceptions internal static void Initialize() { string exceptionsModuleName = "builtins"; - exceptions_module = Runtime.PyImport_ImportModule(exceptionsModuleName); - - Exceptions.ErrorCheck(exceptions_module); - warnings_module = Runtime.PyImport_ImportModule("warnings"); - Exceptions.ErrorCheck(warnings_module); + exceptions_module = PyModule.Import(exceptionsModuleName); + warnings_module = PyModule.Import("warnings"); Type type = typeof(Exceptions); foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | BindingFlags.Static)) { - IntPtr op = Runtime.PyObject_GetAttrString(exceptions_module, fi.Name); + IntPtr op = Runtime.PyObject_GetAttrString(exceptions_module.obj, fi.Name); if (op != IntPtr.Zero) { fi.SetValue(type, op); @@ -147,8 +144,8 @@ internal static void Shutdown() Runtime.XDecref(op); fi.SetValue(null, IntPtr.Zero); } - Runtime.Py_CLEAR(ref exceptions_module); - Runtime.Py_CLEAR(ref warnings_module); + exceptions_module.Dispose(); + warnings_module.Dispose(); } /// @@ -348,9 +345,7 @@ public static void warn(string message, IntPtr exception, int stacklevel) Exceptions.RaiseTypeError("Invalid exception"); } - Runtime.XIncref(warnings_module); - IntPtr warn = Runtime.PyObject_GetAttrString(warnings_module, "warn"); - Runtime.XDecref(warnings_module); + IntPtr warn = Runtime.PyObject_GetAttrString(warnings_module.obj, "warn"); Exceptions.ErrorCheck(warn); IntPtr args = Runtime.PyTuple_New(3); diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs index 066c765fe..13b45df51 100644 --- a/src/runtime/importhook.cs +++ b/src/runtime/importhook.cs @@ -155,7 +155,7 @@ public static unsafe NewReference GetCLRModule(BorrowedReference fromList = defa // find any items from the from list and get them from the root if they're not // already in the module dictionary - if (fromList != null && fromList != default) + if (fromList != null) { if (Runtime.PyTuple_Check(fromList)) { @@ -224,7 +224,7 @@ public static IntPtr __import__(IntPtr self, IntPtr argsRaw, IntPtr kw) if (num_args >= 4) { fromList = Runtime.PyTuple_GetItem(args, 3); - if (fromList != default && + if (fromList != null && Runtime.PyObject_IsTrue(fromList) == 1) { fromlist = true; @@ -297,7 +297,7 @@ public static IntPtr __import__(IntPtr self, IntPtr argsRaw, IntPtr kw) BorrowedReference modules = Runtime.PyImport_GetModuleDict(); BorrowedReference module = Runtime.PyDict_GetItem(modules, py_mod_name); - if (module != default) + if (module != null) { if (fromlist) { diff --git a/src/runtime/pymodule.cs b/src/runtime/pymodule.cs new file mode 100644 index 000000000..800edb686 --- /dev/null +++ b/src/runtime/pymodule.cs @@ -0,0 +1,41 @@ +using System; + +namespace Python.Runtime +{ + public class PyModule : PyScope + { + internal PyModule(ref NewReference reference) : base(ref reference, PyScopeManager.Global) { } + public PyModule(PyObject o) : base(o.Reference, PyScopeManager.Global) { } + + /// + /// Given a module or package name, import the + /// module and return the resulting module object as a . + /// + /// Fully-qualified module or package name + public static PyModule Import(string name) + { + NewReference op = Runtime.PyImport_ImportModule(name); + PythonException.ThrowIfIsNull(op); + return new PyModule(ref op); + } + + /// + /// Reloads the module, and returns the updated object + /// + public PyModule Reload() + { + NewReference op = Runtime.PyImport_ReloadModule(this.Reference); + PythonException.ThrowIfIsNull(op); + return new PyModule(ref op); + } + + public static PyModule FromString(string name, string code) + { + using NewReference c = Runtime.Py_CompileString(code, "none", (int)RunFlagType.File); + PythonException.ThrowIfIsNull(c); + NewReference m = Runtime.PyImport_ExecCodeModule(name, c); + PythonException.ThrowIfIsNull(m); + return new PyModule(ref m); + } + } +} diff --git a/src/runtime/pyscope.cs b/src/runtime/pyscope.cs index 9d68b76fa..4d09004bd 100644 --- a/src/runtime/pyscope.cs +++ b/src/runtime/pyscope.cs @@ -22,15 +22,9 @@ public class PyGILAttribute : Attribute } [PyGIL] - public class PyScope : DynamicObject, IDisposable + public class PyScope : PyObject { - public readonly string Name; - - /// - /// the python Module object the scope associated with. - /// - readonly PyObject obj; - internal BorrowedReference Reference => obj.Reference; + public string Name { get; } /// /// the variable dict of the scope. Borrowed. @@ -49,20 +43,24 @@ public class PyScope : DynamicObject, IDisposable /// public event Action OnDispose; - /// - /// Constructor - /// - /// - /// Create a scope based on a Python Module. - /// - internal PyScope(ref NewReference ptr, PyScopeManager manager) + /// Create a scope based on a Python Module. + internal PyScope(ref NewReference reference, PyScopeManager manager) + : this(reference.DangerousMoveToPointer(), manager) { } + /// Create a scope based on a Python Module. + internal PyScope(BorrowedReference reference, PyScopeManager manager) + : this(reference.DangerousGetAddress(), manager) { - if (!Runtime.PyType_IsSubtype(Runtime.PyObject_TYPE(ptr), Runtime.PyModuleType)) + Runtime.XIncref(reference.DangerousGetAddress()); + } + + /// Create a scope based on a Python Module. + private PyScope(IntPtr ptr, PyScopeManager manager) : base(ptr) + { + if (!Runtime.PyType_IsSubtype(Runtime.PyObject_TYPE(Reference), Runtime.PyModuleType)) { throw new PyScopeException("object is not a module"); } Manager = manager ?? PyScopeManager.Global; - obj = ptr.MoveToPyObject(); //Refcount of the variables not increase variables = Runtime.PyModule_GetDict(Reference).DangerousGetAddress(); PythonException.ThrowIfIsNull(variables); @@ -72,7 +70,8 @@ internal PyScope(ref NewReference ptr, PyScopeManager manager) Runtime.PyEval_GetBuiltins() ); PythonException.ThrowIfIsNotZero(res); - this.Name = this.Get("__name__"); + using var name = this.Get("__name__"); + this.Name = name.As(); } /// @@ -118,7 +117,7 @@ public dynamic Import(string name, string asname = null) } else { - PyObject module = PythonEngine.ImportModule(name); + var module = PyModule.Import(name); Import(module, asname); return module; } @@ -132,7 +131,7 @@ public dynamic Import(string name, string asname = null) /// public void Import(PyScope scope, string asname) { - this.SetPyValue(asname, scope.obj.Handle); + this.SetPyValue(asname, scope.Handle); } /// @@ -169,7 +168,7 @@ public void ImportAll(string name) } else { - PyObject module = PythonEngine.ImportModule(name); + var module = PyModule.Import(name); ImportAll(module); } } @@ -503,16 +502,20 @@ public override bool TrySetMember(SetMemberBinder binder, object value) private void Check() { - if (this.obj.IsDisposed) + if (this.obj == IntPtr.Zero) { throw new PyScopeException($"The scope of name '{Name}' object has been disposed"); } } - public void Dispose() + protected override void Dispose(bool disposing) { + if (this.obj == IntPtr.Zero) + { + return; + } + base.Dispose(disposing); this.OnDispose?.Invoke(this); - this.obj.Dispose(); } } diff --git a/src/runtime/pystring.cs b/src/runtime/pystring.cs index b3d0dc86d..07eabba14 100644 --- a/src/runtime/pystring.cs +++ b/src/runtime/pystring.cs @@ -67,11 +67,8 @@ public PyString(string s) : base(FromString(s)) /// - /// IsStringType Method - /// - /// /// Returns true if the given object is a Python string. - /// + /// public static bool IsStringType(PyObject value) { return Runtime.PyString_Check(value.obj); diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 35ea3f6d2..b12309733 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -468,62 +468,27 @@ public static void EndAllowThreads(IntPtr ts) Runtime.PyEval_RestoreThread(ts); } + [Obsolete("Use PyModule.Import")] + public static PyObject ImportModule(string name) => PyModule.Import(name); - /// - /// ImportModule Method - /// - /// - /// Given a fully-qualified module or package name, import the - /// module and return the resulting module object as a PyObject - /// or null if an exception is raised. - /// - public static PyObject ImportModule(string name) - { - IntPtr op = Runtime.PyImport_ImportModule(name); - PythonException.ThrowIfIsNull(op); - return new PyObject(op); - } - - - /// - /// ReloadModule Method - /// - /// - /// Given a PyObject representing a previously loaded module, reload - /// the module. - /// + [Obsolete("Use PyModule.Reload")] public static PyObject ReloadModule(PyObject module) - { - IntPtr op = Runtime.PyImport_ReloadModule(module.Handle); - PythonException.ThrowIfIsNull(op); - return new PyObject(op); - } + => module is PyModule pyModule ? pyModule.Reload() : new PyModule(module).Reload(); - - /// - /// ModuleFromString Method - /// - /// - /// Given a string module name and a string containing Python code, - /// execute the code in and return a module of the given name. - /// + [Obsolete("Use PyModule.FromString")] public static PyObject ModuleFromString(string name, string code) - { - IntPtr c = Runtime.Py_CompileString(code, "none", (int)RunFlagType.File); - PythonException.ThrowIfIsNull(c); - IntPtr m = Runtime.PyImport_ExecCodeModule(name, c); - PythonException.ThrowIfIsNull(m); - return new PyObject(m); - } + => PyModule.FromString(name, code); + public static PyObject Compile(string code, string filename = "", RunFlagType mode = RunFlagType.File) { var flag = (int)mode; - IntPtr ptr = Runtime.Py_CompileString(code, filename, flag); + NewReference ptr = Runtime.Py_CompileString(code, filename, flag); PythonException.ThrowIfIsNull(ptr); - return new PyObject(ptr); + return ptr.MoveToPyObject(); } + /// /// Eval Method /// @@ -742,10 +707,12 @@ public static KeywordArguments kw(params object[] kv) return dict; } - public static PyObject Import(string name) - { - return PythonEngine.ImportModule(name); - } + /// + /// Given a module or package name, import the + /// module and return the resulting module object as a . + /// + /// Fully-qualified module or package name + public static PyModule Import(string name) => PyModule.Import(name); public static void SetArgv() { diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs index 7dd4f0811..648888293 100644 --- a/src/runtime/pythonexception.cs +++ b/src/runtime/pythonexception.cs @@ -48,7 +48,7 @@ public PythonException() if (_pyTB != IntPtr.Zero) { - using PyObject tb_module = PythonEngine.ImportModule("traceback"); + using var tb_module = PyModule.Import("traceback"); Runtime.XIncref(_pyTB); using var pyTB = new PyObject(_pyTB); @@ -198,7 +198,7 @@ public string Format() using (PyObject pyType = new PyObject(type)) using (PyObject pyValue = new PyObject(value)) using (PyObject pyTB = new PyObject(tb)) - using (PyObject tb_mod = PythonEngine.ImportModule("traceback")) + using (PyObject tb_mod = PyModule.Import("traceback")) { var buffer = new StringBuilder(); var values = tb_mod.InvokeMethod("format_exception", pyType, pyValue, pyTB); diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index ec7f5e446..173bd294d 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -278,9 +278,8 @@ private static void InitPyMembers() _PyObject_NextNotImplemented = Get_PyObject_NextNotImplemented(); { - IntPtr sys = PyImport_ImportModule("sys"); - PyModuleType = PyObject_Type(sys); - XDecref(sys); + using var sys = PyImport_ImportModule("sys"); + PyModuleType = PyObject_Type(sys.DangerousMoveToPointer()); } } @@ -937,14 +936,14 @@ internal static NewReference PyRun_String(string code, RunFlagType st, BorrowedR /// Return value: New reference. /// This is a simplified interface to Py_CompileStringFlags() below, leaving flags set to NULL. /// - internal static IntPtr Py_CompileString(string str, string file, int start) + internal static NewReference Py_CompileString(string str, string file, int start) { using var strPtr = new StrPtr(str, Encoding.UTF8); using var fileObj = new PyString(file); return Delegates.Py_CompileStringObject(strPtr, fileObj.Reference, start, Utf8String, -1); } - internal static IntPtr PyImport_ExecCodeModule(string name, IntPtr code) + internal static NewReference PyImport_ExecCodeModule(string name, BorrowedReference code) { using var namePtr = new StrPtr(name, Encoding.UTF8); return Delegates.PyImport_ExecCodeModule(namePtr, code); @@ -1027,11 +1026,18 @@ internal static int PyObject_HasAttrString(BorrowedReference pointer, string nam internal static IntPtr PyObject_GetAttrString(IntPtr pointer, string name) { using var namePtr = new StrPtr(name, Encoding.UTF8); - return Delegates.PyObject_GetAttrString(pointer, namePtr); + return Delegates.PyObject_GetAttrString(new BorrowedReference(pointer), namePtr) + .DangerousMoveToPointerOrNull(); } + internal static NewReference PyObject_GetAttrString(BorrowedReference pointer, string name) + { + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PyObject_GetAttrString(pointer, namePtr); + } - internal static IntPtr PyObject_GetAttrString(IntPtr pointer, StrPtr name) => Delegates.PyObject_GetAttrString(pointer, name); + internal static NewReference PyObject_GetAttrString(BorrowedReference pointer, StrPtr name) + => Delegates.PyObject_GetAttrString(pointer, name); internal static int PyObject_SetAttrString(IntPtr pointer, string name, IntPtr value) @@ -1917,13 +1923,13 @@ internal static string PyModule_GetFilename(IntPtr module) /// Return value: New reference. /// - internal static IntPtr PyImport_ImportModule(string name) + internal static NewReference PyImport_ImportModule(string name) { using var namePtr = new StrPtr(name, Encoding.UTF8); return Delegates.PyImport_ImportModule(namePtr); } - internal static IntPtr PyImport_ReloadModule(IntPtr module) => Delegates.PyImport_ReloadModule(module); + internal static NewReference PyImport_ReloadModule(BorrowedReference module) => Delegates.PyImport_ReloadModule(module); internal static BorrowedReference PyImport_AddModule(string name) @@ -2294,13 +2300,13 @@ static Delegates() PyRun_SimpleStringFlags = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyRun_SimpleStringFlags), GetUnmanagedDll(_PythonDll)); PyRun_StringFlags = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyRun_StringFlags), GetUnmanagedDll(_PythonDll)); PyEval_EvalCode = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_EvalCode), GetUnmanagedDll(_PythonDll)); - Py_CompileStringObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_CompileStringObject), GetUnmanagedDll(_PythonDll)); - PyImport_ExecCodeModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ExecCodeModule), GetUnmanagedDll(_PythonDll)); + Py_CompileStringObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_CompileStringObject), GetUnmanagedDll(_PythonDll)); + PyImport_ExecCodeModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ExecCodeModule), GetUnmanagedDll(_PythonDll)); PyCFunction_NewEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCFunction_NewEx), GetUnmanagedDll(_PythonDll)); PyCFunction_Call = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCFunction_Call), GetUnmanagedDll(_PythonDll)); PyMethod_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMethod_New), GetUnmanagedDll(_PythonDll)); PyObject_HasAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_HasAttrString), GetUnmanagedDll(_PythonDll)); - PyObject_GetAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetAttrString), GetUnmanagedDll(_PythonDll)); + PyObject_GetAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetAttrString), GetUnmanagedDll(_PythonDll)); PyObject_SetAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetAttrString), GetUnmanagedDll(_PythonDll)); PyObject_HasAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_HasAttr), GetUnmanagedDll(_PythonDll)); PyObject_GetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetAttr), GetUnmanagedDll(_PythonDll)); @@ -2458,8 +2464,8 @@ static Delegates() PyModule_GetFilename = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_GetFilename), GetUnmanagedDll(_PythonDll)); PyModule_Create2 = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_Create2), GetUnmanagedDll(_PythonDll)); PyImport_Import = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_Import), GetUnmanagedDll(_PythonDll)); - PyImport_ImportModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ImportModule), GetUnmanagedDll(_PythonDll)); - PyImport_ReloadModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ReloadModule), GetUnmanagedDll(_PythonDll)); + PyImport_ImportModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ImportModule), GetUnmanagedDll(_PythonDll)); + PyImport_ReloadModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ReloadModule), GetUnmanagedDll(_PythonDll)); PyImport_AddModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_AddModule), GetUnmanagedDll(_PythonDll)); PyImport_GetModuleDict = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_GetModuleDict), GetUnmanagedDll(_PythonDll)); PySys_SetArgvEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_SetArgvEx), GetUnmanagedDll(_PythonDll)); @@ -2566,13 +2572,13 @@ static Delegates() internal static delegate* unmanaged[Cdecl] PyRun_SimpleStringFlags { get; } internal static delegate* unmanaged[Cdecl] PyRun_StringFlags { get; } internal static delegate* unmanaged[Cdecl] PyEval_EvalCode { get; } - internal static delegate* unmanaged[Cdecl] Py_CompileStringObject { get; } - internal static delegate* unmanaged[Cdecl] PyImport_ExecCodeModule { get; } + internal static delegate* unmanaged[Cdecl] Py_CompileStringObject { get; } + internal static delegate* unmanaged[Cdecl] PyImport_ExecCodeModule { get; } internal static delegate* unmanaged[Cdecl] PyCFunction_NewEx { get; } internal static delegate* unmanaged[Cdecl] PyCFunction_Call { get; } internal static delegate* unmanaged[Cdecl] PyMethod_New { get; } internal static delegate* unmanaged[Cdecl] PyObject_HasAttrString { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GetAttrString { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetAttrString { get; } internal static delegate* unmanaged[Cdecl] PyObject_SetAttrString { get; } internal static delegate* unmanaged[Cdecl] PyObject_HasAttr { get; } internal static delegate* unmanaged[Cdecl] PyObject_GetAttr { get; } @@ -2723,8 +2729,8 @@ static Delegates() internal static delegate* unmanaged[Cdecl] PyModule_GetFilename { get; } internal static delegate* unmanaged[Cdecl] PyModule_Create2 { get; } internal static delegate* unmanaged[Cdecl] PyImport_Import { get; } - internal static delegate* unmanaged[Cdecl] PyImport_ImportModule { get; } - internal static delegate* unmanaged[Cdecl] PyImport_ReloadModule { get; } + internal static delegate* unmanaged[Cdecl] PyImport_ImportModule { get; } + internal static delegate* unmanaged[Cdecl] PyImport_ReloadModule { get; } internal static delegate* unmanaged[Cdecl] PyImport_AddModule { get; } internal static delegate* unmanaged[Cdecl] PyImport_GetModuleDict { get; } internal static delegate* unmanaged[Cdecl] PySys_SetArgvEx { get; } diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs index 295219675..b541a7c44 100644 --- a/src/runtime/runtime_state.cs +++ b/src/runtime/runtime_state.cs @@ -139,18 +139,15 @@ private static void RestoreObjects(IntPtr dummyGC) public static IEnumerable PyGCGetObjects() { - var gc = PyImport_ImportModule("gc"); - PythonException.ThrowIfIsNull(gc); - var get_objects = PyObject_GetAttrString(gc, "get_objects"); - var objs = PyObject_CallObject(get_objects, IntPtr.Zero); + using var gc = PyModule.Import("gc"); + using var get_objects = gc.GetAttr("get_objects"); + var objs = PyObject_CallObject(get_objects.Handle, IntPtr.Zero); var length = PyList_Size(new BorrowedReference(objs)); for (long i = 0; i < length; i++) { var obj = PyList_GetItem(new BorrowedReference(objs), i); yield return obj.DangerousGetAddress(); } - XDecref(objs); - XDecref(gc); } public static IEnumerable GetModuleNames() diff --git a/src/runtime/tricks/NullOnly.cs b/src/runtime/tricks/NullOnly.cs new file mode 100644 index 000000000..cc2679a61 --- /dev/null +++ b/src/runtime/tricks/NullOnly.cs @@ -0,0 +1,12 @@ +namespace Python.Runtime +{ + /// + /// An utility class, that can only have one value: null. + /// Useful for overloading operators on structs, + /// that have meaningful concept of null value (e.g. pointers and references). + /// + class NullOnly + { + private NullOnly() { } + } +} diff --git a/src/testing/threadtest.cs b/src/testing/threadtest.cs index 9c76929b2..6664c3643 100644 --- a/src/testing/threadtest.cs +++ b/src/testing/threadtest.cs @@ -34,7 +34,7 @@ public static string CallEchoString(string arg) { if (module == null) { - module = PythonEngine.ModuleFromString("tt", testmod); + module = PyModule.FromString("tt", testmod); } PyObject func = module.GetAttr("echostring"); var parg = new PyString(arg); @@ -58,7 +58,7 @@ public static string CallEchoString2(string arg) { if (module == null) { - module = PythonEngine.ModuleFromString("tt", testmod); + module = PyModule.FromString("tt", testmod); } PyObject func = module.GetAttr("echostring2");