diff --git a/CHANGELOG.md b/CHANGELOG.md index b59b2f040..c3075e2e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ details about the cause of the failure - floating point values passed from Python are no longer silently truncated when .NET expects an integer [#1342][i1342] - More specific error messages for method argument mismatch +- BREAKING: most `PyScope` methods will never return `null`. Instead, `PyObject` `None` will be returned. +- BREAKING: `PyScope` was renamed to `PyModule` - BREAKING: Methods with `ref` or `out` parameters and void return type return a tuple of only the `ref` and `out` parameters. - BREAKING: to call Python from .NET `Runtime.PythonDLL` property must be set to Python DLL name or the DLL must be loaded in advance. This must be done before calling any other Python.NET functions. @@ -97,6 +99,7 @@ Instead, `PyIterable` does that. - implicit assembly loading (you have to explicitly `clr.AddReference` before doing import) - messages in `PythonException` no longer start with exception type +- `PyScopeManager`, `PyScopeException`, `PyScope` (use `PyModule` instead) - support for .NET Framework 4.0-4.6; Mono before 5.4. Python.NET now requires .NET Standard 2.0 (see [the matrix](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support)) diff --git a/src/embed_tests/TestPyScope.cs b/src/embed_tests/Modules.cs similarity index 91% rename from src/embed_tests/TestPyScope.cs rename to src/embed_tests/Modules.cs index a94b8ce28..a88ab8552 100644 --- a/src/embed_tests/TestPyScope.cs +++ b/src/embed_tests/Modules.cs @@ -5,9 +5,9 @@ namespace Python.EmbeddingTest { - public class PyScopeTest + public class Modules { - private PyScope ps; + private PyModule ps; [SetUp] public void SetUp() @@ -15,7 +15,7 @@ public void SetUp() using (Py.GIL()) { ps = Py.CreateScope("test"); - } + } } [TearDown] @@ -28,6 +28,18 @@ public void Dispose() } } + [OneTimeSetUp] + public void OneTimeSetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + PythonEngine.Shutdown(); + } + /// /// Eval a Python expression and obtain its return value. /// @@ -243,7 +255,7 @@ public void TestImportScopeFunction() "def func1():\n" + " return cc + bb\n"); - using (PyScope scope = ps.NewScope()) + using (var scope = ps.NewScope()) { //'func1' is imported from the origion scope scope.Exec( @@ -267,27 +279,6 @@ public void TestImportScopeFunction() } } - /// - /// Import a python module into the session with a new name. - /// Equivalent to the Python "import .. as .." statement. - /// - [Test] - public void TestImportScopeByName() - { - using (Py.GIL()) - { - ps.Set("bb", 100); - - using (var scope = Py.CreateScope()) - { - scope.ImportAll("test"); - //scope.ImportModule("test"); - - Assert.IsTrue(scope.Contains("bb")); - } - } - } - /// /// Use the locals() and globals() method just like in python module /// @@ -381,5 +372,34 @@ public void TestThread() PythonEngine.EndAllowThreads(ts); } } + + [Test] + public void TestCreate() + { + using var scope = Py.CreateScope(); + + Assert.IsFalse(PyModule.SysModules.HasKey("testmod")); + + PyModule testmod = new PyModule("testmod"); + + testmod.SetAttr("testattr1", "True".ToPython()); + + PyModule.SysModules.SetItem("testmod", testmod); + + using PyObject code = PythonEngine.Compile( + "import testmod\n" + + "x = testmod.testattr1" + ); + scope.Execute(code); + + Assert.IsTrue(scope.TryGet("x", out dynamic x)); + Assert.AreEqual("True", x.ToString()); + } + + [Test] + public void ImportClrNamespace() + { + Py.Import(GetType().Namespace); + } } } diff --git a/src/embed_tests/TestPyModule.cs b/src/embed_tests/TestPyModule.cs deleted file mode 100644 index 623f93d52..000000000 --- a/src/embed_tests/TestPyModule.cs +++ /dev/null @@ -1,50 +0,0 @@ -using NUnit.Framework; - -using Python.Runtime; - -namespace Python.EmbeddingTest -{ - public class TestPyModule - { - [OneTimeSetUp] - public void SetUp() - { - PythonEngine.Initialize(); - } - - [OneTimeTearDown] - public void Dispose() - { - PythonEngine.Shutdown(); - } - - [Test] - public void TestCreate() - { - using PyScope scope = Py.CreateScope(); - - Assert.IsFalse(PyModule.SysModules.HasKey("testmod")); - - PyModule testmod = new PyModule("testmod"); - - testmod.SetAttr("testattr1", "True".ToPython()); - - PyModule.SysModules.SetItem("testmod", testmod); - - using PyObject code = PythonEngine.Compile( - "import testmod\n" + - "x = testmod.testattr1" - ); - scope.Execute(code); - - Assert.IsTrue(scope.TryGet("x", out dynamic x)); - Assert.AreEqual("True", x.ToString()); - } - - [Test] - public void ImportClrNamespace() - { - Py.Import(typeof(TestPyModule).Namespace); - } - } -} diff --git a/src/embed_tests/pyinitialize.cs b/src/embed_tests/pyinitialize.cs index df791d664..a15aff585 100644 --- a/src/embed_tests/pyinitialize.cs +++ b/src/embed_tests/pyinitialize.cs @@ -95,15 +95,6 @@ public void ReInitialize() PythonEngine.Shutdown(); } - [Test] - public void TestScopeIsShutdown() - { - PythonEngine.Initialize(); - var scope = PyScopeManager.Global.Create("test"); - PythonEngine.Shutdown(); - Assert.That(PyScopeManager.Global.Contains("test"), Is.False); - } - /// /// Helper for testing the shutdown handlers. /// diff --git a/src/runtime/module.cs b/src/runtime/module.cs new file mode 100644 index 000000000..050df87eb --- /dev/null +++ b/src/runtime/module.cs @@ -0,0 +1,477 @@ +#nullable enable +using System; +using System.Linq; +using System.Collections.Generic; +using System.Dynamic; + +namespace Python.Runtime +{ + public class PyModule : PyObject + { + /// + /// the variable dict of the module. Borrowed. + /// + internal readonly IntPtr variables; + internal BorrowedReference VarsRef => new BorrowedReference(variables); + + public PyModule(string name = "") + : this(Create(name ?? throw new ArgumentNullException(nameof(name)))) + { + } + + public PyModule(string name, string? fileName = null) : this(Create(name, fileName)) { } + + static StolenReference Create(string name, string? filename = null) + { + if (name is null) + { + throw new ArgumentNullException(nameof(name)); + } + + NewReference op = Runtime.PyModule_New(name); + PythonException.ThrowIfIsNull(op); + + if (filename is not null) + { + BorrowedReference globals = Runtime.PyModule_GetDict(op); + PythonException.ThrowIfIsNull(globals); + using var pyFileName = filename.ToPython(); + int rc = Runtime.PyDict_SetItemString(globals, "__file__", pyFileName.Reference); + PythonException.ThrowIfIsNotZero(rc); + } + + return op.Steal(); + } + + internal PyModule(in StolenReference reference) : base(reference) + { + if (!IsModule(Reference)) + { + throw new ArgumentException("object is not a module"); + } + //Refcount of the variables not increase + variables = Runtime.PyModule_GetDict(Reference).DangerousGetAddress(); + PythonException.ThrowIfIsNull(variables); + + int res = Runtime.PyDict_SetItem( + VarsRef, new BorrowedReference(PyIdentifier.__builtins__), + Runtime.PyEval_GetBuiltins() + ); + PythonException.ThrowIfIsNotZero(res); + } + internal PyModule(BorrowedReference reference) : this(new NewReference(reference).Steal()) + { + } + + /// + /// Given a module or package name, import the module and return the resulting object. + /// + /// Fully-qualified module or package name + public static PyObject Import(string name) + { + if (name is null) throw new ArgumentNullException(nameof(name)); + + NewReference op = Runtime.PyImport_ImportModule(name); + PythonException.ThrowIfIsNull(op); + return IsModule(op) ? new PyModule(op.Steal()) : op.MoveToPyObject(); + } + + /// + /// Reloads the module, and returns the updated object + /// + public PyModule Reload() + { + NewReference op = Runtime.PyImport_ReloadModule(this.Reference); + PythonException.ThrowIfIsNull(op); + return new PyModule(op.Steal()); + } + + 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(m.Steal()); + } + + public void SetBuiltins(PyDict builtins) + { + if (builtins == null || builtins.IsNone()) + { + throw new ArgumentNullException(nameof(builtins)); + } + + BorrowedReference globals = Runtime.PyModule_GetDict(this.Reference); + PythonException.ThrowIfIsNull(globals); + int rc = Runtime.PyDict_SetItemString(globals, "__builtins__", builtins.Reference); + PythonException.ThrowIfIsNotZero(rc); + } + + public static PyDict SysModules + { + get + { + BorrowedReference sysModulesRef = Runtime.PyImport_GetModuleDict(); + PythonException.ThrowIfIsNull(sysModulesRef); + return new PyDict(sysModulesRef); + } + } + + internal static bool IsModule(BorrowedReference reference) + { + if (reference == null) return false; + BorrowedReference type = Runtime.PyObject_TYPE(reference); + return Runtime.PyType_IsSubtype(type, Runtime.PyModuleType); + } + + /// + /// Returns the variables dict of the module. + /// + public PyDict Variables() => new(VarsRef); + + /// + /// Create a scope, and import all from this scope + /// + public PyModule NewScope() + { + var scope = new PyModule(); + scope.ImportAll(this); + return scope; + } + /// + /// Import module by its name. + /// + public PyObject Import(string name, string? asname = null) + { + Check(); + + asname ??= name; + + var module = PyModule.Import(name); + Import(module, asname); + return module; + } + + /// + /// Import module as a variable of given name. + /// + public void Import(PyModule module, string asname) + { + this.SetPyValue(asname, module.Handle); + } + + /// + /// The 'import .. as ..' statement in Python. + /// Import a module as a variable. + /// + public void Import(PyObject module, string? asname = null) + { + asname ??= module.GetAttr("__name__").As(); + Set(asname, module); + } + + /// + /// Import all variables of the module into this module. + /// + public void ImportAll(PyModule module) + { + int result = Runtime.PyDict_Update(VarsRef, module.VarsRef); + if (result < 0) + { + throw PythonException.ThrowLastAsClrException(); + } + } + + /// + /// Import all variables of the module into this module. + /// + public void ImportAll(PyObject module) + { + if (module is null) throw new ArgumentNullException(nameof(module)); + + if (!IsModule(module.Reference)) + { + throw new ArgumentException("object is not a module", paramName: nameof(module)); + } + var module_dict = Runtime.PyModule_GetDict(module.Reference); + int result = Runtime.PyDict_Update(VarsRef, module_dict); + if (result < 0) + { + throw PythonException.ThrowLastAsClrException(); + } + } + + /// + /// Import all variables in the dictionary into this module. + /// + public void ImportAll(PyDict dict) + { + int result = Runtime.PyDict_Update(VarsRef, dict.Reference); + if (result < 0) + { + throw PythonException.ThrowLastAsClrException(); + } + } + + /// + /// Execute method + /// + /// + /// Execute a Python ast and return the result as a PyObject. + /// The ast can be either an expression or stmts. + /// + public PyObject Execute(PyObject script, PyDict? locals = null) + { + Check(); + IntPtr _locals = locals == null ? variables : locals.obj; + IntPtr ptr = Runtime.PyEval_EvalCode(script.Handle, variables, _locals); + PythonException.ThrowIfIsNull(ptr); + return new PyObject(ptr); + } + + /// + /// Execute method + /// + /// + /// Execute a Python ast and return the result as a PyObject, + /// and convert the result to a Managed Object of given type. + /// The ast can be either an expression or stmts. + /// + public T Execute(PyObject script, PyDict? locals = null) + { + Check(); + PyObject pyObj = Execute(script, locals); + var obj = pyObj.As(); + return obj; + } + + /// + /// Eval method + /// + /// + /// Evaluate a Python expression and return the result as a PyObject + /// or null if an exception is raised. + /// + public PyObject Eval(string code, PyDict? locals = null) + { + Check(); + BorrowedReference _locals = locals == null ? VarsRef : locals.Reference; + + NewReference reference = Runtime.PyRun_String( + code, RunFlagType.Eval, VarsRef, _locals + ); + PythonException.ThrowIfIsNull(reference); + return reference.MoveToPyObject(); + } + + /// + /// Evaluate a Python expression + /// + /// + /// Evaluate a Python expression + /// and convert the result to a Managed Object of given type. + /// + public T Eval(string code, PyDict? locals = null) + { + Check(); + PyObject pyObj = Eval(code, locals); + var obj = pyObj.As(); + return obj; + } + + /// + /// Exec Method + /// + /// + /// Exec a Python script and save its local variables in the current local variable dict. + /// + public void Exec(string code, PyDict? locals = null) + { + Check(); + BorrowedReference _locals = locals == null ? VarsRef : locals.Reference; + Exec(code, VarsRef, _locals); + } + + private void Exec(string code, BorrowedReference _globals, BorrowedReference _locals) + { + using NewReference reference = Runtime.PyRun_String( + code, RunFlagType.File, _globals, _locals + ); + PythonException.ThrowIfIsNull(reference); + } + + /// + /// Set Variable Method + /// + /// + /// Add a new variable to the variables dict if it not exist + /// or update its value if the variable exists. + /// + public void Set(string name, object value) + { + if (name is null) throw new ArgumentNullException(nameof(name)); + + IntPtr _value = Converter.ToPython(value, value?.GetType()); + SetPyValue(name, _value); + Runtime.XDecref(_value); + } + + private void SetPyValue(string name, IntPtr value) + { + Check(); + using (var pyKey = new PyString(name)) + { + int r = Runtime.PyObject_SetItem(variables, pyKey.obj, value); + if (r < 0) + { + throw PythonException.ThrowLastAsClrException(); + } + } + } + + /// + /// Remove Method + /// + /// + /// Remove a variable from the variables dict. + /// + public void Remove(string name) + { + if (name is null) throw new ArgumentNullException(nameof(name)); + + Check(); + using (var pyKey = new PyString(name)) + { + int r = Runtime.PyObject_DelItem(variables, pyKey.obj); + if (r < 0) + { + throw PythonException.ThrowLastAsClrException(); + } + } + } + + /// + /// Returns true if the variable exists in the module. + /// + public bool Contains(string name) + { + if (name is null) throw new ArgumentNullException(nameof(name)); + + Check(); + using (var pyKey = new PyString(name)) + { + return Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0; + } + } + + /// + /// Returns the value of the variable with the given name. + /// + /// + /// Thrown when variable with the given name does not exist. + /// + public PyObject Get(string name) + { + if (name is null) throw new ArgumentNullException(nameof(name)); + + var state = TryGet(name, out var value); + if (!state) + { + throw new KeyNotFoundException($"The module has no attribute '{name}'"); + } + return value!; + } + + /// + /// TryGet Method + /// + /// + /// Returns the value of the variable, local variable first. + /// If the variable does not exist, return null. + /// + public bool TryGet(string name, out PyObject? value) + { + if (name is null) throw new ArgumentNullException(nameof(name)); + + Check(); + using (var pyKey = new PyString(name)) + { + if (Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0) + { + IntPtr op = Runtime.PyObject_GetItem(variables, pyKey.obj); + if (op == IntPtr.Zero) + { + throw PythonException.ThrowLastAsClrException(); + } + + value = new PyObject(op); + return true; + } + else + { + value = null; + return false; + } + } + } + + /// + /// Get Method + /// + /// + /// Obtain the value of the variable of given name, + /// and convert the result to a Managed Object of given type. + /// If the variable does not exist, throw an Exception. + /// + public T Get(string name) + { + if (name is null) throw new ArgumentNullException(nameof(name)); + + Check(); + PyObject pyObj = Get(name); + return pyObj.As(); + } + + /// + /// TryGet Method + /// + /// + /// Obtain the value of the variable of given name, + /// and convert the result to a Managed Object of given type. + /// If the variable does not exist, return false. + /// + public bool TryGet(string name, out T? value) + { + Check(); + var result = TryGet(name, out var pyObj); + if (!result) + { + value = default(T); + return false; + } + value = pyObj!.As(); + return true; + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + result = CheckNone(this.Get(binder.Name)); + return true; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + this.Set(binder.Name, value); + return true; + } + + private void Check() + { + if (this.obj == IntPtr.Zero) + { + throw new ObjectDisposedException(nameof(PyModule)); + } + } + } +} diff --git a/src/runtime/pymodule.cs b/src/runtime/pymodule.cs deleted file mode 100644 index f36147ce8..000000000 --- a/src/runtime/pymodule.cs +++ /dev/null @@ -1,92 +0,0 @@ -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) { } - public PyModule(string name, string filename = null) : this(Create(name, filename)) { } - - /// - /// Given a module or package name, import the module and return the resulting object. - /// - /// Fully-qualified module or package name - public static PyObject Import(string name) - { - NewReference op = Runtime.PyImport_ImportModule(name); - PythonException.ThrowIfIsNull(op); - return IsModule(op) ? new PyModule(ref op) : op.MoveToPyObject(); - } - - /// - /// 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); - } - - private static PyModule Create(string name, string filename=null) - { - if(string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException(nameof(name)); - } - - NewReference op = Runtime.PyModule_New(name); - PythonException.ThrowIfIsNull(op); - - if (filename != null) - { - BorrowedReference globals = Runtime.PyModule_GetDict(op); - PythonException.ThrowIfIsNull(globals); - int rc = Runtime.PyDict_SetItemString(globals, "__file__", filename.ToPython().Reference); - PythonException.ThrowIfIsNotZero(rc); - } - - return new PyModule(ref op); - } - - public void SetBuiltins(PyDict builtins) - { - if(builtins == null || builtins.IsNone()) - { - throw new ArgumentNullException(nameof(builtins)); - } - - BorrowedReference globals = Runtime.PyModule_GetDict(this.Reference); - PythonException.ThrowIfIsNull(globals); - int rc = Runtime.PyDict_SetItemString(globals, "__builtins__", builtins.Reference); - PythonException.ThrowIfIsNotZero(rc); - } - - public static PyDict SysModules - { - get - { - BorrowedReference sysModulesRef = Runtime.PyImport_GetModuleDict(); - PythonException.ThrowIfIsNull(sysModulesRef); - return new PyDict(sysModulesRef); - } - } - - internal static bool IsModule(BorrowedReference reference) - { - if (reference == null) return false; - BorrowedReference type = Runtime.PyObject_TYPE(reference); - return Runtime.PyType_IsSubtype(type, Runtime.PyModuleType); - } - } -} diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs index 635adbd74..05c482454 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -1366,7 +1366,7 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg // Workaround for https://bugzilla.xamarin.com/show_bug.cgi?id=41509 // See https://github.com/pythonnet/pythonnet/pull/219 - private static object CheckNone(PyObject pyObj) + internal static object CheckNone(PyObject pyObj) { if (pyObj != null) { diff --git a/src/runtime/pyscope.cs b/src/runtime/pyscope.cs deleted file mode 100644 index 66c299811..000000000 --- a/src/runtime/pyscope.cs +++ /dev/null @@ -1,633 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Dynamic; - -namespace Python.Runtime -{ - public class PyScopeException : Exception - { - public PyScopeException(string message) - : base(message) - { - - } - } - - /// - /// Classes/methods have this attribute must be used with GIL obtained. - /// - public class PyGILAttribute : Attribute - { - } - - [PyGIL] - public class PyScope : PyObject - { - public string Name { get; } - - /// - /// the variable dict of the scope. Borrowed. - /// - internal readonly IntPtr variables; - internal BorrowedReference VarsRef => new BorrowedReference(variables); - - /// - /// The Manager this scope associated with. - /// It provides scopes this scope can import. - /// - internal readonly PyScopeManager Manager; - - /// - /// event which will be triggered after the scope disposed. - /// - public event Action OnDispose; - - /// 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) - { - Runtime.XIncref(reference.DangerousGetAddress()); - } - - /// Create a scope based on a Python Module. - private PyScope(IntPtr ptr, PyScopeManager manager) : base(ptr) - { - if (!PyModule.IsModule(Reference)) - { - throw new PyScopeException("object is not a module"); - } - Manager = manager ?? PyScopeManager.Global; - //Refcount of the variables not increase - variables = Runtime.PyModule_GetDict(Reference).DangerousGetAddress(); - PythonException.ThrowIfIsNull(variables); - - int res = Runtime.PyDict_SetItem( - VarsRef, new BorrowedReference(PyIdentifier.__builtins__), - Runtime.PyEval_GetBuiltins() - ); - PythonException.ThrowIfIsNotZero(res); - using var name = this.Get("__name__"); - this.Name = name.As(); - } - - /// - /// return the variable dict of the scope. - /// - public PyDict Variables() - { - return new PyDict(VarsRef); - } - - /// - /// Create a scope, and import all from this scope - /// - /// - public PyScope NewScope() - { - var scope = Manager.Create(); - scope.ImportAll(this); - return scope; - } - - /// - /// Import method - /// - /// - /// Import a scope or a module of given name, - /// scope will be looked up first. - /// - public PyObject Import(string name, string asname = null) - { - Check(); - if (String.IsNullOrEmpty(asname)) - { - asname = name; - } - PyScope scope; - Manager.TryGet(name, out scope); - if (scope != null) - { - Import(scope, asname); - return scope; - } - else - { - var module = PyModule.Import(name); - Import(module, asname); - return module; - } - } - - /// - /// Import a scope as a variable of given name. - /// - public void Import(PyScope scope, string asname) - { - if (scope is null) throw new ArgumentNullException(nameof(scope)); - this.SetPyValue(asname, scope.Handle); - } - - /// - /// The 'import .. as ..' statement in Python. - /// Import a module as a variable into the scope. - /// - public void Import(PyObject module, string asname = null) - { - if (module is null) throw new ArgumentNullException(nameof(module)); - - if (String.IsNullOrEmpty(asname)) - { - asname = module.GetAttr("__name__").As(); - } - Set(asname, module); - } - - /// - /// The 'import * from ..' statement in Python. - /// Import all content of a scope/module of given name into the scope, scope will be looked up first. - /// - public void ImportAll(string name) - { - PyScope scope; - Manager.TryGet(name, out scope); - if (scope != null) - { - ImportAll(scope); - return; - } - else - { - var module = PyModule.Import(name); - ImportAll(module); - } - } - - /// - /// Import all variables of the scope into this scope. - /// - public void ImportAll(PyScope scope) - { - if (scope is null) throw new ArgumentNullException(nameof(scope)); - - int result = Runtime.PyDict_Update(VarsRef, scope.VarsRef); - if (result < 0) - { - throw PythonException.ThrowLastAsClrException(); - } - } - - /// - /// Import all variables of the module into this scope. - /// - public void ImportAll(PyObject module) - { - if (module is null) throw new ArgumentNullException(nameof(module)); - - if (Runtime.PyObject_Type(module.obj) != Runtime.PyModuleType) - { - throw new PyScopeException("object is not a module"); - } - var module_dict = Runtime.PyModule_GetDict(module.Reference); - int result = Runtime.PyDict_Update(VarsRef, module_dict); - if (result < 0) - { - throw PythonException.ThrowLastAsClrException(); - } - } - - /// - /// Import all variables in the dictionary into this scope. - /// - public void ImportAll(PyDict dict) - { - if (dict is null) throw new ArgumentNullException(nameof(dict)); - - int result = Runtime.PyDict_Update(VarsRef, dict.Reference); - if (result < 0) - { - throw PythonException.ThrowLastAsClrException(); - } - } - - /// - /// Execute a Python ast and return the result as a PyObject. - /// The ast can be either an expression or stmts. - /// - public PyObject Execute(PyObject script, PyDict locals = null) - { - if (script is null) throw new ArgumentNullException(nameof(script)); - - Check(); - IntPtr _locals = locals == null ? variables : locals.obj; - IntPtr ptr = Runtime.PyEval_EvalCode(script.Handle, variables, _locals); - PythonException.ThrowIfIsNull(ptr); - if (ptr == Runtime.PyNone) - { - Runtime.XDecref(ptr); - return null; - } - return new PyObject(ptr); - } - - /// - /// Execute a Python ast and return the result as a PyObject, - /// and convert the result to a Managed Object of given type. - /// The ast can be either an expression or stmts. - /// - public T Execute(PyObject script, PyDict locals = null) - { - if (script is null) throw new ArgumentNullException(nameof(script)); - - Check(); - PyObject pyObj = Execute(script, locals); - if (pyObj == null) - { - return default(T); - } - var obj = pyObj.As(); - return obj; - } - - /// - /// Evaluate a Python expression and return the result as a PyObject - /// or null if an exception is raised. - /// - public PyObject Eval(string code, PyDict locals = null) - { - if (code is null) throw new ArgumentNullException(nameof(code)); - - Check(); - BorrowedReference _locals = locals == null ? VarsRef : locals.Reference; - - NewReference reference = Runtime.PyRun_String( - code, RunFlagType.Eval, VarsRef, _locals - ); - PythonException.ThrowIfIsNull(reference); - return reference.MoveToPyObject(); - } - - /// - /// Evaluate a Python expression - /// and convert the result to a managed object of given type. - /// - public T Eval(string code, PyDict locals = null) - { - if (code is null) throw new ArgumentNullException(nameof(code)); - - Check(); - PyObject pyObj = Eval(code, locals); - var obj = pyObj.As(); - return obj; - } - - /// - /// Exec Method - /// - /// - /// Exec a Python script and save its local variables in the current local variable dict. - /// - public void Exec(string code, PyDict locals = null) - { - if (code is null) throw new ArgumentNullException(nameof(code)); - - Check(); - BorrowedReference _locals = locals == null ? VarsRef : locals.Reference; - Exec(code, VarsRef, _locals); - } - - private void Exec(string code, BorrowedReference _globals, BorrowedReference _locals) - { - using NewReference reference = Runtime.PyRun_String( - code, RunFlagType.File, _globals, _locals - ); - PythonException.ThrowIfIsNull(reference); - } - - /// - /// Set Variable Method - /// - /// - /// Add a new variable to the variables dict if it not exist - /// or update its value if the variable exists. - /// - public void Set(string name, object value) - { - IntPtr _value = Converter.ToPython(value, value?.GetType()); - SetPyValue(name, _value); - Runtime.XDecref(_value); - } - - private void SetPyValue(string name, IntPtr value) - { - Check(); - using (var pyKey = new PyString(name)) - { - int r = Runtime.PyObject_SetItem(variables, pyKey.obj, value); - if (r < 0) - { - throw PythonException.ThrowLastAsClrException(); - } - } - } - - /// - /// Remove Method - /// - /// - /// Remove a variable from the variables dict. - /// - public void Remove(string name) - { - Check(); - using (var pyKey = new PyString(name)) - { - int r = Runtime.PyObject_DelItem(variables, pyKey.obj); - if (r < 0) - { - throw PythonException.ThrowLastAsClrException(); - } - } - } - - /// - /// Contains Method - /// - /// - /// Returns true if the variable exists in the scope. - /// - public bool Contains(string name) - { - Check(); - using (var pyKey = new PyString(name)) - { - return Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0; - } - } - - /// - /// Get Method - /// - /// - /// Returns the value of the variable of given name. - /// If the variable does not exist, throw an Exception. - /// - public PyObject Get(string name) - { - PyObject scope; - var state = TryGet(name, out scope); - if (!state) - { - throw new PyScopeException($"The scope of name '{Name}' has no attribute '{name}'"); - } - return scope; - } - - /// - /// TryGet Method - /// - /// - /// Returns the value of the variable, local variable first. - /// If the variable does not exist, return null. - /// - public bool TryGet(string name, out PyObject value) - { - Check(); - using (var pyKey = new PyString(name)) - { - if (Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0) - { - IntPtr op = Runtime.PyObject_GetItem(variables, pyKey.obj); - if (op == IntPtr.Zero) - { - throw PythonException.ThrowLastAsClrException(); - } - if (op == Runtime.PyNone) - { - Runtime.XDecref(op); - value = null; - return true; - } - value = new PyObject(op); - return true; - } - else - { - value = null; - return false; - } - } - } - - /// - /// Get Method - /// - /// - /// Obtain the value of the variable of given name, - /// and convert the result to a Managed Object of given type. - /// If the variable does not exist, throw an Exception. - /// - public T Get(string name) - { - Check(); - PyObject pyObj = Get(name); - if (pyObj == null) - { - return default(T); - } - return pyObj.As(); - } - - /// - /// TryGet Method - /// - /// - /// Obtain the value of the variable of given name, - /// and convert the result to a Managed Object of given type. - /// If the variable does not exist, return false. - /// - public bool TryGet(string name, out T value) - { - Check(); - PyObject pyObj; - var result = TryGet(name, out pyObj); - if (!result) - { - value = default(T); - return false; - } - if (pyObj == null) - { - if (typeof(T).IsValueType) - { - throw new PyScopeException($"The value of the attribute '{name}' is None which cannot be convert to '{typeof(T).ToString()}'"); - } - else - { - value = default(T); - return true; - } - } - value = pyObj.As(); - return true; - } - - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - result = this.Get(binder.Name); - return true; - } - - public override bool TrySetMember(SetMemberBinder binder, object value) - { - this.Set(binder.Name, value); - return true; - } - - private void Check() - { - if (this.obj == IntPtr.Zero) - { - throw new PyScopeException($"The scope of name '{Name}' object has been disposed"); - } - } - - protected override void Dispose(bool disposing) - { - if (this.obj == IntPtr.Zero) - { - return; - } - base.Dispose(disposing); - this.OnDispose?.Invoke(this); - } - } - - public class PyScopeManager - { - public static PyScopeManager Global; - - private Dictionary NamedScopes = new Dictionary(); - - internal static void Reset() - { - Global = new PyScopeManager(); - } - - internal PyScope NewScope(string name) - { - if (name == null) - { - name = ""; - } - var module = Runtime.PyModule_New(name); - if (module.IsNull()) - { - throw PythonException.ThrowLastAsClrException(); - } - return new PyScope(ref module, this); - } - - /// - /// Create Method - /// - /// - /// Create an anonymous scope. - /// - [PyGIL] - public PyScope Create() - { - var scope = this.NewScope(null); - return scope; - } - - /// - /// Create Method - /// - /// - /// Create an named scope of given name. - /// - [PyGIL] - public PyScope Create(string name) - { - if (String.IsNullOrEmpty(name)) - { - throw new ArgumentNullException(nameof(name)); - } - if (name != null && Contains(name)) - { - throw new PyScopeException($"A scope of name '{name}' does already exist"); - } - var scope = this.NewScope(name); - scope.OnDispose += Remove; - NamedScopes[name] = scope; - return scope; - } - - /// - /// Contains Method - /// - /// - /// return true if the scope exists in this manager. - /// - public bool Contains(string name) - { - return NamedScopes.ContainsKey(name); - } - - /// - /// Get Method - /// - /// - /// Find the scope in this manager. - /// If the scope not exist, an Exception will be thrown. - /// - public PyScope Get(string name) - { - if (String.IsNullOrEmpty(name)) - { - throw new ArgumentNullException(nameof(name)); - } - if (NamedScopes.ContainsKey(name)) - { - return NamedScopes[name]; - } - throw new PyScopeException($"There is no scope named '{name}' registered in this manager"); - } - - /// - /// Get Method - /// - /// - /// Try to find the scope in this manager. - /// - public bool TryGet(string name, out PyScope scope) - { - return NamedScopes.TryGetValue(name, out scope); - } - - /// - /// Remove Method - /// - /// - /// remove the scope from this manager. - /// - public void Remove(PyScope scope) - { - NamedScopes.Remove(scope.Name); - } - - [PyGIL] - public void Clear() - { - var scopes = NamedScopes.Values.ToList(); - foreach (var scope in scopes) - { - scope.Dispose(); - } - } - } -} diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 7156c3edd..10808a1cd 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -214,10 +214,6 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true, AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; AppDomain.CurrentDomain.ProcessExit += OnProcessExit; - // The global scope gets used implicitly quite early on, remember - // to clear it out when we shut down. - AddShutdownHandler(PyScopeManager.Global.Clear); - if (setSysArgv) { Py.SetArgv(args); @@ -381,7 +377,6 @@ public static void Shutdown(ShutdownMode mode) AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload; AppDomain.CurrentDomain.ProcessExit -= OnProcessExit; - PyScopeManager.Global.Clear(); ExecuteShutdownHandlers(); // Remember to shut down the runtime. Runtime.Shutdown(mode); @@ -694,19 +689,10 @@ public static GILState GIL() return PythonEngine.DebugGIL ? new DebugGILState() : new GILState(); } - public static PyScope CreateScope() - { - var scope = PyScopeManager.Global.Create(); - return scope; - } - - public static PyScope CreateScope(string name) - { - if (name is null) throw new ArgumentNullException(nameof(name)); + public static PyModule CreateScope() => new(); + public static PyModule CreateScope(string name) + => new(name ?? throw new ArgumentNullException(nameof(name))); - var scope = PyScopeManager.Global.Create(name); - return scope; - } public class GILState : IDisposable { diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index e3fba7e80..8cdd6eb70 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -147,7 +147,6 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd ABI.Initialize(PyVersion); GenericUtil.Reset(); - PyScopeManager.Reset(); ClassManager.Reset(); ClassDerivedObject.Reset(); TypeManager.Initialize(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy