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();