X Tutup
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 : DynamicObject, IPyDisposable { public readonly string Name; /// /// the python Module object the scope associated with. /// internal readonly IntPtr obj; /// /// the variable dict of the scope. /// internal readonly IntPtr variables; private bool _isDisposed; private bool _finalized = false; /// /// 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; /// /// Constructor /// /// /// Create a scope based on a Python Module. /// internal PyScope(IntPtr ptr, PyScopeManager manager) { if (!Runtime.PyType_IsSubtype(Runtime.PyObject_TYPE(ptr), Runtime.PyModuleType)) { throw new PyScopeException("object is not a module"); } Manager = manager ?? PyScopeManager.Global; obj = ptr; //Refcount of the variables not increase variables = Runtime.PyModule_GetDict(obj); Runtime.CheckExceptionOccurred(); Runtime.PyDict_SetItemString( variables, "__builtins__", Runtime.PyEval_GetBuiltins() ); this.Name = this.Get("__name__"); } /// /// return the variable dict of the scope. /// /// public PyDict Variables() { Runtime.XIncref(variables); return new PyDict(variables); } /// /// 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 dynamic 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 { PyObject module = PythonEngine.ImportModule(name); Import(module, asname); return module; } } /// /// Import method /// /// /// Import a scope as a variable of given name. /// public void Import(PyScope scope, string asname) { this.Set(asname, scope.obj); } /// /// Import Method /// /// /// The 'import .. as ..' statement in Python. /// Import a module as a variable into the scope. /// public void Import(PyObject module, string asname = null) { if (String.IsNullOrEmpty(asname)) { asname = module.GetAttr("__name__").As(); } Set(asname, module); } /// /// ImportAll Method /// /// /// 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 { PyObject module = PythonEngine.ImportModule(name); ImportAll(module); } } /// /// ImportAll Method /// /// /// Import all variables of the scope into this scope. /// public void ImportAll(PyScope scope) { int result = Runtime.PyDict_Update(variables, scope.variables); if (result < 0) { throw new PythonException(); } } /// /// ImportAll Method /// /// /// Import all variables of the module into this scope. /// public void ImportAll(PyObject module) { if (Runtime.PyObject_Type(module.obj) != Runtime.PyModuleType) { throw new PyScopeException("object is not a module"); } var module_dict = Runtime.PyModule_GetDict(module.obj); int result = Runtime.PyDict_Update(variables, module_dict); if (result < 0) { throw new PythonException(); } } /// /// ImportAll Method /// /// /// Import all variables in the dictionary into this scope. /// public void ImportAll(PyDict dict) { int result = Runtime.PyDict_Update(variables, dict.obj); if (result < 0) { throw new PythonException(); } } /// /// 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); Runtime.CheckExceptionOccurred(); if (ptr == Runtime.PyNone) { Runtime.XDecref(ptr); return null; } 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); if (pyObj == null) { return default(T); } 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(); IntPtr _locals = locals == null ? variables : locals.obj; var flag = (IntPtr)Runtime.Py_eval_input; IntPtr ptr = Runtime.PyRun_String( code, flag, variables, _locals ); Runtime.CheckExceptionOccurred(); return new PyObject(ptr); } /// /// 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(); IntPtr _locals = locals == null ? variables : locals.obj; Exec(code, variables, _locals); } private void Exec(string code, IntPtr _globals, IntPtr _locals) { var flag = (IntPtr)Runtime.Py_file_input; IntPtr ptr = Runtime.PyRun_String( code, flag, _globals, _locals ); Runtime.CheckExceptionOccurred(); if (ptr != Runtime.PyNone) { throw new PythonException(); } Runtime.XDecref(ptr); } /// /// 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()); Set(name, _value); Runtime.XDecref(_value); } private void Set(string name, IntPtr value) { Check(); using (var pyKey = new PyString(name)) { int r = Runtime.PyObject_SetItem(variables, pyKey.obj, value); if (r < 0) { throw new PythonException(); } } } /// /// 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 new PythonException(); } } } /// /// 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 new PythonException(); } 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 (_isDisposed) { throw new PyScopeException($"The scope of name '{Name}' object has been disposed"); } } public void Dispose() { if (_isDisposed) { return; } _isDisposed = true; Runtime.XDecref(obj); this.OnDispose?.Invoke(this); } public IntPtr[] GetTrackedHandles() { return new IntPtr[] { obj }; } ~PyScope() { if (_finalized || _isDisposed) { return; } _finalized = true; Finalizer.Instance.AddFinalizedObject(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 == IntPtr.Zero) { throw new PythonException(); } return new PyScope(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(); } } } }
X Tutup