// ========================================================================== // This software is subject to the provisions of the Zope Public License, // Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. // THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED // WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS // FOR A PARTICULAR PURPOSE. // ========================================================================== using System; using System.Threading; namespace Python.Runtime { /// /// This class provides the public interface of the Python runtime. /// public class PythonEngine { private static DelegateManager delegateManager; private static bool initialized; #region Properties 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.Py_GetProgramName(); if (result == null) { return ""; } return result; } set { Runtime.Py_SetProgramName(value); } } public static string PythonHome { get { string result = Runtime.Py_GetPythonHome(); if (result == null) { return ""; } return result; } set { Runtime.Py_SetPythonHome(value); } } public static string Version { get { return Runtime.Py_GetVersion(); } } public static string BuildInfo { get { return Runtime.Py_GetBuildInfo(); } } public static string Platform { get { return Runtime.Py_GetPlatform(); } } public static string Copyright { get { return Runtime.Py_GetCopyright(); } } public static int RunSimpleString(string code) { return Runtime.PyRun_SimpleString(code); } #endregion /// /// 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() { 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(); } } //==================================================================== // 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. //==================================================================== public static void InitExt() { 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(); } } /// /// 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.PyGILState_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.PyGILState_Release(gs); } /// /// BeginAllowThreads Method /// /// /// /// Release the Python global interpreter lock to allow other threads /// to run. This is equivalent to the Py_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.PyEval_SaveThread(); } /// /// EndAllowThreads Method /// /// /// /// Re-aquire the Python global interpreter lock for the current /// thread. This is equivalent to the Py_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.PyEval_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.PyImport_ImportModule(name); if (op == IntPtr.Zero) { return null; } 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.PyImport_ReloadModule(module.Handle); if (op == IntPtr.Zero) { throw new PythonException(); } 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.Py_CompileString(code, "none", (IntPtr)257); if (c == IntPtr.Zero) { throw new PythonException(); } IntPtr m = Runtime.PyImport_ExecCodeModule(name, c); if (m == IntPtr.Zero) { throw new PythonException(); } 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 = Runtime.PyEval_GetGlobals(); IntPtr locals = Runtime.PyDict_New(); IntPtr builtins = Runtime.PyEval_GetBuiltins(); Runtime.PyDict_SetItemString(locals, "__builtins__", builtins); IntPtr flag = (IntPtr)257; /* Py_file_input */ IntPtr result = Runtime.PyRun_String(code, flag, globals, locals); Runtime.Decref(locals); if (result == IntPtr.Zero) { return null; } return new PyObject(result); } } }