using System; using System.Runtime.InteropServices; using System.Security; using System.Text; using System.Threading; using System.Collections.Generic; using Python.Runtime.Platform; namespace Python.Runtime { /// /// Encapsulates the low-level Python C API. Note that it is /// the responsibility of the caller to have acquired the GIL /// before calling any of these methods. /// public static partial class Runtime { // C# compiler copies constants to the assemblies that references this library. // We needs to replace all public constants to static readonly fields to allow // binary substitution of different Python.Runtime.dll builds in a target application. // set to true when python is finalizing internal static object IsFinalizingLock = new object(); internal static bool IsFinalizing; internal static bool Is32Bit = IntPtr.Size == 4; internal static bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); #if PYTHON2 internal static bool IsPython2 = true; #else internal static bool IsPython2 = false; #endif internal static bool IsPython3 = !IsPython2; public static int MainManagedThreadId { get; private set; } #if UCS2 && PYTHON2 static int _UCS = 2; #else static int _UCS = 4; #endif /// /// Encoding to use to convert Unicode to/from Managed to Native /// internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32; /// /// Initialize the runtime... /// internal static void Initialize(bool initSigs = false) { if (Py_IsInitialized() == 0) { Py_InitializeEx(initSigs ? 1 : 0); MainManagedThreadId = Thread.CurrentThread.ManagedThreadId; } if (PyEval_ThreadsInitialized() == 0) { PyEval_InitThreads(); } IsFinalizing = false; CLRModule.Reset(); GenericUtil.Reset(); PyScopeManager.Reset(); ClassManager.Reset(); ClassDerivedObject.Reset(); TypeManager.Reset(); IntPtr op; IntPtr dict; if (IsPython3) { op = PyImport_ImportModule("builtins"); dict = PyObject_GetAttrString(op, "__dict__"); } else // Python2 { dict = PyImport_GetModuleDict(); op = PyDict_GetItemString(dict, "__builtin__"); } PyNotImplemented = PyObject_GetAttrString(op, "NotImplemented"); PyBaseObjectType = PyObject_GetAttrString(op, "object"); PyNone = PyObject_GetAttrString(op, "None"); PyTrue = PyObject_GetAttrString(op, "True"); PyFalse = PyObject_GetAttrString(op, "False"); PyBoolType = PyObject_Type(PyTrue); PyNoneType = PyObject_Type(PyNone); PyTypeType = PyObject_Type(PyNoneType); op = PyObject_GetAttrString(dict, "keys"); PyMethodType = PyObject_Type(op); XDecref(op); // For some arcane reason, builtins.__dict__.__setitem__ is *not* // a wrapper_descriptor, even though dict.__setitem__ is. // // object.__init__ seems safe, though. op = PyObject_GetAttrString(PyBaseObjectType, "__init__"); PyWrapperDescriptorType = PyObject_Type(op); XDecref(op); #if !PYTHON2 XDecref(dict); #endif op = PyString_FromString("string"); PyStringType = PyObject_Type(op); XDecref(op); op = PyUnicode_FromString("unicode"); PyUnicodeType = PyObject_Type(op); XDecref(op); #if !PYTHON2 op = PyBytes_FromString("bytes"); PyBytesType = PyObject_Type(op); XDecref(op); #endif op = PyTuple_New(0); PyTupleType = PyObject_Type(op); XDecref(op); op = PyList_New(0); PyListType = PyObject_Type(op); XDecref(op); op = PyDict_New(); PyDictType = PyObject_Type(op); XDecref(op); op = PyInt_FromInt32(0); PyIntType = PyObject_Type(op); XDecref(op); op = PyLong_FromLong(0); PyLongType = PyObject_Type(op); XDecref(op); op = PyFloat_FromDouble(0); PyFloatType = PyObject_Type(op); XDecref(op); PyClassType = IntPtr.Zero; PyInstanceType = IntPtr.Zero; #if PYTHON2 IntPtr s = PyString_FromString("_temp"); IntPtr d = PyDict_New(); IntPtr c = PyClass_New(IntPtr.Zero, d, s); PyClassType = PyObject_Type(c); IntPtr i = PyInstance_New(c, IntPtr.Zero, IntPtr.Zero); PyInstanceType = PyObject_Type(i); XDecref(s); XDecref(i); XDecref(c); XDecref(d); #endif Error = new IntPtr(-1); IntPtr dllLocal = IntPtr.Zero; var loader = LibraryLoader.Instance; _PyObject_NextNotImplemented = loader.GetFunction(dllLocal, "_PyObject_NextNotImplemented"); PyModuleType = loader.GetFunction(dllLocal, "PyModule_Type"); // Initialize modules that depend on the runtime class. AssemblyManager.Initialize(); PyCLRMetaType = MetaType.Initialize(); Exceptions.Initialize(); ImportHook.Initialize(); // Need to add the runtime directory to sys.path so that we // can find built-in assemblies like System.Data, et. al. string rtdir = RuntimeEnvironment.GetRuntimeDirectory(); IntPtr path = PySys_GetObject("path"); IntPtr item = PyString_FromString(rtdir); PyList_Append(path, item); XDecref(item); AssemblyManager.UpdatePath(); } internal static void Shutdown() { AssemblyManager.Shutdown(); Exceptions.Shutdown(); ImportHook.Shutdown(); Finalizer.Shutdown(); Py_Finalize(); } // called *without* the GIL acquired by clr._AtExit internal static int AtExit() { lock (IsFinalizingLock) { IsFinalizing = true; } return 0; } internal static IntPtr Py_single_input = (IntPtr)256; internal static IntPtr Py_file_input = (IntPtr)257; internal static IntPtr Py_eval_input = (IntPtr)258; internal static IntPtr PyBaseObjectType; internal static IntPtr PyModuleType; internal static IntPtr PyClassType; internal static IntPtr PyInstanceType; internal static IntPtr PyCLRMetaType; internal static IntPtr PyMethodType; internal static IntPtr PyWrapperDescriptorType; internal static IntPtr PyUnicodeType; internal static IntPtr PyStringType; internal static IntPtr PyTupleType; internal static IntPtr PyListType; internal static IntPtr PyDictType; internal static IntPtr PyIntType; internal static IntPtr PyLongType; internal static IntPtr PyFloatType; internal static IntPtr PyBoolType; internal static IntPtr PyNoneType; internal static IntPtr PyTypeType; internal static IntPtr PyBytesType; internal static IntPtr _PyObject_NextNotImplemented; internal static IntPtr PyNotImplemented; internal const int Py_LT = 0; internal const int Py_LE = 1; internal const int Py_EQ = 2; internal const int Py_NE = 3; internal const int Py_GT = 4; internal const int Py_GE = 5; internal static IntPtr PyTrue; internal static IntPtr PyFalse; internal static IntPtr PyNone; internal static IntPtr Error; /// /// Check if any Python Exceptions occurred. /// If any exist throw new PythonException. /// /// /// Can be used instead of `obj == IntPtr.Zero` for example. /// internal static void CheckExceptionOccurred() { if (PyErr_Occurred() != IntPtr.Zero) { throw new PythonException(); } } internal static IntPtr ExtendTuple(IntPtr t, params IntPtr[] args) { var size = PyTuple_Size(t); int add = args.Length; IntPtr item; IntPtr items = PyTuple_New(size + add); for (var i = 0; i < size; i++) { item = PyTuple_GetItem(t, i); XIncref(item); PyTuple_SetItem(items, i, item); } for (var n = 0; n < add; n++) { item = args[n]; XIncref(item); PyTuple_SetItem(items, size + n, item); } return items; } internal static Type[] PythonArgsToTypeArray(IntPtr arg) { return PythonArgsToTypeArray(arg, false); } internal static Type[] PythonArgsToTypeArray(IntPtr arg, bool mangleObjects) { // Given a PyObject * that is either a single type object or a // tuple of (managed or unmanaged) type objects, return a Type[] // containing the CLR Type objects that map to those types. IntPtr args = arg; var free = false; if (!PyTuple_Check(arg)) { args = PyTuple_New(1); XIncref(arg); PyTuple_SetItem(args, 0, arg); free = true; } var n = PyTuple_Size(args); var types = new Type[n]; Type t = null; for (var i = 0; i < n; i++) { IntPtr op = PyTuple_GetItem(args, i); if (mangleObjects && (!PyType_Check(op))) { op = PyObject_TYPE(op); } ManagedType mt = ManagedType.GetManagedObject(op); if (mt is ClassBase) { t = ((ClassBase)mt).type; } else if (mt is CLRObject) { object inst = ((CLRObject)mt).inst; if (inst is Type) { t = inst as Type; } } else { t = Converter.GetTypeByAlias(op); } if (t == null) { types = null; break; } types[i] = t; } if (free) { XDecref(args); } return types; } /// /// Managed exports of the Python C API. Where appropriate, we do /// some optimization to avoid managed <--> unmanaged transitions /// (mostly for heavily used methods). /// internal static unsafe void XIncref(IntPtr op) { Py_IncRef(op); return; } internal static unsafe void XDecref(IntPtr op) { Py_DecRef(op); return; } internal static unsafe long Refcount(IntPtr op) { var p = (void*)op; if ((void*)0 == p) { return 0; } return Is32Bit ? (*(int*)p) : (*(long*)p); } //==================================================================== // Python abstract object API //==================================================================== /// /// A macro-like method to get the type of a Python object. This is /// designed to be lean and mean in IL & avoid managed <-> unmanaged /// transitions. Note that this does not incref the type object. /// internal static unsafe IntPtr PyObject_TYPE(IntPtr op) { var p = (void*)op; if ((void*)0 == p) { return IntPtr.Zero; } #if PYTHON_WITH_PYDEBUG // TODO: Only for Python <3.8 var n = 3; #else var n = 1; #endif return Is32Bit ? new IntPtr((void*)(*((uint*)p + n))) : new IntPtr((void*)(*((ulong*)p + n))); } /// /// Managed version of the standard Python C API PyObject_Type call. /// This version avoids a managed <-> unmanaged transition. /// This one does incref the returned type object. /// internal static IntPtr PyObject_Type(IntPtr op) { IntPtr tp = PyObject_TYPE(op); XIncref(tp); return tp; } internal static string PyObject_GetTypeName(IntPtr op) { IntPtr pyType = Marshal.ReadIntPtr(op, ObjectOffset.ob_type); IntPtr ppName = Marshal.ReadIntPtr(pyType, TypeOffset.tp_name); return Marshal.PtrToStringAnsi(ppName); } /// /// Test whether the Python object is an iterable. /// internal static bool PyObject_IsIterable(IntPtr pointer) { var ob_type = Marshal.ReadIntPtr(pointer, ObjectOffset.ob_type); #if PYTHON2 long tp_flags = Util.ReadCLong(ob_type, TypeOffset.tp_flags); if ((tp_flags & TypeFlags.HaveIter) == 0) return false; #endif IntPtr tp_iter = Marshal.ReadIntPtr(ob_type, TypeOffset.tp_iter); return tp_iter != IntPtr.Zero; } internal static int PyObject_Compare(IntPtr value1, IntPtr value2) { int res; res = PyObject_RichCompareBool(value1, value2, Py_LT); if (-1 == res) return -1; else if (1 == res) return -1; res = PyObject_RichCompareBool(value1, value2, Py_EQ); if (-1 == res) return -1; else if (1 == res) return 0; res = PyObject_RichCompareBool(value1, value2, Py_GT); if (-1 == res) return -1; else if (1 == res) return 1; Exceptions.SetError(Exceptions.SystemError, "Error comparing objects"); return -1; } internal static long PyObject_Size(IntPtr pointer) { return (long) _PyObject_Size(pointer); } //==================================================================== // Python number API //==================================================================== internal static bool PyInt_Check(IntPtr ob) { return PyObject_TypeCheck(ob, PyIntType); } internal static bool PyBool_Check(IntPtr ob) { return PyObject_TypeCheck(ob, PyBoolType); } internal static IntPtr PyInt_FromInt32(int value) { var v = new IntPtr(value); return PyInt_FromLong(v); } internal static IntPtr PyInt_FromInt64(long value) { var v = new IntPtr(value); return PyInt_FromLong(v); } internal static bool PyLong_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyLongType; } internal static IntPtr PyLong_FromUnsignedLong(object value) { if(Is32Bit || IsWindows) return PyLong_FromUnsignedLong32(Convert.ToUInt32(value)); else return PyLong_FromUnsignedLong64(Convert.ToUInt64(value)); } internal static object PyLong_AsUnsignedLong(IntPtr value) { if (Is32Bit || IsWindows) return PyLong_AsUnsignedLong32(value); else return PyLong_AsUnsignedLong64(value); } internal static bool PyFloat_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyFloatType; } //==================================================================== // Python sequence API //==================================================================== internal static IntPtr PySequence_GetItem(IntPtr pointer, long index) { return PySequence_GetItem(pointer, new IntPtr(index)); } internal static int PySequence_SetItem(IntPtr pointer, long index, IntPtr value) { return PySequence_SetItem(pointer, new IntPtr(index), value); } internal static int PySequence_DelItem(IntPtr pointer, long index) { return PySequence_DelItem(pointer, new IntPtr(index)); } internal static IntPtr PySequence_GetSlice(IntPtr pointer, long i1, long i2) { return PySequence_GetSlice(pointer, new IntPtr(i1), new IntPtr(i2)); } internal static int PySequence_SetSlice(IntPtr pointer, long i1, long i2, IntPtr v) { return PySequence_SetSlice(pointer, new IntPtr(i1), new IntPtr(i2), v); } internal static int PySequence_DelSlice(IntPtr pointer, long i1, long i2) { return PySequence_DelSlice(pointer, new IntPtr(i1), new IntPtr(i2)); } internal static long PySequence_Size(IntPtr pointer) { return (long) _PySequence_Size(pointer); } internal static IntPtr PySequence_Repeat(IntPtr pointer, long count) { return PySequence_Repeat(pointer, new IntPtr(count)); } internal static long PySequence_Count(IntPtr pointer, IntPtr value) { return (long) _PySequence_Count(pointer, value); } //==================================================================== // Python string API //==================================================================== internal static bool IsStringType(IntPtr op) { IntPtr t = PyObject_TYPE(op); return (t == PyStringType) || (t == PyUnicodeType); } internal static bool PyString_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyStringType; } internal static IntPtr PyString_FromString(string value) { #if !PYTHON2 return PyUnicode_FromKindAndData(_UCS, value, value.Length); #else return PyString_FromStringAndSize(value, value.Length); #endif } #if !PYTHON2 internal static long PyBytes_Size(IntPtr op) { return (long) _PyBytes_Size(op); } internal static IntPtr PyBytes_AS_STRING(IntPtr ob) { return ob + BytesOffset.ob_sval; } internal static IntPtr PyString_FromStringAndSize(string value, long size) { return _PyString_FromStringAndSize(value, new IntPtr(size)); } internal static IntPtr PyUnicode_FromStringAndSize(IntPtr value, long size) { return PyUnicode_FromStringAndSize(value, new IntPtr(size)); } #else internal static IntPtr PyString_FromStringAndSize(string value, long size) { return PyString_FromStringAndSize(value, new IntPtr(size)); } #endif internal static bool PyUnicode_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyUnicodeType; } #if !PYTHON2 internal static IntPtr PyUnicode_FromKindAndData(int kind, string s, long size) { return PyUnicode_FromKindAndData(kind, s, new IntPtr(size)); } internal static IntPtr PyUnicode_FromUnicode(string s, long size) { return PyUnicode_FromKindAndData(_UCS, s, size); } internal static long PyUnicode_GetSize(IntPtr ob) { return (long)_PyUnicode_GetSize(ob); } #else internal static IntPtr PyUnicode_FromUnicode(string s, long size) { return PyUnicode_FromUnicode(s, new IntPtr(size)); } internal static long PyUnicode_GetSize(IntPtr ob) { return (long) _PyUnicode_GetSize(ob); } #endif internal static IntPtr PyUnicode_FromString(string s) { return PyUnicode_FromUnicode(s, s.Length); } /// /// Function to access the internal PyUnicode/PyString object and /// convert it to a managed string with the correct encoding. /// /// /// We can't easily do this through through the CustomMarshaler's on /// the returns because will have access to the IntPtr but not size. /// /// For PyUnicodeType, we can't convert with Marshal.PtrToStringUni /// since it only works for UCS2. /// /// PyStringType or PyUnicodeType object to convert /// Managed String internal static string GetManagedString(IntPtr op) { IntPtr type = PyObject_TYPE(op); #if PYTHON2 // Python 3 strings are all Unicode if (type == PyStringType) { return Marshal.PtrToStringAnsi(PyString_AsString(op), PyString_Size(op)); } #endif if (type == PyUnicodeType) { IntPtr p = PyUnicode_AsUnicode(op); int length = (int)PyUnicode_GetSize(op); int size = length * _UCS; var buffer = new byte[size]; Marshal.Copy(p, buffer, 0, size); return PyEncoding.GetString(buffer, 0, size); } return null; } //==================================================================== // Python dictionary API //==================================================================== internal static bool PyDict_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyDictType; } internal static long PyDict_Size(IntPtr pointer) { return (long) _PyDict_Size(pointer); } //==================================================================== // Python list API //==================================================================== internal static bool PyList_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyListType; } internal static IntPtr PyList_New(long size) { return PyList_New(new IntPtr(size)); } internal static IntPtr PyList_GetItem(IntPtr pointer, long index) { return PyList_GetItem(pointer, new IntPtr(index)); } internal static int PyList_SetItem(IntPtr pointer, long index, IntPtr value) { return PyList_SetItem(pointer, new IntPtr(index), value); } internal static int PyList_Insert(IntPtr pointer, long index, IntPtr value) { return PyList_Insert(pointer, new IntPtr(index), value); } internal static IntPtr PyList_GetSlice(IntPtr pointer, long start, long end) { return PyList_GetSlice(pointer, new IntPtr(start), new IntPtr(end)); } internal static int PyList_SetSlice(IntPtr pointer, long start, long end, IntPtr value) { return PyList_SetSlice(pointer, new IntPtr(start), new IntPtr(end), value); } internal static long PyList_Size(IntPtr pointer) { return (long) _PyList_Size(pointer); } //==================================================================== // Python tuple API //==================================================================== internal static bool PyTuple_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyTupleType; } internal static IntPtr PyTuple_New(long size) { return PyTuple_New(new IntPtr(size)); } internal static IntPtr PyTuple_GetItem(IntPtr pointer, long index) { return PyTuple_GetItem(pointer, new IntPtr(index)); } internal static int PyTuple_SetItem(IntPtr pointer, long index, IntPtr value) { return PyTuple_SetItem(pointer, new IntPtr(index), value); } internal static IntPtr PyTuple_GetSlice(IntPtr pointer, long start, long end) { return PyTuple_GetSlice(pointer, new IntPtr(start), new IntPtr(end)); } internal static long PyTuple_Size(IntPtr pointer) { return (long) _PyTuple_Size(pointer); } //==================================================================== // Python iterator API //==================================================================== internal static bool PyIter_Check(IntPtr pointer) { var ob_type = Marshal.ReadIntPtr(pointer, ObjectOffset.ob_type); #if PYTHON2 long tp_flags = Util.ReadCLong(ob_type, TypeOffset.tp_flags); if ((tp_flags & TypeFlags.HaveIter) == 0) return false; #endif IntPtr tp_iternext = Marshal.ReadIntPtr(ob_type, TypeOffset.tp_iternext); return tp_iternext != IntPtr.Zero && tp_iternext != _PyObject_NextNotImplemented; } //==================================================================== // Python type object API //==================================================================== internal static bool PyType_Check(IntPtr ob) { return PyObject_TypeCheck(ob, PyTypeType); } internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) { IntPtr t = PyObject_TYPE(ob); return (t == tp) || PyType_IsSubtype(t, tp); } internal static IntPtr PyType_GenericAlloc(IntPtr type, long n) { return PyType_GenericAlloc(type, new IntPtr(n)); } //==================================================================== // Python memory API //==================================================================== internal static IntPtr PyMem_Malloc(long size) { return PyMem_Malloc(new IntPtr(size)); } internal static IntPtr PyMem_Realloc(IntPtr ptr, long size) { return PyMem_Realloc(ptr, new IntPtr(size)); } internal static void SetNoSiteFlag() { SetPyNoSiteFlag(1); } internal static Interfaces.ILibPython LibPython; } }