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;
}
}