X Tutup
using System; using System.IO; using System.Threading; using System.Reflection; using System.Collections.Generic; using System.Linq; namespace Python.Runtime { /// /// This class provides the public interface of the Python runtime. /// public class PythonEngine : IDisposable { private static DelegateManager delegateManager; private static bool initialized; public PythonEngine() { Initialize(); } public PythonEngine(params string[] args) { Initialize(args); } public PythonEngine(IEnumerable args) { Initialize(args); } public void Dispose() { Shutdown(); } public static bool IsInitialized { get { return initialized; } } internal static DelegateManager DelegateManager { get { if (delegateManager == null) { throw new InvalidOperationException( "DelegateManager has not yet been initialized using Python.Runtime.PythonEngine.Initialize()."); } return delegateManager; } } public static string ProgramName { get { string result = Runtime.PyPy_GetProgramName(); if (result == null) { return ""; } return result; } set { Runtime.PyPy_SetProgramName(value); } } public static string PythonHome { get { string result = Runtime.PyPy_GetPythonHome(); if (result == null) { return ""; } return result; } set { Runtime.PyPy_SetPythonHome(value); } } public static string PythonPath { get { string result = Runtime.PyPy_GetPath(); if (result == null) { return ""; } return result; } set { Runtime.PyPy_SetPath(value); } } public static string Version { get { return Runtime.PyPy_GetVersion(); } } public static string BuildInfo { get { return Runtime.PyPy_GetBuildInfo(); } } public static string Platform { get { return Runtime.PyPy_GetPlatform(); } } public static string Copyright { get { return Runtime.PyPy_GetCopyright(); } } public static int RunSimpleString(string code) { return Runtime.PyPyRun_SimpleString(code); } public static void Initialize() { Initialize(Enumerable.Empty()); } /// /// Initialize Method /// /// /// Initialize the Python runtime. It is safe to call this method /// more than once, though initialization will only happen on the /// first call. It is *not* necessary to hold the Python global /// interpreter lock (GIL) to call this method. /// public static void Initialize(IEnumerable args) { if (!initialized) { // Creating the delegateManager MUST happen before Runtime.Initialize // is called. If it happens afterwards, DelegateManager's CodeGenerator // throws an exception in its ctor. This exception is eaten somehow // during an initial "import clr", and the world ends shortly thereafter. // This is probably masking some bad mojo happening somewhere in Runtime.Initialize(). delegateManager = new DelegateManager(); Runtime.Initialize(); initialized = true; Exceptions.Clear(); Py.SetArgv(args); // register the atexit callback (this doesn't use PyPy_AtExit as the C atexit // callbacks are called after python is fully finalized but the python ones // are called while the python engine is still running). string code = "import atexit, clr\n" + "atexit.register(clr._AtExit)\n"; PyObject r = PythonEngine.RunString(code); if (r != null) { r.Dispose(); } // Load the clr.py resource into the clr module IntPtr clr = Python.Runtime.ImportHook.GetCLRModule(); IntPtr clr_dict = Runtime.PyPyModule_GetDict(clr); var locals = new PyDict(); try { IntPtr module = Runtime.PyPyImport_AddModule("clr._extras"); IntPtr module_globals = Runtime.PyPyModule_GetDict(module); IntPtr builtins = Runtime.PyPyEval_GetBuiltins(); Runtime.PyPyDict_SetItemString(module_globals, "__builtins__", builtins); Assembly assembly = Assembly.GetExecutingAssembly(); using (Stream stream = assembly.GetManifestResourceStream("clr.py")) using (var reader = new StreamReader(stream)) { // add the contents of clr.py to the module string clr_py = reader.ReadToEnd(); PyObject result = RunString(clr_py, module_globals, locals.Handle); if (null == result) { throw new PythonException(); } result.Dispose(); } // add the imported module to the clr module, and copy the API functions // and decorators into the main clr module. Runtime.PyPyDict_SetItemString(clr_dict, "_extras", module); foreach (PyObject key in locals.Keys()) { if (!key.ToString().StartsWith("_") || key.ToString().Equals("__version__")) { PyObject value = locals[key]; Runtime.PyPyDict_SetItem(clr_dict, key.Handle, value.Handle); value.Dispose(); } key.Dispose(); } } finally { locals.Dispose(); } } } /// /// A helper to perform initialization from the context of an active /// CPython interpreter process - this bootstraps the managed runtime /// when it is imported by the CLR extension module. /// #if PYTHON3 public static IntPtr InitExt() #elif PYTHON2 public static void InitExt() #endif { try { Initialize(); // Trickery - when the import hook is installed into an already // running Python, the standard import machinery is still in // control for the duration of the import that caused bootstrap. // // That is problematic because the std machinery tries to get // sub-names directly from the module __dict__ rather than going // through our module object's getattr hook. This workaround is // evil ;) We essentially climb up the stack looking for the // import that caused the bootstrap to happen, then re-execute // the import explicitly after our hook has been installed. By // doing this, the original outer import should work correctly. // // Note that this is only needed during the execution of the // first import that installs the CLR import hook. This hack // still doesn't work if you use the interactive interpreter, // since there is no line info to get the import line ;( string code = "import traceback\n" + "for item in traceback.extract_stack():\n" + " line = item[3]\n" + " if line is not None:\n" + " if line.startswith('import CLR') or \\\n" + " line.startswith('import clr') or \\\n" + " line.startswith('from clr') or \\\n" + " line.startswith('from CLR'):\n" + " exec(line)\n" + " break\n"; PyObject r = PythonEngine.RunString(code); if (r != null) { r.Dispose(); } } catch (PythonException e) { e.Restore(); #if PYTHON3 return IntPtr.Zero; #endif } #if PYTHON3 return Python.Runtime.ImportHook.GetCLRModule(); #endif } /// /// Shutdown Method /// /// /// Shutdown and release resources held by the Python runtime. The /// Python runtime can no longer be used in the current process /// after calling the Shutdown method. /// public static void Shutdown() { if (initialized) { Runtime.Shutdown(); initialized = false; } } /// /// AcquireLock Method /// /// /// Acquire the Python global interpreter lock (GIL). Managed code /// *must* call this method before using any objects or calling any /// methods on objects in the Python.Runtime namespace. The only /// exception is PythonEngine.Initialize, which may be called without /// first calling AcquireLock. /// Each call to AcquireLock must be matched by a corresponding call /// to ReleaseLock, passing the token obtained from AcquireLock. /// For more information, see the "Extending and Embedding" section /// of the Python documentation on www.python.org. /// public static IntPtr AcquireLock() { return Runtime.PyPyGILState_Ensure(); } /// /// ReleaseLock Method /// /// /// Release the Python global interpreter lock using a token obtained /// from a previous call to AcquireLock. /// For more information, see the "Extending and Embedding" section /// of the Python documentation on www.python.org. /// public static void ReleaseLock(IntPtr gs) { Runtime.PyPyGILState_Release(gs); } /// /// BeginAllowThreads Method /// /// /// Release the Python global interpreter lock to allow other threads /// to run. This is equivalent to the PyPy_BEGIN_ALLOW_THREADS macro /// provided by the C Python API. /// For more information, see the "Extending and Embedding" section /// of the Python documentation on www.python.org. /// public static IntPtr BeginAllowThreads() { return Runtime.PyPyEval_SaveThread(); } /// /// EndAllowThreads Method /// /// /// Re-aquire the Python global interpreter lock for the current /// thread. This is equivalent to the PyPy_END_ALLOW_THREADS macro /// provided by the C Python API. /// For more information, see the "Extending and Embedding" section /// of the Python documentation on www.python.org. /// public static void EndAllowThreads(IntPtr ts) { Runtime.PyPyEval_RestoreThread(ts); } /// /// 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.PyPyImport_ImportModule(name); Py.Throw(); return new PyObject(op); } /// /// ReloadModule Method /// /// /// Given a PyObject representing a previously loaded module, reload /// the module. /// public static PyObject ReloadModule(PyObject module) { IntPtr op = Runtime.PyPyImport_ReloadModule(module.Handle); Py.Throw(); return new PyObject(op); } /// /// 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. /// public static PyObject ModuleFromString(string name, string code) { IntPtr c = Runtime.PyPy_CompileString(code, "none", (IntPtr)257); Py.Throw(); IntPtr m = Runtime.PyPyImport_ExecCodeModule(name, c); Py.Throw(); return new PyObject(m); } /// /// RunString Method /// /// /// Run a string containing Python code. Returns the result of /// executing the code string as a PyObject instance, or null if /// an exception was raised. /// public static PyObject RunString( string code, IntPtr? globals = null, IntPtr? locals = null ) { var borrowedGlobals = true; if (globals == null) { globals = Runtime.PyPyEval_GetGlobals(); if (globals == IntPtr.Zero) { globals = Runtime.PyPyDict_New(); Runtime.PyPyDict_SetItemString( globals.Value, "__builtins__", Runtime.PyPyEval_GetBuiltins() ); borrowedGlobals = false; } } var borrowedLocals = true; if (locals == null) { locals = Runtime.PyPyDict_New(); borrowedLocals = false; } var flag = (IntPtr)257; /* PyPy_file_input */ try { IntPtr result = Runtime.PyPyRun_String( code, flag, globals.Value, locals.Value ); Py.Throw(); return new PyObject(result); } finally { if (!borrowedLocals) { Runtime.XDecref(locals.Value); } if (!borrowedGlobals) { Runtime.XDecref(globals.Value); } } } } public static class Py { public static GILState GIL() { if (!PythonEngine.IsInitialized) { PythonEngine.Initialize(); } return new GILState(); } public class GILState : IDisposable { private IntPtr state; internal GILState() { state = PythonEngine.AcquireLock(); } public void Dispose() { PythonEngine.ReleaseLock(state); GC.SuppressFinalize(this); } ~GILState() { Dispose(); } } public class KeywordArguments : PyDict { } public static KeywordArguments kw(params object[] kv) { var dict = new KeywordArguments(); if (kv.Length % 2 != 0) { throw new ArgumentException("Must have an equal number of keys and values"); } for (var i = 0; i < kv.Length; i += 2) { IntPtr value; if (kv[i + 1] is PyObject) { value = ((PyObject)kv[i + 1]).Handle; } else { value = Converter.ToPython(kv[i + 1], kv[i + 1]?.GetType()); } if (Runtime.PyPyDict_SetItemString(dict.Handle, (string)kv[i], value) != 0) { throw new ArgumentException(string.Format("Cannot add key '{0}' to dictionary.", (string)kv[i])); } if (!(kv[i + 1] is PyObject)) { Runtime.XDecref(value); } } return dict; } public static PyObject Import(string name) { return PythonEngine.ImportModule(name); } public static void SetArgv() { IEnumerable args; try { args = Environment.GetCommandLineArgs(); } catch (NotSupportedException) { args = Enumerable.Empty(); } SetArgv( new[] { "" }.Concat( Environment.GetCommandLineArgs().Skip(1) ) ); } public static void SetArgv(params string[] argv) { SetArgv(argv as IEnumerable); } public static void SetArgv(IEnumerable argv) { using (GIL()) { string[] arr = argv.ToArray(); Runtime.PyPySys_SetArgvEx(arr.Length, arr, 0); Py.Throw(); } } internal static void Throw() { using (GIL()) { if (Runtime.PyPyErr_Occurred() != 0) { throw new PythonException(); } } } } }
X Tutup