diff --git a/.gitignore b/.gitignore index ee40d233aa..4a1393835a 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,8 @@ _ReSharper* # Installshield output folder [Ee]xpress +.idea + # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT diff --git a/Plugins/WinAlfred.Plugin.DouBan/Images/movies.png b/Plugins/WinAlfred.Plugin.DouBan/Images/movies.png new file mode 100644 index 0000000000..a8ec632c4f Binary files /dev/null and b/Plugins/WinAlfred.Plugin.DouBan/Images/movies.png differ diff --git a/Plugins/WinAlfred.Plugin.DouBan/main.py b/Plugins/WinAlfred.Plugin.DouBan/main.py index 00eaf442d9..e74e852a82 100644 --- a/Plugins/WinAlfred.Plugin.DouBan/main.py +++ b/Plugins/WinAlfred.Plugin.DouBan/main.py @@ -2,21 +2,31 @@ import requests from bs4 import BeautifulSoup import json +import webbrowser -class PyWinAlfred(): +def query(key): + k = key.split(" ")[1] + if not k: + return "" + r = requests.get('http://movie.douban.com/subject_search?search_text=' + k) + bs = BeautifulSoup(r.text) + results = [] + for i in bs.select(".article table .pl2"): + res = {} + title = i.select("a")[0].text.replace("\n","").replace(" ","") + score = i.select("span.rating_nums")[0].text if i.select("span.rating_nums") else "0" + res["Title"] = title.split("/")[0] + year = i.select("p.pl")[0].text.split("-")[0] if i.select("p.pl")[0] else "Null" + alias = title.split("/")[1] if len(title.split("/")) >= 2 else "Null" + res["SubTitle"] = "Year: " + year + " Score: " + score + " Alias: " + alias + res["ActionName"] = "openUrl" + res["IcoPath"] = "Images\\movies.png" + res["ActionPara"] = i.select("a[href]")[0]["href"] + results.append(res) + return json.dumps(results) - def query(self,key): - k = key.split(" ")[1] - r = requests.get('http://movie.douban.com/subject_search?search_text=' + k) - bs = BeautifulSoup(r.text) - results = [] - for i in bs.select(".article table .pl2 a"): - res = {} - t = i.text.strip().replace(" ","") - res["Title"] = t.replace("\\n","") - results.append(res) - return json.dumps(results) +def openUrl(url): + webbrowser.open(url) if __name__ == "__main__": - p = PyWinAlfred() - print p.query("movie geo") + print query("movie geo") diff --git a/PyWinAlfred/Main.cpp b/PyWinAlfred/Main.cpp deleted file mode 100644 index 75306ab75e..0000000000 --- a/PyWinAlfred/Main.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include "Python.h" -#include -#include - -extern "C" __declspec(dllexport) void InitPythonEnv() -{ - Py_Initialize(); - PyEval_InitThreads(); -} - -char* GetErrorMessage() -{ - char *pStrErrorMessage = NULL; - - if(PyErr_Occurred()){ - PyObject *ptype, *pvalue, *ptraceback; - PyErr_Fetch(&ptype, &pvalue, &ptraceback); - pStrErrorMessage = PyString_AsString(pvalue); - } - - return pStrErrorMessage; -} - - -char* Exec(char* directory, char* file, char* method, char* para) -{ - PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *pClass, *pInstance; - char *error; - - PyThreadState* global_state = PyThreadState_Get(); - PyThreadState* ts = Py_NewInterpreter(); - PyThreadState_Swap(ts); - // Initialise the Python interpreter - - // Create GIL/enable threads - - //PyGILState_STATE gstate = PyGILState_Ensure(); - // // Get the default thread state - // PyThreadState* state = PyThreadState_Get(); - // // Once in each thread - //PyThreadState* stateForNewThread = PyThreadState_New(state->interp); - //PyEval_RestoreThread(stateForNewThread); - - // Build the name object - PyObject *sys = PyImport_ImportModule("sys"); - PyObject *path = PyObject_GetAttrString(sys, "path"); - PyList_Append(path, PyString_FromString(directory)); - - pName = PyString_FromString(file); - error = GetErrorMessage(); - if(error != NULL){ - char* err =new char[5000](); - sprintf(error, "%s:%s","PYTHONERROR",error); - return err; - } - - pModule = PyImport_Import(pName); - error = GetErrorMessage(); - if(error != NULL){ - char* err =new char[5000](); - sprintf(err, "%s:%s","PYTHONERROR",error); - return err; - } - - pDict = PyModule_GetDict(pModule); - error = GetErrorMessage(); - if(error != NULL){ - char* err =new char[5000](); - sprintf(err, "%s:%s","PYTHONERROR",error); - return err; - } - - pClass = PyDict_GetItemString(pDict,"PyWinAlfred"); - error = GetErrorMessage(); - if(error != NULL){ - char* err =new char[5000](); - sprintf(err, "%s:%s","PYTHONERROR",error); - return err; - } - - pInstance = PyObject_CallObject(pClass, NULL); - error = GetErrorMessage(); - if(error != NULL){ - char* err =new char[5000](); - sprintf(err, "%s:%s","PYTHONERROR",error); - return err; - } - - // Call a method of the class with two parameters - pValue = PyObject_CallMethod(pInstance,method, "(s)",para); - error = GetErrorMessage(); - if(error != NULL){ - char* err =new char[5000](); - sprintf(err, "%s:%s","PYTHONERROR",error); - return err; - } - - char * str_ret = PyString_AsString(pValue); - - //PyEval_SaveThread(); - - // Finish the Python Interpreter - - PyErr_Clear(); - Py_EndInterpreter(ts); - PyThreadState_Swap(global_state); - - return str_ret; -} - -extern "C" __declspec(dllexport) char* ExecPython(char* directory, char* file, char* method, char* para) -{ - auto future = std::async(Exec,directory,file,method,para); - return future.get(); -} \ No newline at end of file diff --git a/PyWinAlfred/PyWinAlfred.vcxproj b/PyWinAlfred/PyWinAlfred.vcxproj deleted file mode 100644 index 6251ad6ae4..0000000000 --- a/PyWinAlfred/PyWinAlfred.vcxproj +++ /dev/null @@ -1,156 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {D03FD663-38A8-4C1A-8431-EB44F93E7EBA} - Win32Proj - PyWinAlfred - - - - DynamicLibrary - true - v110 - Unicode - - - DynamicLibrary - true - v110 - MultiByte - - - DynamicLibrary - false - v110 - true - Unicode - - - DynamicLibrary - false - v110 - true - Unicode - - - - - - - - - - - - - - - - - - - true - C:\Python27\include;$(IncludePath) - C:\Python27\libs;$(LibraryPath) - - - true - C:\Python27\include;$(IncludePath) - C:\Python27\libs;$(LibraryPath) - $(SolutionDir)WinAlfred\bin\Debug\ - - - false - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;PYWINALFRED_EXPORTS;%(PreprocessorDefinitions) - true - - - Windows - true - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;PYWINALFRED_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true - - - Windows - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;_USRDLL;PYWINALFRED_EXPORTS;%(PreprocessorDefinitions) - true - - - Windows - true - true - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;_USRDLL;PYWINALFRED_EXPORTS;%(PreprocessorDefinitions) - true - - - Windows - true - true - true - - - - - - - - - \ No newline at end of file diff --git a/PyWinAlfred/PyWinAlfred.vcxproj.filters b/PyWinAlfred/PyWinAlfred.vcxproj.filters deleted file mode 100644 index 3b076d1fc7..0000000000 --- a/PyWinAlfred/PyWinAlfred.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - \ No newline at end of file diff --git a/Python.Runtime.dll b/Python.Runtime.dll new file mode 100644 index 0000000000..abbd5f37a1 Binary files /dev/null and b/Python.Runtime.dll differ diff --git a/Pythonnet.Runtime/Python.Runtime.csproj b/Pythonnet.Runtime/Python.Runtime.csproj new file mode 100644 index 0000000000..ca785225b0 --- /dev/null +++ b/Pythonnet.Runtime/Python.Runtime.csproj @@ -0,0 +1,170 @@ + + + + Debug + AnyCPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271} + Library + false + Python.Runtime + Python.Runtime + OnBuildSuccess + v3.5 + + + + true + full + true + .\bin\Debug\ + TRACE;DEBUG;PYTHON27,UCS2 + true + + + pdbonly + true + .\bin\Release\ + TRACE;PYTHON27, UCS2 + true + + + true + bin\EmbeddingTest\ + TRACE;DEBUG;PYTHON27,UCS2 + true + true + full + AnyCPU + + + true + bin\UnitTests\ + TRACE;DEBUG;PYTHON27,UCS2 + true + true + full + AnyCPU + + + true + bin\x64\Debug\ + TRACE;DEBUG;PYTHON26,UCS2 + true + true + full + x64 + 1607 + + + bin\x64\Release\ + TRACE;PYTHON26,UCS2 + true + true + pdbonly + x64 + 1607 + + + true + bin\x64\EmbeddingTest\ + TRACE;DEBUG;PYTHON26,UCS2 + true + true + full + x64 + 1607 + + + true + bin\x64\UnitTests\ + TRACE;DEBUG;PYTHON26,UCS2 + true + true + full + x64 + 1607 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + call "$(ProjectDir)buildclrmodule.bat" $(Platform) "$(ProjectDir)" "$(TargetDir)clr.pyd" +copy "$(TargetPath)" "$(SolutionDir)" +copy "$(TargetDir)*.pdb" "$(SolutionDir)" +copy "$(TargetDir)clr.pyd" "$(SolutionDir)" + + del "$(TargetDir)clr.pyd" + + \ No newline at end of file diff --git a/Pythonnet.Runtime/Python.Runtime.mdp b/Pythonnet.Runtime/Python.Runtime.mdp new file mode 100644 index 0000000000..845407ec2f --- /dev/null +++ b/Pythonnet.Runtime/Python.Runtime.mdp @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Pythonnet.Runtime/arrayobject.cs b/Pythonnet.Runtime/arrayobject.cs new file mode 100644 index 0000000000..c96fbd23c4 --- /dev/null +++ b/Pythonnet.Runtime/arrayobject.cs @@ -0,0 +1,252 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; + +namespace Python.Runtime { + + /// + /// Implements a Python type for managed arrays. This type is essentially + /// the same as a ClassObject, except that it provides sequence semantics + /// to support natural array usage (indexing) from Python. + /// + + internal class ArrayObject : ClassBase { + + internal ArrayObject(Type tp) : base(tp) {} + + internal override bool CanSubclass() { + return false; + } + + public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { + ArrayObject self = GetManagedObject(tp) as ArrayObject; + if (Runtime.PyTuple_Size(args) != 1) { + return Exceptions.RaiseTypeError("array expects 1 argument"); + } + IntPtr op = Runtime.PyTuple_GetItem(args, 0); + Object result; + + if (!Converter.ToManaged(op, self.type, out result, true)) { + return IntPtr.Zero; + } + return CLRObject.GetInstHandle(result, tp); + } + + + //==================================================================== + // Implements __getitem__ for array types. + //==================================================================== + + public static IntPtr mp_subscript(IntPtr ob, IntPtr idx) { + CLRObject obj = (CLRObject)ManagedType.GetManagedObject(ob); + Array items = obj.inst as Array; + Type itemType = obj.inst.GetType().GetElementType(); + int rank = items.Rank; + int index = 0; + object value; + + // Note that CLR 1.0 only supports int indexes - methods to + // support long indices were introduced in 1.1. We could + // support long indices automatically, but given that long + // indices are not backward compatible and a relative edge + // case, we won't bother for now. + + // Single-dimensional arrays are the most common case and are + // cheaper to deal with than multi-dimensional, so check first. + + if (rank == 1) { + index = (int)Runtime.PyInt_AsLong(idx); + + if (Exceptions.ErrorOccurred()) { + return Exceptions.RaiseTypeError("invalid index value"); + } + + if (index < 0) { + index = items.Length + index; + } + + try { + value = items.GetValue(index); + } + catch (IndexOutOfRangeException) { + Exceptions.SetError(Exceptions.IndexError, + "array index out of range" + ); + return IntPtr.Zero; + } + + return Converter.ToPython(items.GetValue(index), itemType); + } + + // Multi-dimensional arrays can be indexed a la: list[1, 2, 3]. + + if (!Runtime.PyTuple_Check(idx)) { + Exceptions.SetError(Exceptions.TypeError, + "invalid index value" + ); + return IntPtr.Zero; + } + + int count = Runtime.PyTuple_Size(idx); + + Array args = Array.CreateInstance(typeof(Int32), count); + + for (int i = 0; i < count; i++) { + IntPtr op = Runtime.PyTuple_GetItem(idx, i); + index = (int)Runtime.PyInt_AsLong(op); + + if (Exceptions.ErrorOccurred()) { + return Exceptions.RaiseTypeError("invalid index value"); + } + + if (index < 0) { + index = items.GetLength(i) + index; + } + + args.SetValue(index, i); + } + + try { + value = items.GetValue((int[]) args); + } + catch (IndexOutOfRangeException) { + Exceptions.SetError(Exceptions.IndexError, + "array index out of range" + ); + return IntPtr.Zero; + } + + return Converter.ToPython(value, itemType); + } + + + //==================================================================== + // Implements __setitem__ for array types. + //==================================================================== + + public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) { + CLRObject obj = (CLRObject)ManagedType.GetManagedObject(ob); + Array items = obj.inst as Array; + Type itemType = obj.inst.GetType().GetElementType(); + int rank = items.Rank; + int index = 0; + object value; + + if (items.IsReadOnly) { + Exceptions.RaiseTypeError("array is read-only"); + return -1; + } + + if (!Converter.ToManaged(v, itemType, out value, true)) { + return -1; + } + + if (rank == 1) { + index = (int)Runtime.PyInt_AsLong(idx); + + if (Exceptions.ErrorOccurred()) { + Exceptions.RaiseTypeError("invalid index value"); + return -1; + } + + if (index < 0) { + index = items.Length + index; + } + + try { + items.SetValue(value, index); + } + catch (IndexOutOfRangeException) { + Exceptions.SetError(Exceptions.IndexError, + "array index out of range" + ); + return -1; + } + + return 0; + } + + if (!Runtime.PyTuple_Check(idx)) { + Exceptions.RaiseTypeError("invalid index value"); + return -1; + } + + int count = Runtime.PyTuple_Size(idx); + + Array args = Array.CreateInstance(typeof(Int32), count); + + for (int i = 0; i < count; i++) { + IntPtr op = Runtime.PyTuple_GetItem(idx, i); + index = (int)Runtime.PyInt_AsLong(op); + + if (Exceptions.ErrorOccurred()) { + Exceptions.RaiseTypeError("invalid index value"); + return -1; + } + + if (index < 0) { + index = items.GetLength(i) + index; + } + + args.SetValue(index, i); + } + + try { + items.SetValue(value, (int[])args); + } + catch (IndexOutOfRangeException) { + Exceptions.SetError(Exceptions.IndexError, + "array index out of range" + ); + return -1; + } + + return 0; + } + + + //==================================================================== + // Implements __contains__ for array types. + //==================================================================== + + public static int sq_contains(IntPtr ob, IntPtr v) { + CLRObject obj = (CLRObject)ManagedType.GetManagedObject(ob); + Type itemType = obj.inst.GetType().GetElementType(); + IList items = obj.inst as IList; + object value; + + if (!Converter.ToManaged(v, itemType, out value, false)) { + return 0; + } + + if (items.Contains(value)) { + return 1; + } + + return 0; + } + + + //==================================================================== + // Implements __len__ for array types. + //==================================================================== + + public static int mp_length(IntPtr ob) { + CLRObject self = (CLRObject)ManagedType.GetManagedObject(ob); + Array items = self.inst as Array; + return items.Length; + } + + + } + +} diff --git a/Pythonnet.Runtime/assemblyinfo.cs b/Pythonnet.Runtime/assemblyinfo.cs new file mode 100644 index 0000000000..2e15409e73 --- /dev/null +++ b/Pythonnet.Runtime/assemblyinfo.cs @@ -0,0 +1,48 @@ +// ========================================================================== +// 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.Reflection; +using System.Security.Permissions; +using System.Runtime.InteropServices; +using System.Resources; + +[assembly: System.Reflection.AssemblyProduct("Python for .NET")] +[assembly: System.Reflection.AssemblyVersion("2.0.0.2")] +[assembly: AssemblyDefaultAliasAttribute("Python.Runtime.dll")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] + + +[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, + Name = "FullTrust")] +[assembly: AssemblyCopyrightAttribute("Zope Public License, Version 2.0 (ZPL)")] +[assembly: AssemblyFileVersionAttribute("2.0.0.2")] +[assembly: NeutralResourcesLanguageAttribute("en")] + +#if (PYTHON23) +[assembly: AssemblyTitleAttribute("Python.Runtime for Python 2.3")] +[assembly: AssemblyDescriptionAttribute("Python Runtime for Python 2.3")] +#endif +#if (PYTHON24) +[assembly: AssemblyTitleAttribute("Python.Runtime for Python 2.4")] +[assembly: AssemblyDescriptionAttribute("Python Runtime for Python 2.4")] +#endif +#if (PYTHON25) +[assembly: AssemblyTitleAttribute("Python.Runtime for Python 2.5")] +[assembly: AssemblyDescriptionAttribute("Python Runtime for Python 2.5")] +#endif +#if (PYTHON26) +[assembly: AssemblyTitleAttribute("Python.Runtime for Python 2.6")] +[assembly: AssemblyDescriptionAttribute("Python Runtime for Python 2.6")] +#endif +#if (PYTHON27) +[assembly: AssemblyTitle("Python.Runtime for Python 2.7")] +[assembly: AssemblyDescription("Python Runtime for Python 2.7")] +#endif diff --git a/Pythonnet.Runtime/assemblymanager.cs b/Pythonnet.Runtime/assemblymanager.cs new file mode 100644 index 0000000000..e723ca6590 --- /dev/null +++ b/Pythonnet.Runtime/assemblymanager.cs @@ -0,0 +1,376 @@ +// ========================================================================== +// 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.IO; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; + +namespace Python.Runtime { + + /// + /// The AssemblyManager maintains information about loaded assemblies + /// namespaces and provides an interface for name-based type lookup. + /// + + internal class AssemblyManager { + + static Dictionary> namespaces; + //static Dictionary> generics; + static AssemblyLoadEventHandler lhandler; + static ResolveEventHandler rhandler; + static Dictionary probed; + static List assemblies; + internal static List pypath; + + private AssemblyManager() {} + + //=================================================================== + // Initialization performed on startup of the Python runtime. Here we + // scan all of the currently loaded assemblies to determine exported + // names, and register to be notified of new assembly loads. + //=================================================================== + + internal static void Initialize() { + namespaces = new + Dictionary>(32); + probed = new Dictionary(32); + //generics = new Dictionary>(); + assemblies = new List(16); + pypath = new List(16); + + AppDomain domain = AppDomain.CurrentDomain; + + lhandler = new AssemblyLoadEventHandler(AssemblyLoadHandler); + domain.AssemblyLoad += lhandler; + + rhandler = new ResolveEventHandler(ResolveHandler); + domain.AssemblyResolve += rhandler; + + Assembly[] items = domain.GetAssemblies(); + for (int i = 0; i < items.Length; i++) { + Assembly a = items[i]; + assemblies.Add(a); + ScanAssembly(a); + } + } + + + //=================================================================== + // Cleanup resources upon shutdown of the Python runtime. + //=================================================================== + + internal static void Shutdown() { + AppDomain domain = AppDomain.CurrentDomain; + domain.AssemblyLoad -= lhandler; + domain.AssemblyResolve -= rhandler; + } + + + //=================================================================== + // Event handler for assembly load events. At the time the Python + // runtime loads, we scan the app domain to map the assemblies that + // are loaded at the time. We also have to register this event handler + // so that we can know about assemblies that get loaded after the + // Python runtime is initialized. + //=================================================================== + + static void AssemblyLoadHandler(Object ob, AssemblyLoadEventArgs args){ + Assembly assembly = args.LoadedAssembly; + assemblies.Add(assembly); + ScanAssembly(assembly); + } + + + //=================================================================== + // Event handler for assembly resolve events. This is needed because + // we augment the assembly search path with the PYTHONPATH when we + // load an assembly from Python. Because of that, we need to listen + // for failed loads, because they might be dependencies of something + // we loaded from Python which also needs to be found on PYTHONPATH. + //=================================================================== + + static Assembly ResolveHandler(Object ob, ResolveEventArgs args){ + string name = args.Name.ToLower(); + for (int i = 0; i < assemblies.Count; i++) { + Assembly a = (Assembly)assemblies[i]; + string full = a.FullName.ToLower(); + if (full.StartsWith(name)) { + return a; + } + } + return LoadAssemblyPath(args.Name); + } + + + //=================================================================== + // We __really__ want to avoid using Python objects or APIs when + // probing for assemblies to load, since our ResolveHandler may be + // called in contexts where we don't have the Python GIL and can't + // even safely try to get it without risking a deadlock ;( + // + // To work around that, we update a managed copy of sys.path (which + // is the main thing we care about) when UpdatePath is called. The + // import hook calls this whenever it knows its about to use the + // assembly manager, which lets us keep up with changes to sys.path + // in a relatively lightweight and low-overhead way. + //=================================================================== + + internal static void UpdatePath() { + IntPtr list = Runtime.PySys_GetObject("path"); + int count = Runtime.PyList_Size(list); + if (count != pypath.Count) { + pypath.Clear(); + probed.Clear(); + for (int i = 0; i < count; i++) { + IntPtr item = Runtime.PyList_GetItem(list, i); + string path = Runtime.GetManagedString(item); + if (path != null) { + pypath.Add(path); + } + } + } + } + + + //=================================================================== + // Given an assembly name, try to find this assembly file using the + // PYTHONPATH. If not found, return null to indicate implicit load + // using standard load semantics (app base directory then GAC, etc.) + //=================================================================== + + public static string FindAssembly(string name) { + char sep = Path.DirectorySeparatorChar; + string path; + string temp; + + for (int i = 0; i < pypath.Count; i++) { + string head = pypath[i]; + if (head == null || head.Length == 0) { + path = name; + } + else { + path = head + sep + name; + } + + temp = path + ".dll"; + if (File.Exists(temp)) { + return temp; + } + temp = path + ".exe"; + if (File.Exists(temp)) { + return temp; + } + } + return null; + } + + + //=================================================================== + // Loads an assembly from the application directory or the GAC + // given a simple assembly name. Returns the assembly if loaded. + //=================================================================== + + public static Assembly LoadAssembly(string name) { + Assembly assembly = null; + try { + assembly = Assembly.Load(name); + } + catch { } + return assembly; + } + + + //=================================================================== + // Loads an assembly using an augmented search path (the python path). + //=================================================================== + + public static Assembly LoadAssemblyPath(string name) { + string path = FindAssembly(name); + Assembly assembly = null; + if (path != null) { + try { assembly = Assembly.LoadFrom(path); } + catch {} + } + return assembly; + } + + + //=================================================================== + // Given a qualified name of the form A.B.C.D, attempt to load + // an assembly named after each of A.B.C.D, A.B.C, A.B, A. This + // will only actually probe for the assembly once for each unique + // namespace. Returns true if any assemblies were loaded. + // TODO item 3 "* Deprecate implicit loading of assemblies": + // Set the fromFile flag if the name of the loaded assembly matches + // the fully qualified name that was requested if the framework + // actually loads an assembly. + // Call ONLY for namespaces that HAVE NOT been cached yet. + //=================================================================== + + public static bool LoadImplicit(string name, out bool fromFile) { + // 2010-08-16: Deprecation support + // Added out param to detect fully qualified name load + fromFile = false; + string[] names = name.Split('.'); + bool loaded = false; + string s = ""; + for (int i = 0; i < names.Length; i++) { + s = (i == 0) ? names[0] : s + "." + names[i]; + if (!probed.ContainsKey(s)) { + if (LoadAssemblyPath(s) != null) { + loaded = true; + /* 2010-08-16: Deprecation support */ + if (s == name) { + fromFile = true; + } + } + else if (LoadAssembly(s) != null) { + loaded = true; + /* 2010-08-16: Deprecation support */ + if (s == name) { + fromFile = true; + } + } + probed[s] = 1; + // 2010-12-24: Deprecation logic + /* if (loaded && (s == name)) { + fromFile = true; + break; + } */ + } + } + return loaded; + } + + + //=================================================================== + // Scans an assembly for exported namespaces, adding them to the + // mapping of valid namespaces. Note that for a given namespace + // a.b.c.d, each of a, a.b, a.b.c and a.b.c.d are considered to + // be valid namespaces (to better match Python import semantics). + //=================================================================== + + static void ScanAssembly(Assembly assembly) { + + // A couple of things we want to do here: first, we want to + // gather a list of all of the namespaces contributed to by + // the assembly. + + Type[] types = assembly.GetTypes(); + for (int i = 0; i < types.Length; i++) { + Type t = types[i]; + string ns = t.Namespace; + if ((ns != null) && (!namespaces.ContainsKey(ns))) { + string[] names = ns.Split('.'); + string s = ""; + for (int n = 0; n < names.Length; n++) { + s = (n == 0) ? names[0] : s + "." + names[n]; + if (!namespaces.ContainsKey(s)) { + namespaces.Add(s, + new Dictionary() + ); + } + } + } + + if (ns != null && !namespaces[ns].ContainsKey(assembly)) { + namespaces[ns].Add(assembly, String.Empty); + } + + if (t.IsGenericTypeDefinition) { + GenericUtil.Register(t); + } + } + } + + public static AssemblyName[] ListAssemblies() + { + AssemblyName[] names = new AssemblyName[assemblies.Count]; + Assembly assembly; + for (int i=0; i < assemblies.Count; i++) + { + assembly = assemblies[i]; + names.SetValue(assembly.GetName(), i); + } + return names; + } + + //=================================================================== + // Returns true if the given qualified name matches a namespace + // exported by an assembly loaded in the current app domain. + //=================================================================== + + public static bool IsValidNamespace(string name) { + return namespaces.ContainsKey(name); + } + + + //=================================================================== + // Returns the current list of valid names for the input namespace. + //=================================================================== + + public static List GetNames(string nsname) { + //Dictionary seen = new Dictionary(); + List names = new List(8); + + List g = GenericUtil.GetGenericBaseNames(nsname); + if (g != null) { + foreach (string n in g) { + names.Add(n); + } + } + + if (namespaces.ContainsKey(nsname)) { + foreach (Assembly a in namespaces[nsname].Keys) { + Type[] types = a.GetTypes(); + for (int i = 0; i < types.Length; i++) { + Type t = types[i]; + if (t.Namespace == nsname) { + names.Add(t.Name); + } + } + } + int nslen = nsname.Length; + foreach (string key in namespaces.Keys) { + if (key.Length > nslen && key.StartsWith(nsname)) { + //string tail = key.Substring(nslen); + if (key.IndexOf('.') == -1) { + names.Add(key); + } + } + } + } + return names; + } + + //=================================================================== + // Returns the System.Type object for a given qualified name, + // looking in the currently loaded assemblies for the named + // type. Returns null if the named type cannot be found. + //=================================================================== + + public static Type LookupType(string qname) { + for (int i = 0; i < assemblies.Count; i++) { + Assembly assembly = (Assembly)assemblies[i]; + Type type = assembly.GetType(qname); + if (type != null) { + return type; + } + } + return null; + } + + } + + +} diff --git a/Pythonnet.Runtime/buildclrmodule.bat b/Pythonnet.Runtime/buildclrmodule.bat new file mode 100644 index 0000000000..a7a7c7a8fb --- /dev/null +++ b/Pythonnet.Runtime/buildclrmodule.bat @@ -0,0 +1,36 @@ +:: Call with buildclrmodule.bat + +@echo off + +set TARGET_PLATFORM=%1 +set INPUT_DIRECTORY=%~2 +set INPUT_PATH="%INPUT_DIRECTORY%\clrmodule.il" +set OUTPUT_PATH=%3 + +if %TARGET_PLATFORM%==AnyCPU goto SETUP32 +if %TARGET_PLATFORM%==x64 goto SETUP64 +goto ERROR_BAD_PLATFORM + +:SETUP32 +set INCLUDE_PATH="%INPUT_DIRECTORY%\x86" +goto BUILD_CLR_MODULE + +:SETUP64 +set INCLUDE_PATH="%INPUT_DIRECTORY%\x64" +set ILASM_EXTRA_ARGS=/pe64 /x64 +goto BUILD_CLR_MODULE + +:ERROR_BAD_PLATFORM +echo Unknown target platform: %TARGET_PLATFORM% +exit /b 1 + +:ERROR_MISSING_INPUT +echo Can't find input file: %INPUT_PATH% +exit /b 1 + +:BUILD_CLR_MODULE +if not exist %INPUT_PATH% goto ERROR_MISSING_INPUT +%windir%\Microsoft.NET\Framework\v4.0.30319\ilasm /nologo /quiet /dll %ILASM_EXTRA_ARGS% /include=%INCLUDE_PATH% /output=%OUTPUT_PATH% %INPUT_PATH% + +::: 2.0 +:::%windir%\Microsoft.NET\Framework\v2.0.50727\ilasm /nologo /quiet /dll %ILASM_EXTRA_ARGS% /include=%INCLUDE_PATH% /output=%OUTPUT_PATH% %INPUT_PATH% diff --git a/Pythonnet.Runtime/classbase.cs b/Pythonnet.Runtime/classbase.cs new file mode 100644 index 0000000000..1541b12cd2 --- /dev/null +++ b/Pythonnet.Runtime/classbase.cs @@ -0,0 +1,170 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; +using System.Security; +using System.Runtime.InteropServices; + +namespace Python.Runtime { + + /// + /// Base class for Python types that reflect managed types / classes. + /// Concrete subclasses include ClassObject and DelegateObject. This + /// class provides common attributes and common machinery for doing + /// class initialization (initialization of the class __dict__). The + /// concrete subclasses provide slot implementations appropriate for + /// each variety of reflected type. + /// + + internal class ClassBase : ManagedType { + + internal Indexer indexer; + internal Type type; + + internal ClassBase(Type tp) : base() { + indexer = null; + type = tp; + } + + internal virtual bool CanSubclass() { + return (!this.type.IsEnum); + } + + //==================================================================== + // Implements __init__ for reflected classes and value types. + //==================================================================== + + public static int tp_init(IntPtr ob, IntPtr args, IntPtr kw) { + return 0; + } + + //==================================================================== + // Default implementation of [] semantics for reflected types. + //==================================================================== + + public virtual IntPtr type_subscript(IntPtr idx) { + return Exceptions.RaiseTypeError("unsubscriptable object"); + } + + //==================================================================== + // Standard comparison implementation for instances of reflected types. + //==================================================================== + + public static int tp_compare(IntPtr ob, IntPtr other) { + if (ob == other) { + return 0; + } + + CLRObject co1 = GetManagedObject(ob) as CLRObject; + CLRObject co2 = GetManagedObject(other) as CLRObject; + Object o1 = co1.inst; + Object o2 = co2.inst; + + if (Object.Equals(o1, o2)) { + return 0; + } + return -1; + } + + + //==================================================================== + // Standard iteration support for instances of reflected types. This + // allows natural iteration over objects that either are IEnumerable + // or themselves support IEnumerator directly. + //==================================================================== + + public static IntPtr tp_iter(IntPtr ob) { + CLRObject co = GetManagedObject(ob) as CLRObject; + if (co == null) { + return Exceptions.RaiseTypeError("invalid object"); + } + + IEnumerable e = co.inst as IEnumerable; + IEnumerator o; + + if (e != null) { + o = e.GetEnumerator(); + } + else { + o = co.inst as IEnumerator; + + if (o == null) { + string message = "iteration over non-sequence"; + return Exceptions.RaiseTypeError(message); + } + } + + return new Iterator(o).pyHandle; + } + + + //==================================================================== + // Standard __hash__ implementation for instances of reflected types. + //==================================================================== + + public static IntPtr tp_hash(IntPtr ob) { + CLRObject co = GetManagedObject(ob) as CLRObject; + if (co == null) { + return Exceptions.RaiseTypeError("unhashable type"); + } + return new IntPtr(co.inst.GetHashCode()); + } + + + //==================================================================== + // Standard __str__ implementation for instances of reflected types. + //==================================================================== + + public static IntPtr tp_str(IntPtr ob) { + CLRObject co = GetManagedObject(ob) as CLRObject; + if (co == null) { + return Exceptions.RaiseTypeError("invalid object"); + } + return Runtime.PyString_FromString(co.inst.ToString()); + } + + + //==================================================================== + // Default implementations for required Python GC support. + //==================================================================== + + public static int tp_traverse(IntPtr ob, IntPtr func, IntPtr args) { + return 0; + } + + public static int tp_clear(IntPtr ob) { + return 0; + } + + public static int tp_is_gc(IntPtr type) { + return 1; + } + + //==================================================================== + // Standard dealloc implementation for instances of reflected types. + //==================================================================== + + public static void tp_dealloc(IntPtr ob) { + ManagedType self = GetManagedObject(ob); + IntPtr dict = Marshal.ReadIntPtr(ob, ObjectOffset.ob_dict); + if (dict != IntPtr.Zero) { + Runtime.Decref(dict); + } + Runtime.PyObject_GC_UnTrack(self.pyHandle); + Runtime.PyObject_GC_Del(self.pyHandle); + Runtime.Decref(self.tpHandle); + self.gcHandle.Free(); + } + + + } + +} diff --git a/Pythonnet.Runtime/classmanager.cs b/Pythonnet.Runtime/classmanager.cs new file mode 100644 index 0000000000..088905b380 --- /dev/null +++ b/Pythonnet.Runtime/classmanager.cs @@ -0,0 +1,368 @@ +// ========================================================================== +// 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.Runtime.InteropServices; +using System.Collections.Generic; +using System.Collections; +using System.Reflection; +using System.Security; + +namespace Python.Runtime { + + /// + /// The ClassManager is responsible for creating and managing instances + /// that implement the Python type objects that reflect managed classes. + /// + /// Each managed type reflected to Python is represented by an instance + /// of a concrete subclass of ClassBase. Each instance is associated with + /// a generated Python type object, whose slots point to static methods + /// of the managed instance's class. + /// + + internal class ClassManager { + + static Dictionary cache; + static Type dtype; + + private ClassManager() {} + + static ClassManager() { + cache = new Dictionary(128); + // SEE: http://msdn.microsoft.com/en-us/library/96b1ayy4%28VS.90%29.aspx + // ""All delegates inherit from MulticastDelegate, which inherits from Delegate."" + // Was Delegate, which caused a null MethodInfo returned from GetMethode("Invoke") + // and crashed on Linux under Mono. + dtype = typeof(System.MulticastDelegate); + } + + //==================================================================== + // Return the ClassBase-derived instance that implements a particular + // reflected managed type, creating it if it doesn't yet exist. + //==================================================================== + + internal static ClassBase GetClass(Type type) { + ClassBase cb = null; + cache.TryGetValue(type, out cb); + if (cb != null) { + return cb; + } + cb = CreateClass(type); + cache.Add(type, cb); + return cb; + } + + + //==================================================================== + // Create a new ClassBase-derived instance that implements a reflected + // managed type. The new object will be associated with a generated + // Python type object. + //==================================================================== + + private static ClassBase CreateClass(Type type) { + + // First, we introspect the managed type and build some class + // information, including generating the member descriptors + // that we'll be putting in the Python class __dict__. + + ClassInfo info = GetClassInfo(type); + + // Next, select the appropriate managed implementation class. + // Different kinds of types, such as array types or interface + // types, want to vary certain implementation details to make + // sure that the type semantics are consistent in Python. + + ClassBase impl; + + // Check to see if the given type extends System.Exception. This + // lets us check once (vs. on every lookup) in case we need to + // wrap Exception-derived types in old-style classes + + if (type.ContainsGenericParameters) { + impl = new GenericType(type); + } + + else if (type.IsSubclassOf(dtype)) { + impl = new DelegateObject(type); + } + + else if (type.IsArray) { + impl = new ArrayObject(type); + } + + else if (type.IsInterface) { + impl = new InterfaceObject(type); + } + + else if (type == typeof(Exception) || + type.IsSubclassOf(typeof(Exception))) { + impl = new ExceptionClassObject(type); + } + + else { + impl = new ClassObject(type); + } + + impl.indexer = info.indexer; + + // Now we allocate the Python type object to reflect the given + // managed type, filling the Python type slots with thunks that + // point to the managed methods providing the implementation. + + + IntPtr tp = TypeManager.GetTypeHandle(impl, type); + impl.tpHandle = tp; + + // Finally, initialize the class __dict__ and return the object. + IntPtr dict = Marshal.ReadIntPtr(tp, TypeOffset.tp_dict); + + + IDictionaryEnumerator iter = info.members.GetEnumerator(); + while(iter.MoveNext()) { + ManagedType item = (ManagedType)iter.Value; + string name = (string)iter.Key; + Runtime.PyDict_SetItemString(dict, name, item.pyHandle); + } + + // If class has constructors, generate an __doc__ attribute. + + IntPtr doc; + Type marker = typeof(DocStringAttribute); + Attribute[] attrs = (Attribute[])type.GetCustomAttributes(marker, false); + if (attrs.Length == 0) { + doc = IntPtr.Zero; + } + else { + DocStringAttribute attr = (DocStringAttribute)attrs[0]; + string docStr = attr.DocString; + doc = Runtime.PyString_FromString(docStr); + Runtime.PyDict_SetItemString(dict, "__doc__", doc); + Runtime.Decref(doc); + } + + ClassObject co = impl as ClassObject; + // If this is a ClassObject AND it has constructors, generate a __doc__ attribute. + // required that the ClassObject.ctors be changed to internal + if (co != null) { + if (co.ctors.Length > 0) { + // Implement Overloads on the class object + ConstructorBinding ctors = new ConstructorBinding(type, tp, co.binder); + // ExtensionType types are untracked, so don't Incref() them. + // XXX deprecate __overloads__ soon... + Runtime.PyDict_SetItemString(dict, "__overloads__", ctors.pyHandle); + Runtime.PyDict_SetItemString(dict, "Overloads", ctors.pyHandle); + + if (doc == IntPtr.Zero) { + doc = co.GetDocString(); + Runtime.PyDict_SetItemString(dict, "__doc__", doc); + Runtime.Decref(doc); + } + } + } + + return impl; + } + + + + + private static ClassInfo GetClassInfo(Type type) { + ClassInfo ci = new ClassInfo(type); + Hashtable methods = new Hashtable(); + ArrayList list; + MethodInfo meth; + ManagedType ob; + String name; + Object item; + Type tp; + int i, n; + + // This is complicated because inheritance in Python is name + // based. We can't just find DeclaredOnly members, because we + // could have a base class A that defines two overloads of a + // method and a class B that defines two more. The name-based + // descriptor Python will find needs to know about inherited + // overloads as well as those declared on the sub class. + + BindingFlags flags = BindingFlags.Static | + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic; + + MemberInfo[] info = type.GetMembers(flags); + Hashtable local = new Hashtable(); + ArrayList items = new ArrayList(); + MemberInfo m; + + // Loop through once to find out which names are declared + for (i = 0; i < info.Length; i++) { + m = info[i]; + if (m.DeclaringType == type) { + local[m.Name] = 1; + } + } + + // Now again to filter w/o losing overloaded member info + for (i = 0; i < info.Length; i++) { + m = info[i]; + if (local[m.Name] != null) { + items.Add(m); + } + } + + if (type.IsInterface) { + // Interface inheritance seems to be a different animal: + // more contractual, less structural. Thus, a Type that + // represents an interface that inherits from another + // interface does not return the inherited interface's + // methods in GetMembers. For example ICollection inherits + // from IEnumerable, but ICollection's GetMemebers does not + // return GetEnumerator. + // + // Not sure if this is the correct way to fix this, but it + // seems to work. Thanks to Bruce Dodson for the fix. + + Type[] inheritedInterfaces = type.GetInterfaces(); + + for (i = 0; i < inheritedInterfaces.Length; ++i) { + Type inheritedType = inheritedInterfaces[i]; + MemberInfo[] imembers = inheritedType.GetMembers(flags); + for (n = 0; n < imembers.Length; n++) { + m = imembers[n]; + if (local[m.Name] == null) { + items.Add(m); + } + } + } + } + + for (i = 0; i < items.Count; i++) { + + MemberInfo mi = (MemberInfo)items[i]; + + switch(mi.MemberType) { + + case MemberTypes.Method: + meth = (MethodInfo) mi; + if (!(meth.IsPublic || meth.IsFamily || + meth.IsFamilyOrAssembly)) + continue; + name = meth.Name; + item = methods[name]; + if (item == null) { + item = methods[name] = new ArrayList(); + } + list = (ArrayList) item; + list.Add(meth); + continue; + + case MemberTypes.Property: + PropertyInfo pi = (PropertyInfo) mi; + + MethodInfo mm = null; + try { + mm = pi.GetGetMethod(true); + if (mm == null) { + mm = pi.GetSetMethod(true); + } + } + catch (SecurityException) { + // GetGetMethod may try to get a method protected by + // StrongNameIdentityPermission - effectively private. + continue; + } + + if (mm == null) { + continue; + } + + if (!(mm.IsPublic || mm.IsFamily || mm.IsFamilyOrAssembly)) + continue; + + // Check for indexer + ParameterInfo[] args = pi.GetIndexParameters(); + if (args.GetLength(0) > 0) { + Indexer idx = ci.indexer; + if (idx == null) { + ci.indexer = new Indexer(); + idx = ci.indexer; + } + idx.AddProperty(pi); + continue; + } + + ob = new PropertyObject(pi); + ci.members[pi.Name] = ob; + continue; + + case MemberTypes.Field: + FieldInfo fi = (FieldInfo) mi; + if (!(fi.IsPublic || fi.IsFamily || fi.IsFamilyOrAssembly)) + continue; + ob = new FieldObject(fi); + ci.members[mi.Name] = ob; + continue; + + case MemberTypes.Event: + EventInfo ei = (EventInfo)mi; + MethodInfo me = ei.GetAddMethod(true); + if (!(me.IsPublic || me.IsFamily || me.IsFamilyOrAssembly)) + continue; + ob = new EventObject(ei); + ci.members[ei.Name] = ob; + continue; + + case MemberTypes.NestedType: + tp = (Type) mi; + if (!(tp.IsNestedPublic || tp.IsNestedFamily || + tp.IsNestedFamORAssem)) + continue; + ob = ClassManager.GetClass(tp); + ci.members[mi.Name] = ob; + continue; + + } + } + + IDictionaryEnumerator iter = methods.GetEnumerator(); + + while(iter.MoveNext()) { + name = (string) iter.Key; + list = (ArrayList) iter.Value; + + MethodInfo[] mlist = (MethodInfo[])list.ToArray( + typeof(MethodInfo) + ); + + ob = new MethodObject(name, mlist); + ci.members[name] = ob; + } + + return ci; + + } + + + } + + + internal class ClassInfo { + + internal ClassInfo(Type t) { + members = new Hashtable(); + indexer = null; + } + + public Hashtable members; + public Indexer indexer; + } + + + +} diff --git a/Pythonnet.Runtime/classobject.cs b/Pythonnet.Runtime/classobject.cs new file mode 100644 index 0000000000..c331637be0 --- /dev/null +++ b/Pythonnet.Runtime/classobject.cs @@ -0,0 +1,299 @@ +// ========================================================================== +// 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.Reflection; + +namespace Python.Runtime { + + /// + /// Managed class that provides the implementation for reflected types. + /// Managed classes and value types are represented in Python by actual + /// Python type objects. Each of those type objects is associated with + /// an instance of ClassObject, which provides its implementation. + /// + + internal class ClassObject : ClassBase { + + internal ConstructorBinder binder; + internal ConstructorInfo[] ctors; + + internal ClassObject(Type tp) : base(tp) { + ctors = type.GetConstructors(); + binder = new ConstructorBinder(); + + for (int i = 0; i < ctors.Length; i++) { + binder.AddMethod(ctors[i]); + } + } + + + //==================================================================== + // Helper to get docstring from reflected constructor info. + //==================================================================== + + internal IntPtr GetDocString() { + MethodBase[] methods = binder.GetMethods(); + string str = ""; + for (int i = 0; i < methods.Length; i++) { + if (str.Length > 0) + str += Environment.NewLine; + str += methods[i].ToString(); + } + return Runtime.PyString_FromString(str); + } + + + //==================================================================== + // Implements __new__ for reflected classes and value types. + //==================================================================== + + public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { + + ClassObject self = GetManagedObject(tp) as ClassObject; + + // Sanity check: this ensures a graceful error if someone does + // something intentially wrong like use the managed metatype for + // a class that is not really derived from a managed class. + + if (self == null) { + return Exceptions.RaiseTypeError("invalid object"); + } + + Type type = self.type; + + // Primitive types do not have constructors, but they look like + // they do from Python. If the ClassObject represents one of the + // convertible primitive types, just convert the arg directly. + + if (type.IsPrimitive || type == typeof(String)) { + if (Runtime.PyTuple_Size(args) != 1) { + Exceptions.SetError(Exceptions.TypeError, + "no constructors match given arguments" + ); + return IntPtr.Zero; + } + + IntPtr op = Runtime.PyTuple_GetItem(args, 0); + Object result; + + if (!Converter.ToManaged(op, type, out result, true)) { + return IntPtr.Zero; + } + + return CLRObject.GetInstHandle(result, tp); + } + + if (type.IsAbstract) { + Exceptions.SetError(Exceptions.TypeError, + "cannot instantiate abstract class" + ); + return IntPtr.Zero; + } + + if (type.IsEnum) { + Exceptions.SetError(Exceptions.TypeError, + "cannot instantiate enumeration" + ); + return IntPtr.Zero; + } + + Object obj = self.binder.InvokeRaw(IntPtr.Zero, args, kw); + if (obj == null) { + return IntPtr.Zero; + } + + return CLRObject.GetInstHandle(obj, tp); + } + + + //==================================================================== + // Implementation of [] semantics for reflected types. This exists + // both to implement the Array[int] syntax for creating arrays and + // to support generic name overload resolution using []. + //==================================================================== + + public override IntPtr type_subscript(IntPtr idx) { + + // If this type is the Array type, the [] means we need to + // construct and return an array type of the given element type. + + if ((this.type) == typeof(Array)) { + if (Runtime.PyTuple_Check(idx)) { + return Exceptions.RaiseTypeError("type expected"); + } + ClassBase c = GetManagedObject(idx) as ClassBase; + Type t = (c != null) ? c.type : Converter.GetTypeByAlias(idx); + if (t == null) { + return Exceptions.RaiseTypeError("type expected"); + } + Type a = t.MakeArrayType(); + ClassBase o = ClassManager.GetClass(a); + Runtime.Incref(o.pyHandle); + return o.pyHandle; + } + + // If there are generics in our namespace with the same base name + // as the current type, then [] means the caller wants to + // bind the generic type matching the given type parameters. + + Type[] types = Runtime.PythonArgsToTypeArray(idx); + if (types == null) { + return Exceptions.RaiseTypeError("type(s) expected"); + } + + string gname = this.type.FullName + "`" + types.Length.ToString(); + Type gtype = AssemblyManager.LookupType(gname); + if (gtype != null) { + GenericType g = ClassManager.GetClass(gtype) as GenericType; + return g.type_subscript(idx); + /*Runtime.Incref(g.pyHandle); + return g.pyHandle;*/ + } + return Exceptions.RaiseTypeError("unsubscriptable object"); + } + + + //==================================================================== + // Implements __getitem__ for reflected classes and value types. + //==================================================================== + + public static IntPtr mp_subscript(IntPtr ob, IntPtr idx) { + //ManagedType self = GetManagedObject(ob); + IntPtr tp = Runtime.PyObject_TYPE(ob); + ClassBase cls = (ClassBase)GetManagedObject(tp); + + if (cls.indexer == null || !cls.indexer.CanGet) { + Exceptions.SetError(Exceptions.TypeError, + "unindexable object" + ); + return IntPtr.Zero; + } + + // Arg may be a tuple in the case of an indexer with multiple + // parameters. If so, use it directly, else make a new tuple + // with the index arg (method binders expect arg tuples). + + IntPtr args = idx; + bool free = false; + + if (!Runtime.PyTuple_Check(idx)) { + args = Runtime.PyTuple_New(1); + Runtime.Incref(idx); + Runtime.PyTuple_SetItem(args, 0, idx); + free = true; + } + + IntPtr value = IntPtr.Zero; + + try { + value = cls.indexer.GetItem(ob, args); + } + finally { + if (free) { + Runtime.Decref(args); + } + } + return value; + } + + + //==================================================================== + // Implements __setitem__ for reflected classes and value types. + //==================================================================== + + public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) { + //ManagedType self = GetManagedObject(ob); + IntPtr tp = Runtime.PyObject_TYPE(ob); + ClassBase cls = (ClassBase)GetManagedObject(tp); + + if (cls.indexer == null || !cls.indexer.CanSet) { + Exceptions.SetError(Exceptions.TypeError, + "object doesn't support item assignment" + ); + return -1; + } + + // Arg may be a tuple in the case of an indexer with multiple + // parameters. If so, use it directly, else make a new tuple + // with the index arg (method binders expect arg tuples). + + IntPtr args = idx; + bool free = false; + + if (!Runtime.PyTuple_Check(idx)) { + args = Runtime.PyTuple_New(1); + Runtime.Incref(idx); + Runtime.PyTuple_SetItem(args, 0, idx); + free = true; + } + + int i = Runtime.PyTuple_Size(args); + IntPtr real = Runtime.PyTuple_New(i + 1); + for (int n = 0; n < i; n++) { + IntPtr item = Runtime.PyTuple_GetItem(args, n); + Runtime.Incref(item); + Runtime.PyTuple_SetItem(real, n, item); + } + Runtime.Incref(v); + Runtime.PyTuple_SetItem(real, i, v); + + try { + cls.indexer.SetItem(ob, real); + } + finally { + Runtime.Decref(real); + + if (free) { + Runtime.Decref(args); + } + } + + if (Exceptions.ErrorOccurred()) { + return -1; + } + + return 0; + } + + + //==================================================================== + // This is a hack. Generally, no managed class is considered callable + // from Python - with the exception of System.Delegate. It is useful + // to be able to call a System.Delegate instance directly, especially + // when working with multicast delegates. + //==================================================================== + + public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { + //ManagedType self = GetManagedObject(ob); + IntPtr tp = Runtime.PyObject_TYPE(ob); + ClassBase cb = (ClassBase)GetManagedObject(tp); + + if (cb.type != typeof(System.Delegate)) { + Exceptions.SetError(Exceptions.TypeError, + "object is not callable"); + return IntPtr.Zero; + } + + CLRObject co = (CLRObject)ManagedType.GetManagedObject(ob); + Delegate d = co.inst as Delegate; + BindingFlags flags = BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.Static; + + MethodInfo method = d.GetType().GetMethod("Invoke", flags); + MethodBinder binder = new MethodBinder(method); + return binder.Invoke(ob, args, kw); + } + + + } + +} diff --git a/Pythonnet.Runtime/clrmodule.il b/Pythonnet.Runtime/clrmodule.il new file mode 100644 index 0000000000..be57f06549 --- /dev/null +++ b/Pythonnet.Runtime/clrmodule.il @@ -0,0 +1,278 @@ +// ========================================================================== +// 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. +// ========================================================================== + +//============================================================================ +// This file is a hand-maintained stub - it implements clr.dll, which can be +// loaded by a standard CPython interpreter as an extension module. When it +// is loaded, it bootstraps the managed runtime integration layer and defers +// to it to do initialization and put the clr module into sys.modules, etc. + +// The "USE_PYTHON_RUNTIME_*" defines control what extra evidence is used +// to help the CLR find the appropriate Python.Runtime assembly. + +// If defined, the "pythonRuntimeVersionString" variable must be set to +// Python.Runtime's current version. +#define USE_PYTHON_RUNTIME_VERSION + +// If defined, the "PythonRuntimePublicKeyTokenData" data array must be +// set to Python.Runtime's public key token. +//#define USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN + +// If DEBUG_PRINT is defined, a few System.Console.WriteLine calls are made +// to indicate what's going on during the load... +//#define DEBUG_PRINT +//============================================================================ + +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} + +.assembly clr +{ + .hash algorithm 0x00008004 + .ver 2:4:2:7 +} + +.module clr.dll +.imagebase 0x00400000 +.subsystem 0x00000003 +.file alignment 512 + +// This includes the platform-specific IL. The include search path +// is set depending on whether we're compiling 32 or 64 bit. +// This MUST come before any other .data directives! +// Why, oh why, can't ilasm support command line #defines? :( +#include "clrmodule-platform.il" + +#ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN +.data PythonRuntimePublicKeyTokenData = bytearray (64 e1 4e 84 5a bf 2e 60) +#endif + +.class public auto ansi beforefieldinit clrModule extends [mscorlib]System.Object +{ +#ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN + .field static assembly int64 PythonRuntimePublicKeyToken at PythonRuntimePublicKeyTokenData +#endif + + .method public hidebysig specialname rtspecialname instance void + .ctor() cil managed + { + .maxstack 1 + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } + + .method public hidebysig static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) + initclr() cil managed + { + .vtentry 1:1 + .export [1] as initclr + + .maxstack 6 + .locals init ( + class [mscorlib]System.Reflection.Assembly pythonRuntime, + class [mscorlib]System.Reflection.Assembly executingAssembly, + class [mscorlib]System.Reflection.AssemblyName pythonRuntimeName, + class [mscorlib]System.Type pythonEngineType, + int8[] publicKeyToken, + string assemblyDirectory, + string pythonRuntimeVersionString, + string pythonRuntimeDllPath) + + // pythonRuntime = null; + ldnull + stloc pythonRuntime + + .try + { +#ifdef DEBUG_PRINT + ldstr "Attempting to load Python.Runtime using standard binding rules... " + call void [mscorlib]System.Console::Write(string) +#endif + + // Attempt to find and load Python.Runtime using standard assembly binding rules. + // This roughly translates into looking in order: + // - GAC + // - ApplicationBase + // - A PrivateBinPath under ApplicationBase + // With an unsigned assembly, the GAC is skipped. + + // System.Reflection.AssemblyName pythonRuntimeName = new System.Reflection.AssemblyName(); + newobj instance void [mscorlib]System.Reflection.AssemblyName::.ctor() + stloc pythonRuntimeName + + // pythonRuntimeName.Name = "Python.Runtime"; + ldloc pythonRuntimeName + ldstr "Python.Runtime" + callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_Name(string) + +#ifdef USE_PYTHON_RUNTIME_VERSION + // pythonRuntimeVersionString = "..."; + ldstr "2.0.0.2" + stloc pythonRuntimeVersionString + + // pythonRuntimeName.Version = new Version(pythonRuntimeVersionString); + ldloc pythonRuntimeName + ldloc pythonRuntimeVersionString + newobj instance void [mscorlib]System.Version::.ctor(string) + callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_Version(class [mscorlib]System.Version) +#endif + +#ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN + // publicKeyToken = new byte[] { ... }; + ldc.i4.8 + newarr [mscorlib]System.Byte + dup + ldtoken field int64 clrModule::PythonRuntimePublicKeyToken + call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) + stloc publicKeyToken + + // pythonRuntimeName.SetPublicKeyToken(publicKeyToken); + ldloc pythonRuntimeName + ldloc publicKeyToken + callvirt instance void [mscorlib]System.Reflection.AssemblyName::SetPublicKeyToken(uint8[]) +#endif + + // pythonRuntimeName.CultureInfo = System.Globalization.CultureInfo.InvariantCulture; + ldloc pythonRuntimeName + call class [mscorlib]System.Globalization.CultureInfo [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture() + callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_CultureInfo(class [mscorlib]System.Globalization.CultureInfo) + + // return System.Reflection.Assembly.Load(pythonRuntimeName); + ldloc pythonRuntimeName + call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::Load(class [mscorlib]System.Reflection.AssemblyName) + stloc pythonRuntime + +#ifdef DEBUG_PRINT + ldstr "Success!" + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s LOADED_PYTHON_RUNTIME + } + catch [mscorlib]System.Object + { +#ifdef DEBUG_PRINT + ldstr "Failed." + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s EXIT_CLR_LOAD + } + EXIT_CLR_LOAD: nop + + .try + { + // If the above fails for any reason, we fallback to attempting to load "Python.Runtime.dll" + // from the directory this assembly is running in. "This assembly" is probably "clr.pyd", + // sitting somewhere in PYTHONPATH. This is using Assembly.LoadFrom, and inherits all the + // caveats of that call. See MSDN docs for details. + // Suzanne Cook's blog is also an excellent source of info on this: + // http://blogs.msdn.com/suzcook/ + // http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx + // http://blogs.msdn.com/suzcook/archive/2003/06/13/57180.aspx + // executingAssembly = System.Reflection.Assembly.GetExecutingAssembly(); + call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::GetExecutingAssembly() + stloc executingAssembly + + // assemblyDirectory = System.IO.Path.GetDirectoryName(executingAssembly.Location); + ldloc executingAssembly + callvirt instance string [mscorlib]System.Reflection.Assembly::get_Location() + call string [mscorlib]System.IO.Path::GetDirectoryName(string) + stloc assemblyDirectory + + // pythonRuntimeDllPath = System.IO.Path.Combine(assemblyDirectory, "Python.Runtime.dll"); + ldloc assemblyDirectory + ldstr "Python.Runtime.dll" + call string [mscorlib]System.IO.Path::Combine(string, string) + stloc pythonRuntimeDllPath + +#ifdef DEBUG_PRINT + ldstr "Attempting to load Python.Runtime from: '{0}'... " + ldloc pythonRuntimeDllPath + call void [mscorlib]System.Console::Write(string, object) +#endif + + // pythonRuntime = System.Reflection.Assembly.LoadFrom(pythonRuntimeDllPath); + ldloc pythonRuntimeDllPath + call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::LoadFrom(string) + stloc pythonRuntime + +#ifdef DEBUG_PRINT + ldstr "Success!" + call void [mscorlib]System.Console::WriteLine(string) + + ldloc pythonRuntime + callvirt instance string [mscorlib]System.Reflection.Assembly::get_CodeBase() + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s LOADED_PYTHON_RUNTIME + } + catch [mscorlib]System.Object + { +#ifdef DEBUG_PRINT + ldstr "Failed." + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s EXIT_PYTHONPATH_LOAD + } + EXIT_PYTHONPATH_LOAD: nop + + // If we get here, we haven't loaded Python.Runtime, so bail. +#ifdef DEBUG_PRINT + ldstr "Could not load Python.Runtime, so sad." + call void [mscorlib]System.Console::WriteLine(string) +#endif + ret; + + // Once here, we've successfully loaded SOME version of Python.Runtime + // So now we get the PythonEngine and execute the InitExt method on it. + LOADED_PYTHON_RUNTIME: nop + .try + { +#ifdef DEBUG_PRINT + ldstr "Running Python.Runtime.PythonEngine.InitExt()" + call void [mscorlib]System.Console::WriteLine(string) +#endif + // pythonEngineType = pythonRuntime.GetType("Python.Runtime.PythonEngine"); + ldloc pythonRuntime + ldstr "Python.Runtime.PythonEngine" + callvirt instance class [mscorlib]System.Type [mscorlib]System.Reflection.Assembly::GetType(string) + stloc pythonEngineType + + // pythonEngineType.InvokeMember("InitExt", System.Reflection.BindingFlags.InvokeMethod, null, null, null); + ldloc pythonEngineType + ldstr "InitExt" + ldc.i4 0x100 + ldnull + ldnull + ldnull + callvirt instance object [mscorlib]System.Type::InvokeMember( string, + valuetype [mscorlib]System.Reflection.BindingFlags, + class [mscorlib]System.Reflection.Binder, + object, + object[]) + pop + leave.s EXIT_TRY_INVOKE + } + catch [mscorlib]System.Object + { +#ifdef DEBUG_PRINT + ldstr "Error calling Python.Runtime.PythonEngine.InitExt()." + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s EXIT_TRY_INVOKE + } + EXIT_TRY_INVOKE: nop + + ret + } +} + diff --git a/Pythonnet.Runtime/clrmodule.pp.il b/Pythonnet.Runtime/clrmodule.pp.il new file mode 100644 index 0000000000..59b2893793 --- /dev/null +++ b/Pythonnet.Runtime/clrmodule.pp.il @@ -0,0 +1,279 @@ +// ========================================================================== +// 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. +// ========================================================================== + +//============================================================================ +// This file is a hand-maintained stub - it implements clr.dll, which can be +// loaded by a standard CPython interpreter as an extension module. When it +// is loaded, it bootstraps the managed runtime integration layer and defers +// to it to do initialization and put the clr module into sys.modules, etc. + +// The "USE_PYTHON_RUNTIME_*" defines control what extra evidence is used +// to help the CLR find the appropriate Python.Runtime assembly. + +// If defined, the "pythonRuntimeVersionString" variable must be set to +// Python.Runtime's current version. +#define USE_PYTHON_RUNTIME_VERSION + +// If defined, the "PythonRuntimePublicKeyTokenData" data array must be +// set to Python.Runtime's public key token. +//#define USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN + +// If DEBUG_PRINT is defined, a few System.Console.WriteLine calls are made +// to indicate what's going on during the load... +//#define DEBUG_PRINT +//============================================================================ + +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 2:0:0:0 +} + +.assembly clr +{ + .hash algorithm 0x00008004 + .ver 2:0:0:2 +} + +.module clr.dll +.imagebase 0x00400000 +.subsystem 0x00000003 +.file alignment 512 + +// This includes the platform-specific IL. The include search path +// is set depending on whether we're compiling 32 or 64 bit. +// This MUST come before any other .data directives! +// Why, oh why, can't ilasm support command line #defines? :( + +// Contributed by VIKAS DHIMAN - Handled by /home/barton/Projects/PyDotNet/pythonnet/makefile +// gcc -C -P -x c++ -I $(ARCH) clrmodule.pp.il -o clrmodule.il +// to copy the correct architecture to the clrModule. +// Nice formating, as well - Thanks, Vikas! +#include "clrmodule-platform.il" + +#ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN +.data PythonRuntimePublicKeyTokenData = bytearray (64 e1 4e 84 5a bf 2e 60) +#endif + +.class public auto ansi beforefieldinit clrModule extends [mscorlib]System.Object +{ +#ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN + .field static assembly int64 PythonRuntimePublicKeyToken at PythonRuntimePublicKeyTokenData +#endif + + .method public hidebysig specialname rtspecialname instance void + .ctor() cil managed + { + .maxstack 1 + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } + + .method public hidebysig static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) + initclr() cil managed + { + .vtentry 1:1 + .export [1] as initclr + + .maxstack 6 + .locals init ( + class [mscorlib]System.Reflection.Assembly pythonRuntime, + class [mscorlib]System.Reflection.Assembly executingAssembly, + class [mscorlib]System.Reflection.AssemblyName pythonRuntimeName, + class [mscorlib]System.Type pythonEngineType, + int8[] publicKeyToken, + string assemblyDirectory, + string pythonRuntimeVersionString, + string pythonRuntimeDllPath) + + // pythonRuntime = null; + ldnull + stloc pythonRuntime + + .try + { +#ifdef DEBUG_PRINT + ldstr "Attempting to load Python.Runtime using standard binding rules... " + call void [mscorlib]System.Console::Write(string) +#endif + + // Attempt to find and load Python.Runtime using standard assembly binding rules. + // This roughly translates into looking in order: + // - GAC + // - ApplicationBase + // - A PrivateBinPath under ApplicationBase + // With an unsigned assembly, the GAC is skipped. + + // System.Reflection.AssemblyName pythonRuntimeName = new System.Reflection.AssemblyName(); + newobj instance void [mscorlib]System.Reflection.AssemblyName::.ctor() + stloc pythonRuntimeName + + // pythonRuntimeName.Name = "Python.Runtime"; + ldloc pythonRuntimeName + ldstr "Python.Runtime" + callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_Name(string) + +#ifdef USE_PYTHON_RUNTIME_VERSION + // pythonRuntimeVersionString = "..."; + ldstr "2.0.0.2" + stloc pythonRuntimeVersionString + + // pythonRuntimeName.Version = new Version(pythonRuntimeVersionString); + ldloc pythonRuntimeName + ldloc pythonRuntimeVersionString + newobj instance void [mscorlib]System.Version::.ctor(string) + callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_Version(class [mscorlib]System.Version) +#endif + +#ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN + // publicKeyToken = new byte[] { ... }; + ldc.i4.8 + newarr [mscorlib]System.Byte + dup + ldtoken field int64 clrModule::PythonRuntimePublicKeyToken + call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) + stloc publicKeyToken + + // pythonRuntimeName.SetPublicKeyToken(publicKeyToken); + ldloc pythonRuntimeName + ldloc publicKeyToken + callvirt instance void [mscorlib]System.Reflection.AssemblyName::SetPublicKeyToken(uint8[]) +#endif + + // pythonRuntimeName.CultureInfo = System.Globalization.CultureInfo.InvariantCulture; + ldloc pythonRuntimeName + call class [mscorlib]System.Globalization.CultureInfo [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture() + callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_CultureInfo(class [mscorlib]System.Globalization.CultureInfo) + + // return System.Reflection.Assembly.Load(pythonRuntimeName); + ldloc pythonRuntimeName + call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::Load(class [mscorlib]System.Reflection.AssemblyName) + stloc pythonRuntime + +#ifdef DEBUG_PRINT + ldstr "Success!" + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s LOADED_PYTHON_RUNTIME + } + catch [mscorlib]System.Object + { +#ifdef DEBUG_PRINT + ldstr "Failed." + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s EXIT_CLR_LOAD + } + EXIT_CLR_LOAD: nop + + .try + { + // If the above fails for any reason, we fallback to attempting to load "Python.Runtime.dll" + // from the directory this assembly is running in. "This assembly" is probably "clr.pyd", + // sitting somewhere in PYTHONPATH. This is using Assembly.LoadFrom, and inherits all the + // caveats of that call. See MSDN docs for details. + // Suzanne Cook's blog is also an excellent source of info on this: + // http://blogs.msdn.com/suzcook/ + // http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx + // http://blogs.msdn.com/suzcook/archive/2003/06/13/57180.aspx + // executingAssembly = System.Reflection.Assembly.GetExecutingAssembly(); + call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::GetExecutingAssembly() + stloc executingAssembly + + // assemblyDirectory = System.IO.Path.GetDirectoryName(executingAssembly.Location); + ldloc executingAssembly + callvirt instance string [mscorlib]System.Reflection.Assembly::get_Location() + call string [mscorlib]System.IO.Path::GetDirectoryName(string) + stloc assemblyDirectory + + // pythonRuntimeDllPath = System.IO.Path.Combine(assemblyDirectory, "Python.Runtime.dll"); + ldloc assemblyDirectory + ldstr "Python.Runtime.dll" + call string [mscorlib]System.IO.Path::Combine(string, string) + stloc pythonRuntimeDllPath + +#ifdef DEBUG_PRINT + ldstr "Attempting to load Python.Runtime from: '{0}'... " + ldloc pythonRuntimeDllPath + call void [mscorlib]System.Console::Write(string, object) +#endif + + // pythonRuntime = System.Reflection.Assembly.LoadFrom(pythonRuntimeDllPath); + ldloc pythonRuntimeDllPath + call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::LoadFrom(string) + stloc pythonRuntime + +#ifdef DEBUG_PRINT + ldstr "Success!" + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s LOADED_PYTHON_RUNTIME + } + catch [mscorlib]System.Object + { +#ifdef DEBUG_PRINT + ldstr "Failed." + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s EXIT_PYTHONPATH_LOAD + } + EXIT_PYTHONPATH_LOAD: nop + + // If we get here, we haven't loaded Python.Runtime, so bail. +#ifdef DEBUG_PRINT + ldstr "Could not load Python.Runtime, so sad." + call void [mscorlib]System.Console::WriteLine(string) +#endif + ret + + // Once here, we've successfully loaded SOME version of Python.Runtime + // So now we get the PythonEngine and execute the InitExt method on it. + LOADED_PYTHON_RUNTIME: nop + .try + { +#ifdef DEBUG_PRINT + ldstr "Running Python.Runtime.PythonEngine.InitExt()" + call void [mscorlib]System.Console::WriteLine(string) +#endif + // pythonEngineType = pythonRuntime.GetType("Python.Runtime.PythonEngine"); + ldloc pythonRuntime + ldstr "Python.Runtime.PythonEngine" + callvirt instance class [mscorlib]System.Type [mscorlib]System.Reflection.Assembly::GetType(string) + stloc pythonEngineType + + // pythonEngineType.InvokeMember("InitExt", System.Reflection.BindingFlags.InvokeMethod, null, null, null); + ldloc pythonEngineType + ldstr "InitExt" + ldc.i4 0x100 + ldnull + ldnull + ldnull + callvirt instance object [mscorlib]System.Type::InvokeMember( string, + valuetype [mscorlib]System.Reflection.BindingFlags, + class [mscorlib]System.Reflection.Binder, + object, + object[]) + pop + leave.s EXIT_TRY_INVOKE + } + catch [mscorlib]System.Object + { +#ifdef DEBUG_PRINT + ldstr "Error calling Python.Runtime.PythonEngine.InitExt()." + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s EXIT_TRY_INVOKE + } + EXIT_TRY_INVOKE: nop + + ret + } +} + diff --git a/Pythonnet.Runtime/clrobject.cs b/Pythonnet.Runtime/clrobject.cs new file mode 100644 index 0000000000..c61f9523db --- /dev/null +++ b/Pythonnet.Runtime/clrobject.cs @@ -0,0 +1,78 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace Python.Runtime { + + + internal class CLRObject : ManagedType { + + internal Object inst; + + internal CLRObject(Object ob, IntPtr tp) : base() { + + IntPtr py = Runtime.PyType_GenericAlloc(tp, 0); + + int flags = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_flags); + if ((flags & TypeFlags.Subclass) != 0) { + IntPtr dict = Marshal.ReadIntPtr(py, ObjectOffset.ob_dict); + if (dict == IntPtr.Zero) { + dict = Runtime.PyDict_New(); + Marshal.WriteIntPtr(py, ObjectOffset.ob_dict, dict); + } + } + + GCHandle gc = GCHandle.Alloc(this); + Marshal.WriteIntPtr(py, ObjectOffset.magic(), (IntPtr)gc); + this.tpHandle = tp; + this.pyHandle = py; + this.gcHandle = gc; + inst = ob; + } + + + internal static CLRObject GetInstance(Object ob, IntPtr pyType) { + return new CLRObject(ob, pyType); + } + + + internal static CLRObject GetInstance(Object ob) { + ClassBase cc = ClassManager.GetClass(ob.GetType()); + return GetInstance(ob, cc.tpHandle); + } + + + internal static IntPtr GetInstHandle(Object ob, IntPtr pyType) { + CLRObject co = GetInstance(ob, pyType); + return co.pyHandle; + } + + + internal static IntPtr GetInstHandle(Object ob, Type type) { + ClassBase cc = ClassManager.GetClass(type); + CLRObject co = GetInstance(ob, cc.tpHandle); + return co.pyHandle; + } + + + internal static IntPtr GetInstHandle(Object ob) { + CLRObject co = GetInstance(ob); + return co.pyHandle; + } + + + } + + +} + diff --git a/Pythonnet.Runtime/codegenerator.cs b/Pythonnet.Runtime/codegenerator.cs new file mode 100644 index 0000000000..4305471e09 --- /dev/null +++ b/Pythonnet.Runtime/codegenerator.cs @@ -0,0 +1,62 @@ +// ========================================================================== +// 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; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +namespace Python.Runtime { + + /// + /// Several places in the runtime generate code on the fly to support + /// dynamic functionality. The CodeGenerator class manages the dynamic + /// assembly used for code generation and provides utility methods for + /// certain repetitive tasks. + /// + + internal class CodeGenerator { + + AssemblyBuilder aBuilder; + ModuleBuilder mBuilder; + + internal CodeGenerator() { + AssemblyName aname = new AssemblyName(); + aname.Name = "__CodeGenerator_Assembly"; + AssemblyBuilderAccess aa = AssemblyBuilderAccess.Run; + + aBuilder = Thread.GetDomain().DefineDynamicAssembly(aname, aa); + mBuilder = aBuilder.DefineDynamicModule("__CodeGenerator_Module"); + } + + //==================================================================== + // DefineType is a shortcut utility to get a new TypeBuilder. + //==================================================================== + + internal TypeBuilder DefineType(string name) { + TypeAttributes attrs = TypeAttributes.Public; + return mBuilder.DefineType(name, attrs); + } + + //==================================================================== + // DefineType is a shortcut utility to get a new TypeBuilder. + //==================================================================== + + internal TypeBuilder DefineType(string name, Type basetype) { + TypeAttributes attrs = TypeAttributes.Public; + return mBuilder.DefineType(name, attrs, basetype); + } + + } + + +} diff --git a/Pythonnet.Runtime/constructorbinder.cs b/Pythonnet.Runtime/constructorbinder.cs new file mode 100644 index 0000000000..4dc1f1457a --- /dev/null +++ b/Pythonnet.Runtime/constructorbinder.cs @@ -0,0 +1,96 @@ +// ========================================================================== +// 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.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // A ConstructorBinder encapsulates information about one or more managed + // constructors, and is responsible for selecting the right constructor + // given a set of Python arguments. This is slightly different than the + // standard MethodBinder because of a difference in invoking constructors + // using reflection (which is seems to be a CLR bug). + //======================================================================== + + internal class ConstructorBinder : MethodBinder { + + internal ConstructorBinder () : base() {} + + //==================================================================== + // Constructors get invoked when an instance of a wrapped managed + // class or a subclass of a managed class is created. This differs + // from the MethodBinder implementation in that we return the raw + // result of the constructor rather than wrapping it as a Python + // object - the reason is that only the caller knows the correct + // Python type to use when wrapping the result (may be a subclass). + //==================================================================== + + internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw) { + return this.InvokeRaw(inst, args, kw, null); + } + /// + /// Allows ctor selection to be limited to a single attempt at a + /// match by providing the MethodBase to use instead of searching + /// the entire MethodBinder.list (generic ArrayList) + /// + /// (possibly null) instance + /// PyObject* to the arg tuple + /// PyObject* to the keyword args dict + /// The sole ContructorInfo to use or null + /// The result of the constructor call with converted params + /// + /// 2010-07-24 BC: I added the info parameter to the call to Bind() + /// Binding binding = this.Bind(inst, args, kw, info); + /// to take advantage of Bind()'s ability to use a single MethodBase (CI or MI). + /// + internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, + MethodBase info) { + Binding binding = this.Bind(inst, args, kw, info); + Object result; + + if (binding == null) { + // It is possible for __new__ to be invoked on construction + // of a Python subclass of a managed class, so args may + // reflect more args than are required to instantiate the + // class. So if we cant find a ctor that matches, we'll see + // if there is a default constructor and, if so, assume that + // any extra args are intended for the subclass' __init__. + + IntPtr eargs = Runtime.PyTuple_New(0); + binding = this.Bind(inst, eargs, kw); + Runtime.Decref(eargs); + + if (binding == null) { + Exceptions.SetError(Exceptions.TypeError, + "no constructor matches given arguments" + ); + return null; + } + } + + // Fire the selected ctor and catch errors... + ConstructorInfo ci = (ConstructorInfo)binding.info; + // Object construction is presumed to be non-blocking and fast + // enough that we shouldn't really need to release the GIL. + try { + result = ci.Invoke(binding.args); + } + catch (Exception e) { + if (e.InnerException != null) { + e = e.InnerException; + } + Exceptions.SetError(e); + return null; + } + return result; + } + } +} diff --git a/Pythonnet.Runtime/constructorbinding.cs b/Pythonnet.Runtime/constructorbinding.cs new file mode 100644 index 0000000000..c5f014469f --- /dev/null +++ b/Pythonnet.Runtime/constructorbinding.cs @@ -0,0 +1,237 @@ +// ========================================================================== +// 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.Reflection; + +namespace Python.Runtime { + + /// + /// Implements a Python type that wraps a CLR ctor call. Constructor objects + /// support a .Overloads[] syntax to allow explicit ctor overload selection. + /// + /// + /// ClassManager stores a ConstructorBinding instance in the class's __dict__['Overloads'] + /// + /// SomeType.Overloads[Type, ...] works like this: + /// 1) Python retreives the Overloads attribute from this ClassObject's dictionary normally + /// and finds a non-null tp_descr_get slot which is called by the interpreter + /// and returns an IncRef()ed pyHandle to itself. + /// 2) The ConstructorBinding object handles the [] syntax in its mp_subscript by matching + /// the Type object parameters to a contructor overload using Type.GetConstructor() + /// [NOTE: I don't know why method overloads are not searched the same way.] + /// and creating the BoundContructor oject which contains ContructorInfo object. + /// 3) In tp_call, if ctorInfo is not null, ctorBinder.InvokeRaw() is called. + /// + internal class ConstructorBinding : ExtensionType + { + Type type; // The managed Type being wrapped in a ClassObject + IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create. + ConstructorBinder ctorBinder; + IntPtr repr; + + public ConstructorBinding(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder) : base() { + this.type = type; + Runtime.Incref(pyTypeHndl); + this.pyTypeHndl = pyTypeHndl; + this.ctorBinder = ctorBinder; + repr = IntPtr.Zero; + } + + /// + /// Descriptor __get__ implementation. + /// Implements a Python type that wraps a CLR ctor call that requires the use + /// of a .Overloads[pyTypeOrType...] syntax to allow explicit ctor overload + /// selection. + /// + /// PyObject* to a Constructors wrapper + /// the instance that the attribute was accessed through, + /// or None when the attribute is accessed through the owner + /// always the owner class + /// a CtorMapper (that borrows a reference to this python type and the + /// ClassObject's ConstructorBinder) wrapper. + /// + /// + /// Python 2.6.5 docs: + /// object.__get__(self, instance, owner) + /// Called to get the attribute of the owner class (class attribute access) + /// or of an instance of that class (instance attribute access). + /// owner is always the owner class, while instance is the instance that + /// the attribute was accessed through, or None when the attribute is accessed through the owner. + /// This method should return the (computed) attribute value or raise an AttributeError exception. + /// + public static IntPtr tp_descr_get(IntPtr op, IntPtr instance, IntPtr owner) + { + ConstructorBinding self = (ConstructorBinding)GetManagedObject(op); + if (self == null) { + return IntPtr.Zero; + } + + // It doesn't seem to matter if it's accessed through an instance (rather than via the type). + /*if (instance != IntPtr.Zero) { + // This is ugly! PyObject_IsInstance() returns 1 for true, 0 for false, -1 for error... + if (Runtime.PyObject_IsInstance(instance, owner) < 1) { + return Exceptions.RaiseTypeError("How in the world could that happen!"); + } + }*/ + Runtime.Incref(self.pyHandle); // Decref'd by the interpreter. + return self.pyHandle; + } + + //==================================================================== + // Implement explicit overload selection using subscript syntax ([]). + //==================================================================== + /// + /// ConstructorBinding.GetItem(PyObject *o, PyObject *key) + /// Return element of o corresponding to the object key or NULL on failure. + /// This is the equivalent of the Python expression o[key]. + /// + /// + /// + /// + public static IntPtr mp_subscript(IntPtr op, IntPtr key) { + ConstructorBinding self = (ConstructorBinding)GetManagedObject(op); + + Type[] types = Runtime.PythonArgsToTypeArray(key); + if (types == null) { + return Exceptions.RaiseTypeError("type(s) expected"); + } + //MethodBase[] methBaseArray = self.ctorBinder.GetMethods(); + //MethodBase ci = MatchSignature(methBaseArray, types); + ConstructorInfo ci = self.type.GetConstructor(types); + if (ci == null) { + string msg = "No match found for constructor signature"; + return Exceptions.RaiseTypeError(msg); + } + BoundContructor boundCtor = new BoundContructor(self.type, self.pyTypeHndl, self.ctorBinder, ci); + + /* Since nothing's chached, do we need the increment??? + Runtime.Incref(boundCtor.pyHandle); // Decref'd by the interpreter??? */ + return boundCtor.pyHandle; + } + + //==================================================================== + // ConstructorBinding __repr__ implementation [borrowed from MethodObject]. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + ConstructorBinding self = (ConstructorBinding)GetManagedObject(ob); + if (self.repr != IntPtr.Zero) { + Runtime.Incref(self.repr); + return self.repr; + } + MethodBase[] methods = self.ctorBinder.GetMethods(); + string name = self.type.FullName; + string doc = ""; + for (int i = 0; i < methods.Length; i++) { + if (doc.Length > 0) + doc += "\n"; + string str = methods[i].ToString(); + int idx = str.IndexOf("("); + doc += String.Format("{0}{1}", name, str.Substring(idx)); + } + self.repr = Runtime.PyString_FromString(doc); + Runtime.Incref(self.repr); + return self.repr; + } + + //==================================================================== + // ConstructorBinding dealloc implementation. + //==================================================================== + + public static new void tp_dealloc(IntPtr ob) { + ConstructorBinding self = (ConstructorBinding)GetManagedObject(ob); + Runtime.Decref(self.repr); + Runtime.Decref(self.pyTypeHndl); + ExtensionType.FinalizeObject(self); + } + } + + /// + /// Implements a Python type that constucts the given Type given a particular ContructorInfo. + /// + /// + /// Here mostly because I wanted a new __repr__ function for the selected constructor. + /// An earlier implementation hung the __call__ on the ContructorBinding class and + /// returned an Incref()ed self.pyHandle from the __get__ function. + /// + internal class BoundContructor : ExtensionType { + Type type; // The managed Type being wrapped in a ClassObject + IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create. + ConstructorBinder ctorBinder; + ConstructorInfo ctorInfo; + IntPtr repr; + + public BoundContructor(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder, ConstructorInfo ci) + : base() { + this.type = type; + Runtime.Incref(pyTypeHndl); + this.pyTypeHndl = pyTypeHndl; + this.ctorBinder = ctorBinder; + ctorInfo = ci; + repr = IntPtr.Zero; + } + + /// + /// BoundContructor.__call__(PyObject *callable_object, PyObject *args, PyObject *kw) + /// + /// PyObject *callable_object + /// PyObject *args + /// PyObject *kw + /// A reference to a new instance of the class by invoking the selected ctor(). + public static IntPtr tp_call(IntPtr op, IntPtr args, IntPtr kw) { + BoundContructor self = (BoundContructor)GetManagedObject(op); + // Even though a call with null ctorInfo just produces the old behavior + /*if (self.ctorInfo == null) { + string msg = "Usage: Class.Overloads[CLR_or_python_Type, ...]"; + return Exceptions.RaiseTypeError(msg); + }*/ + // Bind using ConstructorBinder.Bind and invoke the ctor providing a null instancePtr + // which will fire self.ctorInfo using ConstructorInfo.Invoke(). + Object obj = self.ctorBinder.InvokeRaw(IntPtr.Zero, args, kw, self.ctorInfo); + if (obj == null) { + // XXX set an error + return IntPtr.Zero; + } + // Instantiate the python object that wraps the result of the method call + // and return the PyObject* to it. + return CLRObject.GetInstHandle(obj, self.pyTypeHndl); + } + + //==================================================================== + // BoundContructor __repr__ implementation [borrowed from MethodObject]. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + BoundContructor self = (BoundContructor)GetManagedObject(ob); + if (self.repr != IntPtr.Zero) { + Runtime.Incref(self.repr); + return self.repr; + } + string name = self.type.FullName; + string str = self.ctorInfo.ToString(); + int idx = str.IndexOf("("); + str = String.Format("returns a new {0}{1}", name, str.Substring(idx)); + self.repr = Runtime.PyString_FromString(str); + Runtime.Incref(self.repr); + return self.repr; + } + + //==================================================================== + // ConstructorBinding dealloc implementation. + //==================================================================== + + public static new void tp_dealloc(IntPtr ob) { + BoundContructor self = (BoundContructor)GetManagedObject(ob); + Runtime.Decref(self.repr); + Runtime.Decref(self.pyTypeHndl); + ExtensionType.FinalizeObject(self); + } + } +} diff --git a/Pythonnet.Runtime/converter.cs b/Pythonnet.Runtime/converter.cs new file mode 100644 index 0000000000..af7acd972a --- /dev/null +++ b/Pythonnet.Runtime/converter.cs @@ -0,0 +1,714 @@ +// ========================================================================== +// 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.Reflection; +using System.Runtime.InteropServices; +using System.Globalization; +using System.Security; + +namespace Python.Runtime { + + //======================================================================== + // Performs data conversions between managed types and Python types. + //======================================================================== + + [SuppressUnmanagedCodeSecurityAttribute()] + + internal class Converter { + + private Converter() {} + + static NumberFormatInfo nfi; + static Type objectType; + static Type stringType; + static Type doubleType; + static Type int32Type; + static Type int64Type; + static Type flagsType; + static Type boolType; + //static Type typeType; + + static Converter () { + nfi = NumberFormatInfo.InvariantInfo; + objectType = typeof(Object); + stringType = typeof(String); + int32Type = typeof(Int32); + int64Type = typeof(Int64); + doubleType = typeof(Double); + flagsType = typeof(FlagsAttribute); + boolType = typeof(Boolean); + //typeType = typeof(Type); + } + + + //==================================================================== + // Given a builtin Python type, return the corresponding CLR type. + //==================================================================== + + internal static Type GetTypeByAlias(IntPtr op) { + if ((op == Runtime.PyStringType) || + (op == Runtime.PyUnicodeType)) { + return stringType; + } + else if (op == Runtime.PyIntType) { + return int32Type; + } + else if (op == Runtime.PyLongType) { + return int64Type; + } + else if (op == Runtime.PyFloatType) { + return doubleType; + } + else if (op == Runtime.PyBoolType) { + return boolType; + } + return null; + } + + + //==================================================================== + // Return a Python object for the given native object, converting + // basic types (string, int, etc.) into equivalent Python objects. + // This always returns a new reference. Note that the System.Decimal + // type has no Python equivalent and converts to a managed instance. + //==================================================================== + + internal static IntPtr ToPython(Object value, Type type) { + IntPtr result = IntPtr.Zero; + + // Null always converts to None in Python. + + if (value == null) { + result = Runtime.PyNone; + Runtime.Incref(result); + return result; + } + + // hmm - from Python, we almost never care what the declared + // type is. we'd rather have the object bound to the actual + // implementing class. + + type = value.GetType(); + + TypeCode tc = Type.GetTypeCode(type); + + switch(tc) { + + case TypeCode.Object: + result = CLRObject.GetInstHandle(value, type); + + // XXX - hack to make sure we convert new-style class based + // managed exception instances to wrappers ;( + if (Runtime.wrap_exceptions) { + Exception e = value as Exception; + if (e != null) { + return Exceptions.GetExceptionInstanceWrapper(result); + } + } + + return result; + + case TypeCode.String: + return Runtime.PyUnicode_FromString((string)value); + + case TypeCode.Int32: + return Runtime.PyInt_FromInt32((int)value); + + case TypeCode.Boolean: + if ((bool)value) { + Runtime.Incref(Runtime.PyTrue); + return Runtime.PyTrue; + } + Runtime.Incref(Runtime.PyFalse); + return Runtime.PyFalse; + + case TypeCode.Byte: + return Runtime.PyInt_FromInt32((int)((byte)value)); + + case TypeCode.Char: + return Runtime.PyUnicode_FromOrdinal((int)((char)value)); + + case TypeCode.Int16: + return Runtime.PyInt_FromInt32((int)((short)value)); + + case TypeCode.Int64: + return Runtime.PyLong_FromLongLong((long)value); + + case TypeCode.Single: + // return Runtime.PyFloat_FromDouble((double)((float)value)); + string ss = ((float)value).ToString(nfi); + IntPtr ps = Runtime.PyString_FromString(ss); + IntPtr op = Runtime.PyFloat_FromString(ps, IntPtr.Zero); + Runtime.Decref(ps); + return op; + + case TypeCode.Double: + return Runtime.PyFloat_FromDouble((double)value); + + case TypeCode.SByte: + return Runtime.PyInt_FromInt32((int)((sbyte)value)); + + case TypeCode.UInt16: + return Runtime.PyInt_FromInt32((int)((ushort)value)); + + case TypeCode.UInt32: + return Runtime.PyLong_FromUnsignedLong((uint)value); + + case TypeCode.UInt64: + return Runtime.PyLong_FromUnsignedLongLong((ulong)value); + + default: + result = CLRObject.GetInstHandle(value, type); + return result; + } + + } + + + //==================================================================== + // In a few situations, we don't have any advisory type information + // when we want to convert an object to Python. + //==================================================================== + + internal static IntPtr ToPythonImplicit(Object value) { + if (value == null) { + IntPtr result = Runtime.PyNone; + Runtime.Incref(result); + return result; + } + + return ToPython(value, objectType); + } + + + //==================================================================== + // Return a managed object for the given Python object, taking funny + // byref types into account. + //==================================================================== + + internal static bool ToManaged(IntPtr value, Type type, + out object result, bool setError) { + if (type.IsByRef) { + type = type.GetElementType(); + } + return Converter.ToManagedValue(value, type, out result, setError); + } + + + internal static bool ToManagedValue(IntPtr value, Type obType, + out Object result, bool setError) { + // Common case: if the Python value is a wrapped managed object + // instance, just return the wrapped object. + ManagedType mt = ManagedType.GetManagedObject(value); + result = null; + + // XXX - hack to support objects wrapped in old-style classes + // (such as exception objects). + if (Runtime.wrap_exceptions) { + if (mt == null) { + if (Runtime.PyObject_IsInstance( + value, Exceptions.Exception + ) > 0) { + IntPtr p = Runtime.PyObject_GetAttrString(value, "_inner"); + if (p != IntPtr.Zero) { + // This is safe because we know that the __dict__ of + // value holds a reference to _inner. + value = p; + Runtime.Decref(p); + mt = ManagedType.GetManagedObject(value); + } + } + IntPtr c = Exceptions.UnwrapExceptionClass(value); + if ((c != IntPtr.Zero) && (c != value)) { + value = c; + Runtime.Decref(c); + mt = ManagedType.GetManagedObject(value); + } + } + } + + if (mt != null) { + if (mt is CLRObject) { + object tmp = ((CLRObject)mt).inst; + if (obType.IsInstanceOfType(tmp)) { + result = tmp; + return true; + } + string err = "value cannot be converted to {0}"; + err = String.Format(err, obType); + Exceptions.SetError(Exceptions.TypeError, err); + return false; + } + if (mt is ClassBase) { + result = ((ClassBase)mt).type; + return true; + } + // shouldnt happen + return false; + } + + if (value == Runtime.PyNone && !obType.IsValueType) { + result = null; + return true; + } + + if (obType.IsArray) { + return ToArray(value, obType, out result, setError); + } + + if (obType.IsEnum) { + return ToEnum(value, obType, out result, setError); + } + + // Conversion to 'Object' is done based on some reasonable + // default conversions (Python string -> managed string, + // Python int -> Int32 etc.). + + if (obType == objectType) { + if (Runtime.IsStringType(value)) { + return ToPrimitive(value, stringType, out result, + setError); + } + + else if (Runtime.PyBool_Check(value)) { + return ToPrimitive(value, boolType, out result, setError); + } + + else if (Runtime.PyInt_Check(value)) { + return ToPrimitive(value, int32Type, out result, setError); + } + + else if (Runtime.PyLong_Check(value)) { + return ToPrimitive(value, int64Type, out result, setError); + } + + else if (Runtime.PyFloat_Check(value)) { + return ToPrimitive(value, doubleType, out result, setError); + } + + else if (Runtime.PySequence_Check(value)) { + return ToArray(value, typeof(object[]), out result, + setError); + } + + if (setError) { + Exceptions.SetError(Exceptions.TypeError, + "value cannot be converted to Object" + ); + } + + return false; + } + + return ToPrimitive(value, obType, out result, setError); + + } + + //==================================================================== + // Convert a Python value to an instance of a primitive managed type. + //==================================================================== + + static bool ToPrimitive(IntPtr value, Type obType, out Object result, + bool setError) { + + IntPtr overflow = Exceptions.OverflowError; + TypeCode tc = Type.GetTypeCode(obType); + result = null; + IntPtr op; + int ival; + + switch(tc) { + + case TypeCode.String: + string st = Runtime.GetManagedString(value); + if (st == null) { + goto type_error; + } + result = st; + return true; + + case TypeCode.Int32: + // Trickery to support 64-bit platforms. + if (IntPtr.Size == 4) { + op = Runtime.PyNumber_Int(value); + + // As of Python 2.3, large ints magically convert :( + if (Runtime.PyLong_Check(op) ) { + Runtime.Decref(op); + goto overflow; + } + + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + ival = (int)Runtime.PyInt_AsLong(op); + Runtime.Decref(op); + result = ival; + return true; + } + else { + op = Runtime.PyNumber_Long(value); + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + long ll = (long)Runtime.PyLong_AsLongLong(op); + Runtime.Decref(op); + if ((ll == -1) && Exceptions.ErrorOccurred()) { + goto overflow; + } + if (ll > Int32.MaxValue || ll < Int32.MinValue) { + goto overflow; + } + result = (int)ll; + return true; + } + + case TypeCode.Boolean: + result = (Runtime.PyObject_IsTrue(value) != 0); + return true; + + case TypeCode.Byte: + if (Runtime.PyObject_TypeCheck(value, Runtime.PyStringType)) { + if (Runtime.PyString_Size(value) == 1) { + op = Runtime.PyString_AS_STRING(value); + result = (byte)Marshal.ReadByte(op); + return true; + } + goto type_error; + } + + op = Runtime.PyNumber_Int(value); + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + ival = (int) Runtime.PyInt_AsLong(op); + Runtime.Decref(op); + + if (ival > Byte.MaxValue || ival < Byte.MinValue) { + goto overflow; + } + byte b = (byte) ival; + result = b; + return true; + + case TypeCode.SByte: + if (Runtime.PyObject_TypeCheck(value, Runtime.PyStringType)) { + if (Runtime.PyString_Size(value) == 1) { + op = Runtime.PyString_AS_STRING(value); + result = (sbyte)Marshal.ReadByte(op); + return true; + } + goto type_error; + } + + op = Runtime.PyNumber_Int(value); + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + ival = (int) Runtime.PyInt_AsLong(op); + Runtime.Decref(op); + + if (ival > SByte.MaxValue || ival < SByte.MinValue) { + goto overflow; + } + sbyte sb = (sbyte) ival; + result = sb; + return true; + + case TypeCode.Char: + + if (Runtime.PyObject_TypeCheck(value, Runtime.PyStringType)) { + if (Runtime.PyString_Size(value) == 1) { + op = Runtime.PyString_AS_STRING(value); + result = (char)Marshal.ReadByte(op); + return true; + } + goto type_error; + } + + else if (Runtime.PyObject_TypeCheck(value, + Runtime.PyUnicodeType)) { + if (Runtime.PyUnicode_GetSize(value) == 1) { + op = Runtime.PyUnicode_AS_UNICODE(value); +#if (!UCS4) + // 2011-01-02: Marshal as character array because the cast + // result = (char)Marshal.ReadInt16(op); throws an OverflowException + // on negative numbers with Check Overflow option set on the project + Char[] buff = new Char[1]; + Marshal.Copy(op, buff, 0, 1); + result = buff[0]; +#else + // XXX this is probably NOT correct? + result = (char)Marshal.ReadInt32(op); +#endif + return true; + } + goto type_error; + } + + op = Runtime.PyNumber_Int(value); + if (op == IntPtr.Zero) { + goto type_error; + } + ival = Runtime.PyInt_AsLong(op); + if (ival > Char.MaxValue || ival < Char.MinValue) { + goto overflow; + } + Runtime.Decref(op); + result = (char)ival; + return true; + + case TypeCode.Int16: + op = Runtime.PyNumber_Int(value); + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + ival = (int) Runtime.PyInt_AsLong(op); + Runtime.Decref(op); + if (ival > Int16.MaxValue || ival < Int16.MinValue) { + goto overflow; + } + short s = (short) ival; + result = s; + return true; + + case TypeCode.Int64: + op = Runtime.PyNumber_Long(value); + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + long l = (long)Runtime.PyLong_AsLongLong(op); + Runtime.Decref(op); + if ((l == -1) && Exceptions.ErrorOccurred()) { + goto overflow; + } + result = l; + return true; + + case TypeCode.UInt16: + op = Runtime.PyNumber_Int(value); + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + ival = (int) Runtime.PyInt_AsLong(op); + Runtime.Decref(op); + if (ival > UInt16.MaxValue || ival < UInt16.MinValue) { + goto overflow; + } + ushort us = (ushort) ival; + result = us; + return true; + + case TypeCode.UInt32: + op = Runtime.PyNumber_Long(value); + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + uint ui = (uint)Runtime.PyLong_AsUnsignedLong(op); + Runtime.Decref(op); + if (Exceptions.ErrorOccurred()) { + goto overflow; + } + result = ui; + return true; + + case TypeCode.UInt64: + op = Runtime.PyNumber_Long(value); + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + ulong ul = (ulong)Runtime.PyLong_AsUnsignedLongLong(op); + Runtime.Decref(op); + if (Exceptions.ErrorOccurred()) { + goto overflow; + } + result = ul; + return true; + + + case TypeCode.Single: + op = Runtime.PyNumber_Float(value); + if (op == IntPtr.Zero) { + if (Exceptions.ExceptionMatches(overflow)) { + goto overflow; + } + goto type_error; + } + double dd = Runtime.PyFloat_AsDouble(value); + if (dd > Single.MaxValue || dd < Single.MinValue) { + goto overflow; + } + result = (float)dd; + return true; + + case TypeCode.Double: + op = Runtime.PyNumber_Float(value); + if (op == IntPtr.Zero) { + goto type_error; + } + double d = Runtime.PyFloat_AsDouble(op); + Runtime.Decref(op); + if (d > Double.MaxValue || d < Double.MinValue) { + goto overflow; + } + result = d; + return true; + + } + + + type_error: + + if (setError) { + string format = "'{0}' value cannot be converted to {1}"; + string tpName = Runtime.PyObject_GetTypeName(value); + string error = String.Format(format, tpName, obType); + Exceptions.SetError(Exceptions.TypeError, error); + } + + return false; + + overflow: + + if (setError) { + string error = "value too large to convert"; + Exceptions.SetError(Exceptions.OverflowError, error); + } + + return false; + + } + + + static void SetConversionError(IntPtr value, Type target) { + IntPtr ob = Runtime.PyObject_Repr(value); + string src = Runtime.GetManagedString(ob); + Runtime.Decref(ob); + string error = String.Format( + "Cannot convert {0} to {1}", src, target + ); + Exceptions.SetError(Exceptions.TypeError, error); + } + + + //==================================================================== + // Convert a Python value to a correctly typed managed array instance. + // The Python value must support the Python sequence protocol and the + // items in the sequence must be convertible to the target array type. + //==================================================================== + + static bool ToArray(IntPtr value, Type obType, out Object result, + bool setError) { + + Type elementType = obType.GetElementType(); + int size = Runtime.PySequence_Size(value); + result = null; + + if (size < 0) { + if (setError) { + SetConversionError(value, obType); + } + return false; + } + + Array items = Array.CreateInstance(elementType, size); + + // XXX - is there a better way to unwrap this if it is a real + // array? + for (int i = 0; i < size; i++) { + Object obj = null; + IntPtr item = Runtime.PySequence_GetItem(value, i); + if (item == IntPtr.Zero) { + if (setError) { + SetConversionError(value, obType); + return false; + } + } + + if (!Converter.ToManaged(item, elementType, out obj, true)) { + Runtime.Decref(item); + return false; + } + + items.SetValue(obj, i); + Runtime.Decref(item); + } + + result = items; + return true; + } + + + //==================================================================== + // Convert a Python value to a correctly typed managed enum instance. + //==================================================================== + + static bool ToEnum(IntPtr value, Type obType, out Object result, + bool setError) { + + Type etype = Enum.GetUnderlyingType(obType); + result = null; + + if (!ToPrimitive(value, etype, out result, setError)) { + return false; + } + + if (Enum.IsDefined(obType, result)) { + result = Enum.ToObject(obType, result); + return true; + } + + if (obType.GetCustomAttributes(flagsType, true).Length > 0) { + result = Enum.ToObject(obType, result); + return true; + } + + if (setError) { + string error = "invalid enumeration value"; + Exceptions.SetError(Exceptions.ValueError, error); + } + + return false; + + } + + + + } + + +} diff --git a/Pythonnet.Runtime/debughelper.cs b/Pythonnet.Runtime/debughelper.cs new file mode 100644 index 0000000000..2c7c6a054f --- /dev/null +++ b/Pythonnet.Runtime/debughelper.cs @@ -0,0 +1,128 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Threading; + +namespace Python.Runtime { + + /// + /// Debugging helper utilities. + /// The methods are only executed when the DEBUG flag is set. Otherwise + /// they are automagically hidden by the compiler and silently surpressed. + /// + + internal class DebugUtil { + + [Conditional("DEBUG")] + public static void Print(string msg, params IntPtr[] args) { + string result = msg; + result += " "; + + for (int i = 0; i < args.Length; i++) { + if (args[i] == IntPtr.Zero) { + Console.WriteLine("null arg to print"); + } + IntPtr ob = Runtime.PyObject_Repr(args[i]); + result += Runtime.GetManagedString(ob); + Runtime.Decref(ob); + result += " "; + } + Console.WriteLine(result); + return; + } + + [Conditional("DEBUG")] + public static void Print(string msg) { + Console.WriteLine(msg); + } + + [Conditional("DEBUG")] + internal static void DumpType(IntPtr type) { + IntPtr op = Marshal.ReadIntPtr(type, TypeOffset.tp_name); + string name = Marshal.PtrToStringAnsi(op); + + Console.WriteLine("Dump type: {0}", name); + + op = Marshal.ReadIntPtr(type, TypeOffset.ob_type); + DebugUtil.Print(" type: ", op); + + op = Marshal.ReadIntPtr(type, TypeOffset.tp_base); + DebugUtil.Print(" base: ", op); + + op = Marshal.ReadIntPtr(type, TypeOffset.tp_bases); + DebugUtil.Print(" bases: ", op); + + //op = Marshal.ReadIntPtr(type, TypeOffset.tp_mro); + //DebugUtil.Print(" mro: ", op); + + + FieldInfo[] slots = typeof(TypeOffset).GetFields(); + int size = IntPtr.Size; + + for (int i = 0; i < slots.Length; i++) { + int offset = i * size; + name = slots[i].Name; + op = Marshal.ReadIntPtr(type, offset); + Console.WriteLine(" {0}: {1}", name, op); + } + + Console.WriteLine(""); + Console.WriteLine(""); + + op = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); + if (op == IntPtr.Zero) { + Console.WriteLine(" dict: null"); + } + else { + DebugUtil.Print(" dict: ", op); + } + + } + + [Conditional("DEBUG")] + internal static void DumpInst(IntPtr ob) { + IntPtr tp = Runtime.PyObject_TYPE(ob); + int sz = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_basicsize); + + for (int i = 0; i < sz; i += IntPtr.Size) { + IntPtr pp = new IntPtr(ob.ToInt64() + i); + IntPtr v = Marshal.ReadIntPtr(pp); + Console.WriteLine("offset {0}: {1}", i, v); + } + + Console.WriteLine(""); + Console.WriteLine(""); + } + + [Conditional("DEBUG")] + internal static void debug(string msg) { + StackTrace st = new StackTrace(1, true); + StackFrame sf = st.GetFrame(0); + MethodBase mb = sf.GetMethod(); + Type mt = mb.DeclaringType; + string caller = mt.Name + "." + sf.GetMethod().Name; + Thread t = Thread.CurrentThread; + string tid = t.GetHashCode().ToString(); + Console.WriteLine("thread {0} : {1}", tid, caller); + Console.WriteLine(" {0}", msg); + return; + } + + + } + + +} + + diff --git a/Pythonnet.Runtime/delegatemanager.cs b/Pythonnet.Runtime/delegatemanager.cs new file mode 100644 index 0000000000..ddbabf8724 --- /dev/null +++ b/Pythonnet.Runtime/delegatemanager.cs @@ -0,0 +1,289 @@ +// ========================================================================== +// 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; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +namespace Python.Runtime { + + /// + /// The DelegateManager class manages the creation of true managed + /// delegate instances that dispatch calls to Python methods. + /// + + internal class DelegateManager { + + Hashtable cache; + Type basetype; + Type listtype; + Type voidtype; + Type typetype; + Type ptrtype; + CodeGenerator codeGenerator; + + public DelegateManager() { + basetype = typeof(Dispatcher); + listtype = typeof(ArrayList); + voidtype = typeof(void); + typetype = typeof(Type); + ptrtype = typeof(IntPtr); + cache = new Hashtable(); + codeGenerator = new CodeGenerator(); + } + + //==================================================================== + // Given a true delegate instance, return the PyObject handle of the + // Python object implementing the delegate (or IntPtr.Zero if the + // delegate is not implemented in Python code. + //==================================================================== + + public IntPtr GetPythonHandle(Delegate d) { + if ((d != null) && (d.Target is Dispatcher)) { + Dispatcher disp = d.Target as Dispatcher; + return disp.target; + } + return IntPtr.Zero; + } + + //==================================================================== + // GetDispatcher is responsible for creating a class that provides + // an appropriate managed callback method for a given delegate type. + //==================================================================== + + private Type GetDispatcher(Type dtype) { + + // If a dispatcher type for the given delegate type has already + // been generated, get it from the cache. The cache maps delegate + // types to generated dispatcher types. A possible optimization + // for the future would be to generate dispatcher types based on + // unique signatures rather than delegate types, since multiple + // delegate types with the same sig could use the same dispatcher. + + Object item = cache[dtype]; + if (item != null) { + return (Type)item; + } + + string name = "__" + dtype.FullName + "Dispatcher"; + name = name.Replace('.', '_'); + name = name.Replace('+', '_'); + TypeBuilder tb = codeGenerator.DefineType(name, basetype); + + // Generate a constructor for the generated type that calls the + // appropriate constructor of the Dispatcher base type. + + MethodAttributes ma = MethodAttributes.Public | + MethodAttributes.HideBySig | + MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName; + CallingConventions cc = CallingConventions.Standard; + Type[] args = {ptrtype, typetype}; + ConstructorBuilder cb = tb.DefineConstructor(ma, cc, args); + ConstructorInfo ci = basetype.GetConstructor(args); + ILGenerator il = cb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Call, ci); + il.Emit(OpCodes.Ret); + + // Method generation: we generate a method named "Invoke" on the + // dispatcher type, whose signature matches the delegate type for + // which it is generated. The method body simply packages the + // arguments and hands them to the Dispatch() method, which deals + // with converting the arguments, calling the Python method and + // converting the result of the call. + + MethodInfo method = dtype.GetMethod("Invoke"); + ParameterInfo[] pi = method.GetParameters(); + + Type[] signature = new Type[pi.Length]; + for (int i = 0; i < pi.Length; i++) { + signature[i] = pi[i].ParameterType; + } + + MethodBuilder mb = tb.DefineMethod( + "Invoke", + MethodAttributes.Public, + method.ReturnType, + signature + ); + + ConstructorInfo ctor = listtype.GetConstructor(Type.EmptyTypes); + MethodInfo dispatch = basetype.GetMethod("Dispatch"); + MethodInfo add = listtype.GetMethod("Add"); + + il = mb.GetILGenerator(); + il.DeclareLocal(listtype); + il.Emit(OpCodes.Newobj, ctor); + il.Emit(OpCodes.Stloc_0); + + for (int c = 0; c < signature.Length; c++) { + Type t = signature[c]; + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldarg_S, (byte)(c + 1)); + + if (t.IsValueType) { + il.Emit(OpCodes.Box, t); + } + + il.Emit(OpCodes.Callvirt, add); + il.Emit(OpCodes.Pop); + } + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Call, dispatch); + + if (method.ReturnType == voidtype) { + il.Emit(OpCodes.Pop); + } + else if (method.ReturnType.IsValueType) { + il.Emit(OpCodes.Unbox_Any, method.ReturnType); + } + + il.Emit(OpCodes.Ret); + + Type disp = tb.CreateType(); + cache[dtype] = disp; + return disp; + } + + //==================================================================== + // Given a delegate type and a callable Python object, GetDelegate + // returns an instance of the delegate type. The delegate instance + // returned will dispatch calls to the given Python object. + //==================================================================== + + internal Delegate GetDelegate(Type dtype, IntPtr callable) { + Type dispatcher = GetDispatcher(dtype); + object[] args = {callable, dtype}; + object o = Activator.CreateInstance(dispatcher, args); + return Delegate.CreateDelegate(dtype, o, "Invoke"); + } + + + + } + + + /* When a delegate instance is created that has a Python implementation, + the delegate manager generates a custom subclass of Dispatcher and + instantiates it, passing the IntPtr of the Python callable. + + The "real" delegate is created using CreateDelegate, passing the + instance of the generated type and the name of the (generated) + implementing method (Invoke). + + The true delegate instance holds the only reference to the dispatcher + instance, which ensures that when the delegate dies, the finalizer + of the referenced instance will be able to decref the Python + callable. + + A possible alternate strategy would be to create custom subclasses + of the required delegate type, storing the IntPtr in it directly. + This would be slightly cleaner, but I'm not sure if delegates are + too "special" for this to work. It would be more work, so for now + the 80/20 rule applies :) + + */ + + public class Dispatcher { + + public IntPtr target; + public Type dtype; + + public Dispatcher(IntPtr target, Type dtype) { + Runtime.Incref(target); + this.target = target; + this.dtype = dtype; + } + + ~Dispatcher() { + // Note: the managed GC thread can run and try to free one of + // these *after* the Python runtime has been finalized! + if (Runtime.Py_IsInitialized() > 0) { + IntPtr gs = PythonEngine.AcquireLock(); + Runtime.Decref(target); + PythonEngine.ReleaseLock(gs); + } + } + + public object Dispatch(ArrayList args) { + IntPtr gs = PythonEngine.AcquireLock(); + object ob = null; + + try { + ob = TrueDispatch(args); + } + catch (Exception e) { + PythonEngine.ReleaseLock(gs); + throw e; + } + + PythonEngine.ReleaseLock(gs); + return ob; + } + + public object TrueDispatch(ArrayList args) { + MethodInfo method = dtype.GetMethod("Invoke"); + ParameterInfo[] pi = method.GetParameters(); + IntPtr pyargs = Runtime.PyTuple_New(pi.Length); + Type rtype = method.ReturnType; + + for (int i = 0; i < pi.Length; i++) { + // Here we own the reference to the Python value, and we + // give the ownership to the arg tuple. + IntPtr arg = Converter.ToPython(args[i], pi[i].ParameterType); + Runtime.PyTuple_SetItem(pyargs, i, arg); + } + + IntPtr op = Runtime.PyObject_Call(target, pyargs, IntPtr.Zero); + Runtime.Decref(pyargs); + + if (op == IntPtr.Zero) { + PythonException e = new PythonException(); + throw e; + } + + if (rtype == typeof(void)) { + return null; + } + + Object result = null; + if (!Converter.ToManaged(op, rtype, out result, false)) { + string s = "could not convert Python result to " + + rtype.ToString(); + Runtime.Decref(op); + throw new ConversionException(s); + } + + Runtime.Decref(op); + return result; + } + + + } + + + public class ConversionException : System.Exception { + + public ConversionException() : base() {} + + public ConversionException(string msg) : base(msg) {} + + } + + +} diff --git a/Pythonnet.Runtime/delegateobject.cs b/Pythonnet.Runtime/delegateobject.cs new file mode 100644 index 0000000000..839fb71e59 --- /dev/null +++ b/Pythonnet.Runtime/delegateobject.cs @@ -0,0 +1,120 @@ +// ========================================================================== +// 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.Reflection; +using System.Runtime.InteropServices; + +namespace Python.Runtime { + + /// + /// Managed class that provides the implementation for reflected delegate + /// types. Delegates are represented in Python by generated type objects. + /// Each of those type objects is associated an instance of this class, + /// which provides its implementation. + /// + + internal class DelegateObject : ClassBase { + + MethodBinder binder; + + internal DelegateObject(Type tp) : base(tp) { + binder = new MethodBinder(tp.GetMethod("Invoke")); + } + + + //==================================================================== + // Given a PyObject pointer to an instance of a delegate type, return + // the true managed delegate the Python object represents (or null). + //==================================================================== + + private static Delegate GetTrueDelegate(IntPtr op) { + CLRObject o = GetManagedObject(op) as CLRObject; + if (o != null) { + Delegate d = o.inst as Delegate; + return d; + } + return null; + } + + + internal override bool CanSubclass() { + return false; + } + + + //==================================================================== + // DelegateObject __new__ implementation. The result of this is a new + // PyObject whose type is DelegateObject and whose ob_data is a handle + // to an actual delegate instance. The method wrapped by the actual + // delegate instance belongs to an object generated to relay the call + // to the Python callable passed in. + //==================================================================== + + public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { + DelegateObject self = (DelegateObject)GetManagedObject(tp); + + if (Runtime.PyTuple_Size(args) != 1) { + string message = "class takes exactly one argument"; + return Exceptions.RaiseTypeError(message); + } + + IntPtr method = Runtime.PyTuple_GetItem(args, 0); + + if (Runtime.PyCallable_Check(method) != 1) { + return Exceptions.RaiseTypeError("argument must be callable"); + } + + Delegate d = PythonEngine.DelegateManager.GetDelegate(self.type, method); + return CLRObject.GetInstHandle(d, self.pyHandle); + } + + + + //==================================================================== + // Implements __call__ for reflected delegate types. + //==================================================================== + + public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { + // todo: add fast type check! + IntPtr pytype = Runtime.PyObject_TYPE(ob); + DelegateObject self = (DelegateObject)GetManagedObject(pytype); + CLRObject o = GetManagedObject(ob) as CLRObject; + + if (o == null) { + return Exceptions.RaiseTypeError("invalid argument"); + } + + Delegate d = o.inst as Delegate; + + if (d == null) { + return Exceptions.RaiseTypeError("invalid argument"); + } + return self.binder.Invoke(ob, args, kw); + } + + + //==================================================================== + // Implements __cmp__ for reflected delegate types. + //==================================================================== + + public static new int tp_compare(IntPtr ob, IntPtr other) { + Delegate d1 = GetTrueDelegate(ob); + Delegate d2 = GetTrueDelegate(other); + if (d1 == d2) { + return 0; + } + return -1; + } + + + } + + +} diff --git a/Pythonnet.Runtime/eventbinding.cs b/Pythonnet.Runtime/eventbinding.cs new file mode 100644 index 0000000000..6135c1d68e --- /dev/null +++ b/Pythonnet.Runtime/eventbinding.cs @@ -0,0 +1,132 @@ +// ========================================================================== +// 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; + +namespace Python.Runtime { + + //======================================================================== + // Implements a Python event binding type, similar to a method binding. + //======================================================================== + + internal class EventBinding : ExtensionType { + + EventObject e; + IntPtr target; + + public EventBinding(EventObject e, IntPtr target) : base() { + Runtime.Incref(target); + this.target = target; + this.e = e; + } + + + //==================================================================== + // EventBinding += operator implementation. + //==================================================================== + + public static IntPtr nb_inplace_add(IntPtr ob, IntPtr arg) { + EventBinding self = (EventBinding)GetManagedObject(ob); + + if (Runtime.PyCallable_Check(arg) < 1) { + Exceptions.SetError(Exceptions.TypeError, + "event handlers must be callable" + ); + return IntPtr.Zero; + } + + if(!self.e.AddEventHandler(self.target, arg)) { + return IntPtr.Zero; + } + + Runtime.Incref(self.pyHandle); + return self.pyHandle; + } + + + //==================================================================== + // EventBinding -= operator implementation. + //==================================================================== + + public static IntPtr nb_inplace_subtract(IntPtr ob, IntPtr arg) { + EventBinding self = (EventBinding)GetManagedObject(ob); + + if (Runtime.PyCallable_Check(arg) < 1) { + Exceptions.SetError(Exceptions.TypeError, + "invalid event handler" + ); + return IntPtr.Zero; + } + + if (!self.e.RemoveEventHandler(self.target, arg)) { + return IntPtr.Zero; + } + + Runtime.Incref(self.pyHandle); + return self.pyHandle; + } + + + //==================================================================== + // EventBinding __hash__ implementation. + //==================================================================== + + public static IntPtr tp_hash(IntPtr ob) { + EventBinding self = (EventBinding)GetManagedObject(ob); + long x = 0; + long y = 0; + + if (self.target != IntPtr.Zero) { + x = Runtime.PyObject_Hash(self.target).ToInt64(); + if (x == -1) { + return new IntPtr(-1); + } + } + + y = Runtime.PyObject_Hash(self.e.pyHandle).ToInt64(); + if (y == -1) { + return new IntPtr(-1); + } + + x ^= y; + + if (x == -1) { + x = -1; + } + + return new IntPtr(x); + } + + + //==================================================================== + // EventBinding __repr__ implementation. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + EventBinding self = (EventBinding)GetManagedObject(ob); + string type = (self.target == IntPtr.Zero) ? "unbound" : "bound"; + string s = String.Format("<{0} event '{1}'>", type, self.e.name); + return Runtime.PyString_FromString(s); + } + + + //==================================================================== + // EventBinding dealloc implementation. + //==================================================================== + + public static new void tp_dealloc(IntPtr ob) { + EventBinding self = (EventBinding)GetManagedObject(ob); + Runtime.Decref(self.target); + ExtensionType.FinalizeObject(self); + } + + } + + +} diff --git a/Pythonnet.Runtime/eventobject.cs b/Pythonnet.Runtime/eventobject.cs new file mode 100644 index 0000000000..0e9122f492 --- /dev/null +++ b/Pythonnet.Runtime/eventobject.cs @@ -0,0 +1,230 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // Implements a Python descriptor type that provides access to CLR events. + //======================================================================== + + internal class EventObject : ExtensionType { + + internal string name; + internal EventBinding unbound; + internal EventInfo info; + internal Hashtable reg; + + public EventObject(EventInfo info) : base() { + this.name = info.Name; + this.info = info; + } + + + //==================================================================== + // Register a new Python object event handler with the event. + //==================================================================== + + internal bool AddEventHandler(IntPtr target, IntPtr handler) { + Object obj = null; + if (target != IntPtr.Zero) { + CLRObject co = (CLRObject)ManagedType.GetManagedObject(target); + obj = co.inst; + } + + // Create a true delegate instance of the appropriate type to + // wrap the Python handler. Note that wrapper delegate creation + // always succeeds, though calling the wrapper may fail. + + Type type = this.info.EventHandlerType; + Delegate d = PythonEngine.DelegateManager.GetDelegate(type, handler); + + // Now register the handler in a mapping from instance to pairs + // of (handler hash, delegate) so we can lookup to remove later. + // All this is done lazily to avoid overhead until an event is + // actually subscribed to by a Python event handler. + + if (reg == null) { + reg = new Hashtable(); + } + object key = (obj != null) ? obj : this.info.ReflectedType; + ArrayList list = reg[key] as ArrayList; + if (list == null) { + list = new ArrayList(); + reg[key] = list; + } + list.Add(new Handler(Runtime.PyObject_Hash(handler), d)); + + // Note that AddEventHandler helper only works for public events, + // so we have to get the underlying add method explicitly. + + object[] args = { d }; + MethodInfo mi = this.info.GetAddMethod(true); + mi.Invoke(obj, BindingFlags.Default, null, args, null); + + return true; + } + + + //==================================================================== + // Remove the given Python object event handler. + //==================================================================== + + internal bool RemoveEventHandler(IntPtr target, IntPtr handler) { + Object obj = null; + if (target != IntPtr.Zero) { + CLRObject co = (CLRObject)ManagedType.GetManagedObject(target); + obj = co.inst; + } + + IntPtr hash = Runtime.PyObject_Hash(handler); + if (Exceptions.ErrorOccurred() || (reg == null)) { + Exceptions.SetError(Exceptions.ValueError, + "unknown event handler" + ); + return false; + } + + object key = (obj != null) ? obj : this.info.ReflectedType; + ArrayList list = reg[key] as ArrayList; + + if (list == null) { + Exceptions.SetError(Exceptions.ValueError, + "unknown event handler" + ); + return false; + } + + object[] args = { null }; + MethodInfo mi = this.info.GetRemoveMethod(true); + + for (int i = 0; i < list.Count; i++) { + Handler item = (Handler)list[i]; + if (item.hash != hash) { + continue; + } + args[0] = item.del; + try { + mi.Invoke(obj, BindingFlags.Default, null, args, null); + } + catch { + continue; + } + list.RemoveAt(i); + return true; + } + + Exceptions.SetError(Exceptions.ValueError, + "unknown event handler" + ); + return false; + } + + + //==================================================================== + // Descriptor __get__ implementation. A getattr on an event returns + // a "bound" event that keeps a reference to the object instance. + //==================================================================== + + public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { + EventObject self = GetManagedObject(ds) as EventObject; + EventBinding binding; + + if (self == null) { + return Exceptions.RaiseTypeError("invalid argument"); + } + + // If the event is accessed through its type (rather than via + // an instance) we return an 'unbound' EventBinding that will + // be cached for future accesses through the type. + + if (ob == IntPtr.Zero) { + if (self.unbound == null) { + self.unbound = new EventBinding(self, IntPtr.Zero); + } + binding = self.unbound; + Runtime.Incref(binding.pyHandle); + return binding.pyHandle; + } + + if (Runtime.PyObject_IsInstance(ob, tp) < 1) { + return Exceptions.RaiseTypeError("invalid argument"); + } + + binding = new EventBinding(self, ob); + return binding.pyHandle; + } + + + //==================================================================== + // Descriptor __set__ implementation. This actually never allows you + // to set anything; it exists solely to support the '+=' spelling of + // event handler registration. The reason is that given code like: + // 'ob.SomeEvent += method', Python will attempt to set the attribute + // SomeEvent on ob to the result of the '+=' operation. + //==================================================================== + + public static new int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val) { + EventBinding e = GetManagedObject(val) as EventBinding; + + if (e != null) { + return 0; + } + + string message = "cannot set event attributes"; + Exceptions.RaiseTypeError(message); + return -1; + } + + + //==================================================================== + // Descriptor __repr__ implementation. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + EventObject self = (EventObject)GetManagedObject(ob); + string s = String.Format("", self.name); + return Runtime.PyString_FromString(s); + } + + + //==================================================================== + // Descriptor dealloc implementation. + //==================================================================== + + public static new void tp_dealloc(IntPtr ob) { + EventObject self = (EventObject)GetManagedObject(ob); + if (self.unbound != null) { + Runtime.Decref(self.unbound.pyHandle); + } + ExtensionType.FinalizeObject(self); + } + + + } + + + + internal class Handler { + + public IntPtr hash; + public Delegate del; + + public Handler(IntPtr hash, Delegate d) { + this.hash = hash; + this.del = d; + } + + } + + +} diff --git a/Pythonnet.Runtime/exceptions.cs b/Pythonnet.Runtime/exceptions.cs new file mode 100644 index 0000000000..f08217dac6 --- /dev/null +++ b/Pythonnet.Runtime/exceptions.cs @@ -0,0 +1,639 @@ +// ========================================================================== +// 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.Reflection; +using System.Collections; +using System.Runtime.InteropServices; + + +namespace Python.Runtime { + + /// + /// Base class for Python types that reflect managed exceptions based on + /// System.Exception + /// + /// + /// The Python wrapper for managed exceptions LIES about its inheritance + /// tree. Although the real System.Exception is a subclass of + /// System.Object the Python type for System.Exception does NOT claim that + /// it subclasses System.Object. Instead TypeManager.CreateType() uses + /// Python's exception.Exception class as base class for System.Exception. + /// + internal class ExceptionClassObject : ClassObject { + + internal ExceptionClassObject(Type tp) : base(tp) { + } + +#if (PYTHON25 || PYTHON26 || PYTHON27) + internal static Exception ToException(IntPtr ob) { + CLRObject co = GetManagedObject(ob) as CLRObject; + if (co == null) { + return null; + } + Exception e = co.inst as Exception; + if (e == null) { + return null; + } + return e; + } + + //==================================================================== + // Exception __str__ implementation + //==================================================================== + + public new static IntPtr tp_str(IntPtr ob) { + Exception e = ToException(ob); + if (e == null) { + return Exceptions.RaiseTypeError("invalid object"); + } + + string message = String.Empty; + if (e.Message != String.Empty) { + message = e.Message; + } + if ((e.StackTrace != null) && (e.StackTrace != String.Empty)) { + message = message + "\n" + e.StackTrace; + } + return Runtime.PyUnicode_FromString(message); + } + + //==================================================================== + // Exception __repr__ implementation. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + Exception e = ToException(ob); + if (e == null) { + return Exceptions.RaiseTypeError("invalid object"); + } + string name = e.GetType().Name; + string message; + if (e.Message != String.Empty) { + message = String.Format("{0}('{1}',)", name, e.Message); + } else { + message = String.Format("{0}()", name); + } + return Runtime.PyUnicode_FromString(message); + } + //==================================================================== + // Exceptions __getattribute__ implementation. + // handles Python's args and message attributes + //==================================================================== + + public static IntPtr tp_getattro(IntPtr ob, IntPtr key) + { + if (!Runtime.PyString_Check(key)) { + Exceptions.SetError(Exceptions.TypeError, "string expected"); + return IntPtr.Zero; + } + + string name = Runtime.GetManagedString(key); + if (name == "args") { + Exception e = ToException(ob); + IntPtr args; + if (e.Message != String.Empty) { + args = Runtime.PyTuple_New(1); + IntPtr msg = Runtime.PyUnicode_FromString(e.Message); + Runtime.PyTuple_SetItem(args, 0, msg); + } else { + args = Runtime.PyTuple_New(0); + } + return args; + } + + if (name == "message") { + return ExceptionClassObject.tp_str(ob); + } + + return Runtime.PyObject_GenericGetAttr(ob, key); + } +#endif // (PYTHON25 || PYTHON26 || PYTHON27) + } + + /// + /// Encapsulates the Python exception APIs. + /// + /// + /// Readability of the Exceptions class improvements as we look toward version 2.7 ... + /// + + public class Exceptions { + + internal static IntPtr warnings_module; + internal static IntPtr exceptions_module; + + private Exceptions() {} + + //=================================================================== + // Initialization performed on startup of the Python runtime. + //=================================================================== + + internal static void Initialize() { + exceptions_module = Runtime.PyImport_ImportModule("exceptions"); + Exceptions.ErrorCheck(exceptions_module); + warnings_module = Runtime.PyImport_ImportModule("warnings"); + Exceptions.ErrorCheck(warnings_module); + Type type = typeof(Exceptions); + foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | + BindingFlags.Static)) { + IntPtr op = Runtime.PyObject_GetAttrString(exceptions_module, fi.Name); + if (op != IntPtr.Zero) { + fi.SetValue(type, op); + } + else { + fi.SetValue(type, IntPtr.Zero); + DebugUtil.Print("Unknown exception: " + fi.Name); + } + } + Runtime.PyErr_Clear(); + if (Runtime.wrap_exceptions) { + SetupExceptionHack(); + } + } + + + //=================================================================== + // Cleanup resources upon shutdown of the Python runtime. + //=================================================================== + + internal static void Shutdown() { + Type type = typeof(Exceptions); + foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | + BindingFlags.Static)) { + IntPtr op = (IntPtr)fi.GetValue(type); + if (op != IntPtr.Zero) { + Runtime.Decref(op); + } + } + Runtime.Decref(exceptions_module); + Runtime.Decref(warnings_module); + } + + /// + /// Shortcut for (pointer == NULL) -> throw PythonException + /// + /// Pointer to a Python object + internal unsafe static void ErrorCheck(IntPtr pointer) { + if (pointer == IntPtr.Zero) { + throw new PythonException(); + } + } + + /// + /// Shortcut for (pointer == NULL or ErrorOccurred()) -> throw PythonException + /// + /// Shortcut for (pointer == NULL) -> throw PythonException + internal unsafe static void ErrorOccurredCheck(IntPtr pointer) { + if ((pointer == IntPtr.Zero) || Exceptions.ErrorOccurred()) { + throw new PythonException(); + } + } + + // Versions of CPython up to 2.4 do not allow exceptions to be + // new-style classes. To get around that restriction and provide + // a consistent user experience for programmers, we wrap managed + // exceptions in an old-style class that (through some dont-try- + // this-at-home hackery) delegates to the managed exception and + // obeys the conventions of both Python and managed exceptions. + + /// + /// Conditionally initialized variables! + /// + static IntPtr ns_exc; // new-style class for System.Exception + static IntPtr os_exc; // old-style class for System.Exception + static Hashtable cache; + + /// + /// the lines + /// // XXX - hack to raise a compatible old-style exception ;( + /// if (Runtime.wrap_exceptions) { + /// CallOneOfTheseMethods(); + /// + /// + internal static void SetupExceptionHack() { + ns_exc = ClassManager.GetClass(typeof(Exception)).pyHandle; + cache = new Hashtable(); + + string code = + "import exceptions\n" + + "class Exception(exceptions.Exception):\n" + + " _class = None\n" + + " _inner = None\n" + + " \n" + + " #@property\n" + + " def message(self):\n" + + " return self.Message\n" + + " message = property(message)\n" + + " \n" + + " def __init__(self, *args, **kw):\n" + + " inst = self.__class__._class(*args, **kw)\n" + + " self.__dict__['_inner'] = inst\n" + + " exceptions.Exception.__init__(self, *args, **kw)\n" + + "\n" + + " def __getattr__(self, name, _marker=[]):\n" + + " inner = self.__dict__['_inner']\n" + + " v = getattr(inner, name, _marker)\n" + + " if v is not _marker:\n" + + " return v\n" + + " v = self.__dict__.get(name, _marker)\n" + + " if v is not _marker:\n" + + " return v\n" + + " raise AttributeError(name)\n" + + "\n" + + " def __setattr__(self, name, value):\n" + + " inner = self.__dict__['_inner']\n" + + " setattr(inner, name, value)\n" + + "\n" + + " def __str__(self):\n" + + " inner = self.__dict__.get('_inner')\n" + + " msg = getattr(inner, 'Message', '')\n" + + " st = getattr(inner, 'StackTrace', '')\n" + + " st = st and '\\n' + st or ''\n" + + " return msg + st\n" + + " \n" + + " def __repr__(self):\n" + + " inner = self.__dict__.get('_inner')\n" + + " msg = getattr(inner, 'Message', '')\n" + + " name = self.__class__.__name__\n" + + " return '%s(\\'%s\\',)' % (name, msg) \n" + + "\n"; + + IntPtr dict = Runtime.PyDict_New(); + + IntPtr builtins = Runtime.PyEval_GetBuiltins(); + Runtime.PyDict_SetItemString(dict, "__builtins__", builtins); + + IntPtr namestr = Runtime.PyString_FromString("System"); + Runtime.PyDict_SetItemString(dict, "__name__", namestr); + Runtime.Decref(namestr); + + Runtime.PyDict_SetItemString(dict, "__file__", Runtime.PyNone); + Runtime.PyDict_SetItemString(dict, "__doc__", Runtime.PyNone); + + IntPtr flag = Runtime.Py_file_input; + IntPtr result = Runtime.PyRun_String(code, flag, dict, dict); + Exceptions.ErrorCheck(result); + Runtime.Decref(result); + + os_exc = Runtime.PyDict_GetItemString(dict, "Exception"); + Runtime.PyObject_SetAttrString(os_exc, "_class", ns_exc); + Runtime.PyErr_Clear(); + } + + + internal static IntPtr GenerateExceptionClass(IntPtr real) { + if (real == ns_exc) { + return os_exc; + } + + IntPtr nbases = Runtime.PyObject_GetAttrString(real, "__bases__"); + if (Runtime.PyTuple_Size(nbases) != 1) { + throw new SystemException("Invalid __bases__"); + } + IntPtr nsbase = Runtime.PyTuple_GetItem(nbases, 0); + Runtime.Decref(nbases); + + IntPtr osbase = GetExceptionClassWrapper(nsbase); + IntPtr baselist = Runtime.PyTuple_New(1); + Runtime.Incref(osbase); + Runtime.PyTuple_SetItem(baselist, 0, osbase); + IntPtr name = Runtime.PyObject_GetAttrString(real, "__name__"); + + IntPtr dict = Runtime.PyDict_New(); + IntPtr mod = Runtime.PyObject_GetAttrString(real, "__module__"); + Runtime.PyDict_SetItemString(dict, "__module__", mod); + Runtime.Decref(mod); + + IntPtr subc = Runtime.PyClass_New(baselist, dict, name); + Runtime.Decref(baselist); + Runtime.Decref(dict); + Runtime.Decref(name); + + Runtime.PyObject_SetAttrString(subc, "_class", real); + return subc; + } + + internal static IntPtr GetExceptionClassWrapper(IntPtr real) { + // Given the pointer to a new-style class representing a managed + // exception, return an appropriate old-style class wrapper that + // maintains all of the expectations and delegates to the wrapped + // class. + object ob = cache[real]; + if (ob == null) { + IntPtr op = GenerateExceptionClass(real); + cache[real] = op; + return op; + } + return (IntPtr)ob; + } + + internal static IntPtr GetExceptionInstanceWrapper(IntPtr real) { + // Given the pointer to a new-style class instance representing a + // managed exception, return an appropriate old-style class + // wrapper instance that delegates to the wrapped instance. + IntPtr tp = Runtime.PyObject_TYPE(real); + if (Runtime.PyObject_TYPE(tp) == Runtime.PyInstanceType) { + return real; + } + // Get / generate a class wrapper, instantiate it and set its + // _inner attribute to the real new-style exception instance. + IntPtr ct = GetExceptionClassWrapper(tp); + Exceptions.ErrorCheck(ct); + IntPtr op = Runtime.PyInstance_NewRaw(ct, IntPtr.Zero); + Exceptions.ErrorCheck(op); + IntPtr d = Runtime.PyObject_GetAttrString(op, "__dict__"); + Exceptions.ErrorCheck(d); + Runtime.PyDict_SetItemString(d, "_inner", real); + Runtime.Decref(d); + return op; + } + + internal static IntPtr UnwrapExceptionClass(IntPtr op) { + // In some cases its necessary to recognize an exception *class*, + // and obtain the inner (wrapped) exception class. This method + // returns the inner class if found, or a null pointer. + + IntPtr d = Runtime.PyObject_GetAttrString(op, "__dict__"); + if (d == IntPtr.Zero) { + Exceptions.Clear(); + return IntPtr.Zero; + } + IntPtr c = Runtime.PyDict_GetItemString(d, "_class"); + Runtime.Decref(d); + if (c == IntPtr.Zero) { + Exceptions.Clear(); + } + return c; + } + + /// + /// GetException Method + /// + /// + /// + /// Retrieve Python exception information as a PythonException + /// instance. The properties of the PythonException may be used + /// to access the exception type, value and traceback info. + /// + + public static PythonException GetException() { + // TODO: implement this. + return null; + } + + /// + /// ExceptionMatches Method + /// + /// + /// + /// Returns true if the current Python exception matches the given + /// Python object. This is a wrapper for PyErr_ExceptionMatches. + /// + + public static bool ExceptionMatches(IntPtr ob) { + return Runtime.PyErr_ExceptionMatches(ob) != 0; + } + + /// + /// ExceptionMatches Method + /// + /// + /// + /// Returns true if the given Python exception matches the given + /// Python object. This is a wrapper for PyErr_GivenExceptionMatches. + /// + + public static bool ExceptionMatches(IntPtr exc, IntPtr ob) { + int i = Runtime.PyErr_GivenExceptionMatches(exc, ob); + return (i != 0); + } + + /// + /// SetError Method + /// + /// + /// + /// Sets the current Python exception given a native string. + /// This is a wrapper for the Python PyErr_SetString call. + /// + + public static void SetError(IntPtr ob, string value) { + Runtime.PyErr_SetString(ob, value); + } + + /// + /// SetError Method + /// + /// + /// + /// Sets the current Python exception given a Python object. + /// This is a wrapper for the Python PyErr_SetObject call. + /// + + public static void SetError(IntPtr ob, IntPtr value) { + Runtime.PyErr_SetObject(ob, value); + } + + /// + /// SetError Method + /// + /// + /// + /// Sets the current Python exception given a CLR exception + /// object. The CLR exception instance is wrapped as a Python + /// object, allowing it to be handled naturally from Python. + /// + + public static void SetError(Exception e) { + + // Because delegates allow arbitrary nestings of Python calling + // managed calling Python calling... etc. it is possible that we + // might get a managed exception raised that is a wrapper for a + // Python exception. In that case we'd rather have the real thing. + + PythonException pe = e as PythonException; + if (pe != null) { + Runtime.PyErr_SetObject(pe.PyType, pe.PyValue); + return; + } + + IntPtr op = CLRObject.GetInstHandle(e); + + // XXX - hack to raise a compatible old-style exception ;( + if (Runtime.wrap_exceptions) { + op = GetExceptionInstanceWrapper(op); + } + IntPtr etype = Runtime.PyObject_GetAttrString(op, "__class__"); + Runtime.PyErr_SetObject(etype, op); + Runtime.Decref(etype); + Runtime.Decref(op); + } + + /// + /// ErrorOccurred Method + /// + /// + /// + /// Returns true if an exception occurred in the Python runtime. + /// This is a wrapper for the Python PyErr_Occurred call. + /// + + public static bool ErrorOccurred() { + return Runtime.PyErr_Occurred() != 0; + } + + /// + /// Clear Method + /// + /// + /// + /// Clear any exception that has been set in the Python runtime. + /// + + public static void Clear() { + Runtime.PyErr_Clear(); + } + + //==================================================================== + // helper methods for raising warnings + //==================================================================== + + /// + /// Alias for Python's warnings.warn() function. + /// + public static void warn(string message, IntPtr exception, int stacklevel) + { + if ((exception == IntPtr.Zero) || + (Runtime.PyObject_IsSubclass(exception, Exceptions.Warning) != 1)) { + Exceptions.RaiseTypeError("Invalid exception"); + } + + Runtime.Incref(warnings_module); + IntPtr warn = Runtime.PyObject_GetAttrString(warnings_module, "warn"); + Runtime.Decref(warnings_module); + Exceptions.ErrorCheck(warn); + + IntPtr args = Runtime.PyTuple_New(3); + IntPtr msg = Runtime.PyString_FromString(message); + Runtime.Incref(exception); // PyTuple_SetItem steals a reference + IntPtr level = Runtime.PyInt_FromInt32(stacklevel); + Runtime.PyTuple_SetItem(args, 0, msg); + Runtime.PyTuple_SetItem(args, 1, exception); + Runtime.PyTuple_SetItem(args, 2, level); + + IntPtr result = Runtime.PyObject_CallObject(warn, args); + Exceptions.ErrorCheck(result); + + Runtime.Decref(warn); + Runtime.Decref(result); + Runtime.Decref(args); + } + + public static void warn(string message, IntPtr exception) + { + warn(message, exception, 1); + } + + public static void deprecation(string message, int stacklevel) + { + warn(message, Exceptions.DeprecationWarning, stacklevel); + } + + public static void deprecation(string message) + { + deprecation(message, 1); + } + + //==================================================================== + // Internal helper methods for common error handling scenarios. + //==================================================================== + + internal static IntPtr RaiseTypeError(string message) { + Exceptions.SetError(Exceptions.TypeError, message); + return IntPtr.Zero; + } + + // 2010-11-16: Arranged in python (2.6 & 2.7) source header file order + /* Predefined exceptions are + puplic static variables on the Exceptions class filled in from + the python class using reflection in Initialize() looked up by + name, not posistion. */ +#if (PYTHON25 || PYTHON26 || PYTHON27) + public static IntPtr BaseException; +#endif + public static IntPtr Exception; + public static IntPtr StopIteration; +#if (PYTHON25 || PYTHON26 || PYTHON27) + public static IntPtr GeneratorExit; +#endif + public static IntPtr StandardError; + public static IntPtr ArithmeticError; + public static IntPtr LookupError; + + public static IntPtr AssertionError; + public static IntPtr AttributeError; + public static IntPtr EOFError; + public static IntPtr FloatingPointError; + public static IntPtr EnvironmentError; + public static IntPtr IOError; + public static IntPtr OSError; + public static IntPtr ImportError; + public static IntPtr IndexError; + public static IntPtr KeyError; + public static IntPtr KeyboardInterrupt; + public static IntPtr MemoryError; + public static IntPtr NameError; + public static IntPtr OverflowError; + public static IntPtr RuntimeError; + public static IntPtr NotImplementedError; + public static IntPtr SyntaxError; + public static IntPtr IndentationError; + public static IntPtr TabError; + public static IntPtr ReferenceError; + public static IntPtr SystemError; + public static IntPtr SystemExit; + public static IntPtr TypeError; + public static IntPtr UnboundLocalError; + public static IntPtr UnicodeError; + public static IntPtr UnicodeEncodeError; + public static IntPtr UnicodeDecodeError; + public static IntPtr UnicodeTranslateError; + public static IntPtr ValueError; + public static IntPtr ZeroDivisionError; +//#ifdef MS_WINDOWS + //public static IntPtr WindowsError; +//#endif +//#ifdef __VMS + //public static IntPtr VMSError; +//#endif + + //PyAPI_DATA(PyObject *) PyExc_BufferError; + + //PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst; + //PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst; + + + /* Predefined warning categories */ + public static IntPtr Warning; + public static IntPtr UserWarning; + public static IntPtr DeprecationWarning; + public static IntPtr PendingDeprecationWarning; + public static IntPtr SyntaxWarning; + public static IntPtr RuntimeWarning; + public static IntPtr FutureWarning; +#if (PYTHON25 || PYTHON26 || PYTHON27) + public static IntPtr ImportWarning; + public static IntPtr UnicodeWarning; + //PyAPI_DATA(PyObject *) PyExc_BytesWarning; +#endif + } + + +} diff --git a/Pythonnet.Runtime/extensiontype.cs b/Pythonnet.Runtime/extensiontype.cs new file mode 100644 index 0000000000..b0499bb0ae --- /dev/null +++ b/Pythonnet.Runtime/extensiontype.cs @@ -0,0 +1,129 @@ +// ========================================================================== +// 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.Runtime.InteropServices; +using System.Collections; +using System.Reflection; + +namespace Python.Runtime { + + /// + /// Base class for extensions whose instances *share* a single Python + /// type object, such as the types that represent CLR methods, fields, + /// etc. Instances implemented by this class do not support subtyping. + /// + + internal abstract class ExtensionType : ManagedType { + + public ExtensionType() : base() { + + // Create a new PyObject whose type is a generated type that is + // implemented by the particuar concrete ExtensionType subclass. + // The Python instance object is related to an instance of a + // particular concrete subclass with a hidden CLR gchandle. + + IntPtr tp = TypeManager.GetTypeHandle(this.GetType()); + +// int rc = (int)Marshal.ReadIntPtr(tp, TypeOffset.ob_refcnt); +// if (rc > 1050) { +// DebugUtil.Print("tp is: ", tp); +// DebugUtil.DumpType(tp); +// } + + IntPtr py = Runtime.PyType_GenericAlloc(tp, 0); + + GCHandle gc = GCHandle.Alloc(this); + Marshal.WriteIntPtr(py, ObjectOffset.magic(), (IntPtr)gc); + + // We have to support gc because the type machinery makes it very + // hard not to - but we really don't have a need for it in most + // concrete extension types, so untrack the object to save calls + // from Python into the managed runtime that are pure overhead. + + Runtime.PyObject_GC_UnTrack(py); + + this.tpHandle = tp; + this.pyHandle = py; + this.gcHandle = gc; + } + + + //==================================================================== + // Common finalization code to support custom tp_deallocs. + //==================================================================== + + public static void FinalizeObject(ManagedType self) { + Runtime.PyObject_GC_Del(self.pyHandle); + Runtime.Decref(self.tpHandle); + self.gcHandle.Free(); + } + + + //==================================================================== + // Type __setattr__ implementation. + //==================================================================== + + public static int tp_setattro(IntPtr ob, IntPtr key, IntPtr val) { + string message = "type does not support setting attributes"; + if (val == IntPtr.Zero) { + message = "readonly attribute"; + } + Exceptions.SetError(Exceptions.TypeError, message); + return -1; + } + + + //==================================================================== + // Default __set__ implementation - this prevents descriptor instances + // being silently replaced in a type __dict__ by default __setattr__. + //==================================================================== + + public static int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val) { + string message = "attribute is read-only"; + Exceptions.SetError(Exceptions.AttributeError, message); + return -1; + } + + + //==================================================================== + // Required Python GC support. + //==================================================================== + + public static int tp_traverse(IntPtr ob, IntPtr func, IntPtr args) { + return 0; + } + + + public static int tp_clear(IntPtr ob) { + return 0; + } + + + public static int tp_is_gc(IntPtr type) { + return 1; + } + + + //==================================================================== + // Default dealloc implementation. + //==================================================================== + + public static void tp_dealloc(IntPtr ob) { + // Clean up a Python instance of this extension type. This + // frees the allocated Python object and decrefs the type. + ManagedType self = GetManagedObject(ob); + FinalizeObject(self); + } + + + } + + +} diff --git a/Pythonnet.Runtime/fieldobject.cs b/Pythonnet.Runtime/fieldobject.cs new file mode 100644 index 0000000000..ee9d3392a5 --- /dev/null +++ b/Pythonnet.Runtime/fieldobject.cs @@ -0,0 +1,150 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace Python.Runtime { + + //======================================================================== + // Implements a Python descriptor type that provides access to CLR fields. + //======================================================================== + + internal class FieldObject : ExtensionType { + + FieldInfo info; + + public FieldObject(FieldInfo info) : base() { + this.info = info; + } + + //==================================================================== + // Descriptor __get__ implementation. This method returns the + // value of the field on the given object. The returned value + // is converted to an appropriately typed Python object. + //==================================================================== + + public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { + FieldObject self = (FieldObject)GetManagedObject(ds); + Object result; + + if (self == null) { + return IntPtr.Zero; + } + + FieldInfo info = self.info; + + if ((ob == IntPtr.Zero) || (ob == Runtime.PyNone)) { + if (!info.IsStatic) { + Exceptions.SetError(Exceptions.TypeError, + "instance attribute must be accessed " + + "through a class instance" + ); + return IntPtr.Zero; + } + try { + result = info.GetValue(null); + return Converter.ToPython(result, info.FieldType); + } + catch(Exception e) { + Exceptions.SetError(Exceptions.TypeError, e.Message); + return IntPtr.Zero; + } + } + + try { + CLRObject co = (CLRObject)GetManagedObject(ob); + result = info.GetValue(co.inst); + return Converter.ToPython(result, info.FieldType); + } + catch(Exception e) { + Exceptions.SetError(Exceptions.TypeError, e.Message); + return IntPtr.Zero; + } + } + + //==================================================================== + // Descriptor __set__ implementation. This method sets the value of + // a field based on the given Python value. The Python value must be + // convertible to the type of the field. + //==================================================================== + + public static new int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val) { + FieldObject self = (FieldObject)GetManagedObject(ds); + Object newval; + + if (self == null) { + return -1; + } + + if (val == IntPtr.Zero) { + Exceptions.SetError(Exceptions.TypeError, + "cannot delete field" + ); + return -1; + } + + FieldInfo info = self.info; + + if (info.IsLiteral || info.IsInitOnly) { + Exceptions.SetError(Exceptions.TypeError, + "field is read-only" + ); + return -1; + } + + bool is_static = info.IsStatic; + + if ((ob == IntPtr.Zero) || (ob == Runtime.PyNone)) { + if (!is_static) { + Exceptions.SetError(Exceptions.TypeError, + "instance attribute must be set " + + "through a class instance" + ); + return -1; + } + } + + if (!Converter.ToManaged(val, info.FieldType, out newval, + true)) { + return -1; + } + + try { + if (!is_static) { + CLRObject co = (CLRObject)GetManagedObject(ob); + info.SetValue(co.inst, newval); + } + else { + info.SetValue(null, newval); + } + return 0; + } + catch(Exception e) { + Exceptions.SetError(Exceptions.TypeError, e.Message); + return -1; + } + } + + //==================================================================== + // Descriptor __repr__ implementation. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + FieldObject self = (FieldObject)GetManagedObject(ob); + string s = String.Format("", self.info.Name); + return Runtime.PyString_FromStringAndSize(s, s.Length); + } + + } + + +} diff --git a/Pythonnet.Runtime/generictype.cs b/Pythonnet.Runtime/generictype.cs new file mode 100644 index 0000000000..082bc768ce --- /dev/null +++ b/Pythonnet.Runtime/generictype.cs @@ -0,0 +1,100 @@ +// ========================================================================== +// 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.Reflection; + +namespace Python.Runtime { + + /// + /// Implements reflected generic types. Note that the Python behavior + /// is the same for both generic type definitions and constructed open + /// generic types. Both are essentially factories for creating closed + /// types based on the required generic type parameters. + /// + + internal class GenericType : ClassBase { + + internal GenericType(Type tp) : base(tp) {} + + //==================================================================== + // Implements __new__ for reflected generic types. + //==================================================================== + + public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { + Exceptions.SetError(Exceptions.TypeError, + "cannot instantiate an open generic type" + ); + return IntPtr.Zero; + } + + + //==================================================================== + // Implements __call__ for reflected generic types. + //==================================================================== + + public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { + Exceptions.SetError(Exceptions.TypeError, + "object is not callable"); + return IntPtr.Zero; + } + + //==================================================================== + // Implements subscript syntax for reflected generic types. A closed + // type is created by binding the generic type via subscript syntax: + // inst = List[str]() + //==================================================================== + + public override IntPtr type_subscript(IntPtr idx) { + Type[] types = Runtime.PythonArgsToTypeArray(idx); + if (types == null) { + return Exceptions.RaiseTypeError("type(s) expected"); + } + if (!this.type.IsGenericTypeDefinition) { + return Exceptions.RaiseTypeError( + "type is not a generic type definition" + ); + } + + // This is a little tricky, because an instance of GenericType + // may represent either a specific generic type, or act as an + // alias for one or more generic types with the same base name. + + if (this.type.ContainsGenericParameters) { + Type[] args = this.type.GetGenericArguments(); + Type target = null; + + if (args.Length == types.Length) { + target = this.type; + } + else { + foreach (Type t in + GenericUtil.GenericsForType(this.type)) { + if (t.GetGenericArguments().Length == types.Length) { + target = t; + break; + } + } + } + + if (target != null) { + Type t = target.MakeGenericType(types); + ManagedType c = (ManagedType)ClassManager.GetClass(t); + Runtime.Incref(c.pyHandle); + return c.pyHandle; + } + return Exceptions.RaiseTypeError("no type matches params"); + } + + return Exceptions.RaiseTypeError("unsubscriptable object"); + } + + } + +} diff --git a/Pythonnet.Runtime/genericutil.cs b/Pythonnet.Runtime/genericutil.cs new file mode 100644 index 0000000000..c3de0aa564 --- /dev/null +++ b/Pythonnet.Runtime/genericutil.cs @@ -0,0 +1,138 @@ +// ========================================================================== +// 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.Runtime.InteropServices; +using System.Collections.Generic; +using System.Collections; +using System.Reflection; +using System.Security; + +namespace Python.Runtime { + + /// + /// This class is responsible for efficiently maintaining the bits + /// of information we need to support aliases with 'nice names'. + /// + + internal class GenericUtil { + + static Dictionary>> mapping; + + private GenericUtil() {} + + static GenericUtil() { + mapping = new + Dictionary>>(); + } + + //==================================================================== + // Register a generic type that appears in a given namespace. + //==================================================================== + + internal static void Register(Type t) { + Dictionary> nsmap = null; + mapping.TryGetValue(t.Namespace, out nsmap); + if (nsmap == null) { + nsmap = new Dictionary>(); + mapping[t.Namespace] = nsmap; + } + string basename = t.Name; + int tick = basename.IndexOf("`"); + if (tick > -1) { + basename = basename.Substring(0, tick); + } + List gnames = null; + nsmap.TryGetValue(basename, out gnames); + if (gnames == null) { + gnames = new List(); + nsmap[basename] = gnames; + } + gnames.Add(t.Name); + } + + //==================================================================== + // xxx + //==================================================================== + + public static List GetGenericBaseNames(string ns) { + Dictionary> nsmap = null; + mapping.TryGetValue(ns, out nsmap); + if (nsmap == null) { + return null; + } + List names = new List(); + foreach (string key in nsmap.Keys) { + names.Add(key); + } + return names; + } + + //==================================================================== + // xxx + //==================================================================== + + public static List GenericsForType(Type t) { + Dictionary> nsmap = null; + mapping.TryGetValue(t.Namespace, out nsmap); + if (nsmap == null) { + return null; + } + + string basename = t.Name; + int tick = basename.IndexOf("`"); + if (tick > -1) { + basename = basename.Substring(0, tick); + } + + List names = null; + nsmap.TryGetValue(basename, out names); + if (names == null) { + return null; + } + + List result = new List(); + foreach (string name in names) { + string qname = t.Namespace + "." + name; + Type o = AssemblyManager.LookupType(qname); + if (o != null) { + result.Add(o); + } + } + + return result; + } + + //==================================================================== + // xxx + //==================================================================== + + public static string GenericNameForBaseName(string ns, string name) { + Dictionary> nsmap = null; + mapping.TryGetValue(ns, out nsmap); + if (nsmap == null) { + return null; + } + List gnames = null; + nsmap.TryGetValue(name, out gnames); + if (gnames == null) { + return null; + } + if (gnames.Count > 0) { + return gnames[0]; + } + return null; + } + + + } + + + +} diff --git a/Pythonnet.Runtime/importhook.cs b/Pythonnet.Runtime/importhook.cs new file mode 100644 index 0000000000..9d618b60dd --- /dev/null +++ b/Pythonnet.Runtime/importhook.cs @@ -0,0 +1,243 @@ +// ========================================================================== +// 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.Collections; +using System.Runtime.InteropServices; + +namespace Python.Runtime { + + //======================================================================== + // Implements the "import hook" used to integrate Python with the CLR. + //======================================================================== + + internal class ImportHook { + + static IntPtr py_import; + static CLRModule root; + static MethodWrapper hook; + + //=================================================================== + // Initialization performed on startup of the Python runtime. + //=================================================================== + + internal static void Initialize() { + + // Initialize the Python <--> CLR module hook. We replace the + // built-in Python __import__ with our own. This isn't ideal, + // but it provides the most "Pythonic" way of dealing with CLR + // modules (Python doesn't provide a way to emulate packages). + + IntPtr dict = Runtime.PyImport_GetModuleDict(); + IntPtr mod = Runtime.PyDict_GetItemString(dict, "__builtin__"); + py_import = Runtime.PyObject_GetAttrString(mod, "__import__"); + + hook = new MethodWrapper(typeof(ImportHook), "__import__"); + Runtime.PyObject_SetAttrString(mod, "__import__", hook.ptr); + Runtime.Decref(hook.ptr); + + root = new CLRModule(); + Runtime.Incref(root.pyHandle); // we are using the module two times + Runtime.PyDict_SetItemString(dict, "CLR", root.pyHandle); + Runtime.PyDict_SetItemString(dict, "clr", root.pyHandle); + } + + + //=================================================================== + // Cleanup resources upon shutdown of the Python runtime. + //=================================================================== + + internal static void Shutdown() { + Runtime.Decref(root.pyHandle); + Runtime.Decref(root.pyHandle); + Runtime.Decref(py_import); + } + + + //=================================================================== + // The actual import hook that ties Python to the managed world. + //=================================================================== + + public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) { + + // Replacement for the builtin __import__. The original import + // hook is saved as this.py_import. This version handles CLR + // import and defers to the normal builtin for everything else. + + int num_args = Runtime.PyTuple_Size(args); + if (num_args < 1) { + return Exceptions.RaiseTypeError( + "__import__() takes at least 1 argument (0 given)" + ); + } + + // borrowed reference + IntPtr py_mod_name = Runtime.PyTuple_GetItem(args, 0); + if ((py_mod_name == IntPtr.Zero) || + (!Runtime.IsStringType(py_mod_name))) { + return Exceptions.RaiseTypeError("string expected"); + } + + // Check whether the import is of the form 'from x import y'. + // This determines whether we return the head or tail module. + + IntPtr fromList = IntPtr.Zero; + bool fromlist = false; + if (num_args >= 4) { + fromList = Runtime.PyTuple_GetItem(args, 3); + if ((fromList != IntPtr.Zero) && + (Runtime.PyObject_IsTrue(fromList) == 1)) { + fromlist = true; + } + } + + string mod_name = Runtime.GetManagedString(py_mod_name); + // Check these BEFORE the built-in import runs; may as well + // do the Incref()ed return here, since we've already found + // the module. + if (mod_name == "clr") { + root.InitializePreload(); + Runtime.Incref(root.pyHandle); + return root.pyHandle; + } + if (mod_name == "CLR") { + Exceptions.deprecation("The CLR module is deprecated. " + + "Please use 'clr'."); + root.InitializePreload(); + Runtime.Incref(root.pyHandle); + return root.pyHandle; + } + string realname = mod_name; + if (mod_name.StartsWith("CLR.")) { + realname = mod_name.Substring(4); + string msg = String.Format("Importing from the CLR.* namespace "+ + "is deprecated. Please import '{0}' directly.", realname); + Exceptions.deprecation(msg); + } + else { + // 2010-08-15: Always seemed smart to let python try first... + // This shaves off a few tenths of a second on test_module.py + // and works around a quirk where 'sys' is found by the + // LoadImplicit() deprecation logic. + // Turns out that the AssemblyManager.ResolveHandler() checks to see if any + // Assembly's FullName.ToLower().StartsWith(name.ToLower()), which makes very + // little sense to me. + IntPtr res = Runtime.PyObject_Call(py_import, args, kw); + if (res != IntPtr.Zero) { + // There was no error. + return res; + } + // There was an error + if (!Exceptions.ExceptionMatches(Exceptions.ImportError)) { + // and it was NOT an ImportError; bail out here. + return IntPtr.Zero; + } + // Otherwise, just clear the it. + Exceptions.Clear(); + } + + string[] names = realname.Split('.'); + + // Now we need to decide if the name refers to a CLR module, + // and may have to do an implicit load (for b/w compatibility) + // using the AssemblyManager. The assembly manager tries + // really hard not to use Python objects or APIs, because + // parts of it can run recursively and on strange threads. + // + // It does need an opportunity from time to time to check to + // see if sys.path has changed, in a context that is safe. Here + // we know we have the GIL, so we'll let it update if needed. + + AssemblyManager.UpdatePath(); + if (!AssemblyManager.IsValidNamespace(realname)) { + bool fromFile = false; + if (AssemblyManager.LoadImplicit(realname, out fromFile)) { + if (true == fromFile) { + string deprWarning = String.Format("\nThe module was found, but not in a referenced namespace.\n" + + "Implicit loading is deprecated. Please use clr.AddReference(\"{0}\").", realname); + Exceptions.deprecation(deprWarning); + } + } + else + { + // May be called when a module being imported imports a module. + // In particular, I've seen decimal import copy import org.python.core + return Runtime.PyObject_Call(py_import, args, kw); + } + } + + // See if sys.modules for this interpreter already has the + // requested module. If so, just return the exising module. + + IntPtr modules = Runtime.PyImport_GetModuleDict(); + IntPtr module = Runtime.PyDict_GetItem(modules, py_mod_name); + + if (module != IntPtr.Zero) { + if (fromlist) { + Runtime.Incref(module); + return module; + } + module = Runtime.PyDict_GetItemString(modules, names[0]); + Runtime.Incref(module); + return module; + } + Exceptions.Clear(); + + // Traverse the qualified module name to get the named module + // and place references in sys.modules as we go. Note that if + // we are running in interactive mode we pre-load the names in + // each module, which is often useful for introspection. If we + // are not interactive, we stick to just-in-time creation of + // objects at lookup time, which is much more efficient. + // NEW: The clr got a new module variable preload. You can + // enable preloading in a non-interactive python processing by + // setting clr.preload = True + + ModuleObject head = (mod_name == realname) ? null : root; + ModuleObject tail = root; + root.InitializePreload(); + + for (int i = 0; i < names.Length; i++) { + string name = names[i]; + ManagedType mt = tail.GetAttribute(name, true); + if (!(mt is ModuleObject)) { + string error = String.Format("No module named {0}", name); + Exceptions.SetError(Exceptions.ImportError, error); + return IntPtr.Zero; + } + if (head == null) { + head = (ModuleObject)mt; + } + tail = (ModuleObject) mt; + if (CLRModule.preload) { + tail.LoadNames(); + } + Runtime.PyDict_SetItemString(modules, tail.moduleName, + tail.pyHandle + ); + } + + ModuleObject mod = fromlist ? tail : head; + + if (fromlist && Runtime.PySequence_Size(fromList) == 1) { + IntPtr fp = Runtime.PySequence_GetItem(fromList, 0); + if ((!CLRModule.preload) && Runtime.GetManagedString(fp) == "*") { + mod.LoadNames(); + } + Runtime.Decref(fp); + } + + Runtime.Incref(mod.pyHandle); + return mod.pyHandle; + } + + } + + +} diff --git a/Pythonnet.Runtime/indexer.cs b/Pythonnet.Runtime/indexer.cs new file mode 100644 index 0000000000..8118dc339b --- /dev/null +++ b/Pythonnet.Runtime/indexer.cs @@ -0,0 +1,68 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; +using System.Security.Permissions; + +namespace Python.Runtime { + + //======================================================================== + // Bundles the information required to support an indexer property. + //======================================================================== + + internal class Indexer { + + public MethodBinder GetterBinder; + public MethodBinder SetterBinder; + + public Indexer() { + GetterBinder = new MethodBinder(); + SetterBinder = new MethodBinder(); + } + + + public bool CanGet { + get { + return GetterBinder.Count > 0; + } + } + + public bool CanSet { + get { + return SetterBinder.Count > 0; + } + } + + + public void AddProperty(PropertyInfo pi) { + MethodInfo getter = pi.GetGetMethod(true); + MethodInfo setter = pi.GetSetMethod(true); + if (getter != null) { + GetterBinder.AddMethod(getter); + } + if (setter != null) { + SetterBinder.AddMethod(setter); + } + } + + internal IntPtr GetItem(IntPtr inst, IntPtr args) { + return GetterBinder.Invoke(inst, args, IntPtr.Zero); + } + + + internal void SetItem(IntPtr inst, IntPtr args) { + SetterBinder.Invoke(inst, args, IntPtr.Zero); + } + + } + + +} diff --git a/Pythonnet.Runtime/interfaceobject.cs b/Pythonnet.Runtime/interfaceobject.cs new file mode 100644 index 0000000000..7c2aead1fe --- /dev/null +++ b/Pythonnet.Runtime/interfaceobject.cs @@ -0,0 +1,89 @@ +// ========================================================================== +// 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.Reflection; +using System.Runtime.InteropServices; + +namespace Python.Runtime { + + /// + /// Provides the implementation for reflected interface types. Managed + /// interfaces are represented in Python by actual Python type objects. + /// Each of those type objects is associated with an instance of this + /// class, which provides the implementation for the Python type. + /// + + internal class InterfaceObject : ClassBase { + + internal ConstructorInfo ctor; + + internal InterfaceObject(Type tp) : base(tp) { + CoClassAttribute coclass = (CoClassAttribute) + Attribute.GetCustomAttribute(tp, cc_attr); + if (coclass != null) { + ctor = coclass.CoClass.GetConstructor(Type.EmptyTypes); + } + } + + static Type cc_attr; + + static InterfaceObject() { + cc_attr = typeof(CoClassAttribute); + } + + //==================================================================== + // Implements __new__ for reflected interface types. + //==================================================================== + + public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { + InterfaceObject self = (InterfaceObject)GetManagedObject(tp); + int nargs = Runtime.PyTuple_Size(args); + Type type = self.type; + Object obj; + + if (nargs == 1) { + IntPtr inst = Runtime.PyTuple_GetItem(args, 0); + CLRObject co = GetManagedObject(inst) as CLRObject; + + if ((co == null) || (!type.IsInstanceOfType(co.inst))) { + string msg = "object does not implement " + type.Name; + Exceptions.SetError(Exceptions.TypeError, msg); + return IntPtr.Zero; + } + + obj = co.inst; + } + + else if ((nargs == 0) && (self.ctor != null)) { + obj = self.ctor.Invoke(null); + + if (obj == null || !type.IsInstanceOfType(obj)) { + Exceptions.SetError(Exceptions.TypeError, + "CoClass default constructor failed" + ); + return IntPtr.Zero; + } + } + + else { + Exceptions.SetError(Exceptions.TypeError, + "interface takes exactly one argument" + ); + return IntPtr.Zero; + } + + return CLRObject.GetInstHandle(obj, self.pyHandle); + } + + + } + + +} diff --git a/Pythonnet.Runtime/interfaces.cs b/Pythonnet.Runtime/interfaces.cs new file mode 100644 index 0000000000..484a9ad5d2 --- /dev/null +++ b/Pythonnet.Runtime/interfaces.cs @@ -0,0 +1,40 @@ +// ========================================================================== +// 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.Reflection; +using System.Runtime.InteropServices; + +namespace Python.Runtime { + + /// + /// xxx + /// + + internal interface IReflectedType { + string PythonTypeName(); + Type GetReflectedType(); + } + + internal interface IReflectedClass : IReflectedType { + bool IsException(); + } + + internal interface IReflectedInterface : IReflectedType { + + } + + internal interface IReflectedArray : IReflectedType { + } + + internal interface IReflectedGenericClass : IReflectedClass { + } + + +} diff --git a/Pythonnet.Runtime/interop.cs b/Pythonnet.Runtime/interop.cs new file mode 100644 index 0000000000..9aad4c6e40 --- /dev/null +++ b/Pythonnet.Runtime/interop.cs @@ -0,0 +1,545 @@ +// ========================================================================== +// 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.Collections; +using System.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Reflection; + +namespace Python.Runtime { + + //======================================================================= + // This file defines objects to support binary interop with the Python + // runtime. Generally, the definitions here need to be kept up to date + // when moving to new Python versions. + //======================================================================= + + [Serializable()] + [AttributeUsage(AttributeTargets.All)] + public class DocStringAttribute : Attribute { + public DocStringAttribute(string docStr) { + DocString = docStr; + } + public string DocString { + get { return docStr; } + set { docStr = value; } + } + private string docStr; + } + + [Serializable()] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate)] + internal class PythonMethodAttribute : Attribute { + public PythonMethodAttribute() {} + } + + [Serializable()] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate)] + internal class ModuleFunctionAttribute : Attribute { + public ModuleFunctionAttribute() {} + } + + [Serializable()] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate)] + internal class ForbidPythonThreadsAttribute : Attribute { + public ForbidPythonThreadsAttribute() { } + } + + + [Serializable()] + [AttributeUsage(AttributeTargets.Property)] + internal class ModulePropertyAttribute : Attribute { + public ModulePropertyAttribute() {} + } + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] + internal class ObjectOffset { + + static ObjectOffset() { + int size = IntPtr.Size; + int n = 0; // Py_TRACE_REFS add two pointers to PyObject_HEAD +#if (Py_DEBUG) + _ob_next = 0; + _ob_prev = 1 * size; + n = 2; +#endif + ob_refcnt = (n+0) * size; + ob_type = (n+1) * size; + ob_dict = (n+2) * size; + ob_data = (n+3) * size; + } + + public static int magic() { + return ob_data; + } + + public static int Size() { +#if (Py_DEBUG) + return 6 * IntPtr.Size; +#else + return 4 * IntPtr.Size; +#endif + } + +#if (Py_DEBUG) + public static int _ob_next; + public static int _ob_prev; +#endif + public static int ob_refcnt; + public static int ob_type; + public static int ob_dict; + public static int ob_data; + } + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] + internal class TypeOffset { + + static TypeOffset() { + Type type = typeof(TypeOffset); + FieldInfo[] fi = type.GetFields(); + int size = IntPtr.Size; + for (int i = 0; i < fi.Length; i++) { + fi[i].SetValue(null, i * size); + } + } + + public static int magic() { + return ob_size; + } + +/* The *real* layout of a type object when allocated on the heap */ +//typedef struct _heaptypeobject { +#if (Py_DEBUG) // #ifdef Py_TRACE_REFS +/* _PyObject_HEAD_EXTRA defines pointers to support a doubly-linked list of all live heap objects. */ + public static int _ob_next = 0; + public static int _ob_prev = 0; +#endif +// PyObject_VAR_HEAD { +// PyObject_HEAD { + public static int ob_refcnt = 0; + public static int ob_type = 0; + // } + public static int ob_size = 0; /* Number of items in _VAR_iable part */ +// } + public static int tp_name = 0; /* For printing, in format "." */ + public static int tp_basicsize = 0; /* For allocation */ + public static int tp_itemsize = 0; + + /* Methods to implement standard operations */ + public static int tp_dealloc = 0; + public static int tp_print = 0; + public static int tp_getattr = 0; + public static int tp_setattr = 0; + public static int tp_compare = 0; + public static int tp_repr = 0; + + /* Method suites for standard classes */ + public static int tp_as_number = 0; + public static int tp_as_sequence = 0; + public static int tp_as_mapping = 0; + + /* More standard operations (here for binary compatibility) */ + public static int tp_hash = 0; + public static int tp_call = 0; + public static int tp_str = 0; + public static int tp_getattro = 0; + public static int tp_setattro = 0; + + /* Functions to access object as input/output buffer */ + public static int tp_as_buffer = 0; + + /* Flags to define presence of optional/expanded features */ + public static int tp_flags = 0; + + public static int tp_doc = 0; /* Documentation string */ + + /* Assigned meaning in release 2.0 */ + /* call function for all accessible objects */ + public static int tp_traverse = 0; + /* delete references to contained objects */ + public static int tp_clear = 0; + + /* Assigned meaning in release 2.1 */ + /* rich comparisons */ + public static int tp_richcompare = 0; + /* weak reference enabler */ + public static int tp_weaklistoffset = 0; + + /* Added in release 2.2 */ + /* Iterators */ + public static int tp_iter = 0; + public static int tp_iternext = 0; + /* Attribute descriptor and subclassing stuff */ + public static int tp_methods = 0; + public static int tp_members = 0; + public static int tp_getset = 0; + public static int tp_base = 0; + public static int tp_dict = 0; + public static int tp_descr_get = 0; + public static int tp_descr_set = 0; + public static int tp_dictoffset = 0; + public static int tp_init = 0; + public static int tp_alloc = 0; + public static int tp_new = 0; + public static int tp_free = 0; /* Low-level free-memory routine */ + public static int tp_is_gc = 0; /* For PyObject_IS_GC */ + public static int tp_bases = 0; + public static int tp_mro = 0; /* method resolution order */ + public static int tp_cache = 0; + public static int tp_subclasses = 0; + public static int tp_weaklist = 0; + public static int tp_del = 0; +#if (PYTHON26 || PYTHON27) + /* Type attribute cache version tag. Added in version 2.6 */ + public static int tp_version_tag; +#endif + // COUNT_ALLOCS adds some more stuff to PyTypeObject +#if (Py_COUNT_ALLOCS) + /* these must be last and never explicitly initialized */ + public static int tp_allocs = 0; + public static int tp_frees = 0; + public static int tp_maxalloc = 0; + public static int tp_prev = 0; + public static int tp_next = 0; +#endif +//} PyTypeObject; +//typedef struct { + public static int nb_add = 0; + public static int nb_subtract = 0; + public static int nb_multiply = 0; + public static int nb_divide = 0; + public static int nb_remainder = 0; + public static int nb_divmod = 0; + public static int nb_power = 0; + public static int nb_negative = 0; + public static int nb_positive = 0; + public static int nb_absolute = 0; + public static int nb_nonzero = 0; + public static int nb_invert = 0; + public static int nb_lshift = 0; + public static int nb_rshift = 0; + public static int nb_and = 0; + public static int nb_xor = 0; + public static int nb_or = 0; + public static int nb_coerce = 0; + public static int nb_int = 0; + public static int nb_long = 0; + public static int nb_float = 0; + public static int nb_oct = 0; + public static int nb_hex = 0; + /* Added in release 2.0 */ + public static int nb_inplace_add = 0; + public static int nb_inplace_subtract = 0; + public static int nb_inplace_multiply = 0; + public static int nb_inplace_divide = 0; + public static int nb_inplace_remainder = 0; + public static int nb_inplace_power = 0; + public static int nb_inplace_lshift = 0; + public static int nb_inplace_rshift = 0; + public static int nb_inplace_and = 0; + public static int nb_inplace_xor = 0; + public static int nb_inplace_or = 0; + /* Added in release 2.2 */ + /* The following require the Py_TPFLAGS_HAVE_CLASS flag */ + public static int nb_floor_divide = 0; + public static int nb_true_divide = 0; + public static int nb_inplace_floor_divide = 0; + public static int nb_inplace_true_divide = 0; +#if (PYTHON25 || PYTHON26 || PYTHON27) + /* Added in release 2.5 */ + public static int nb_index = 0; +#endif + //} PyNumberMethods; +//typedef struct { + public static int mp_length = 0; + public static int mp_subscript = 0; + public static int mp_ass_subscript = 0; +//} PyMappingMethods; +//typedef struct { + public static int sq_length = 0; + public static int sq_concat = 0; + public static int sq_repeat = 0; + public static int sq_item = 0; + public static int sq_slice = 0; + public static int sq_ass_item = 0; + public static int sq_ass_slice = 0; + public static int sq_contains = 0; + /* Added in release 2.0 */ + public static int sq_inplace_concat = 0; + public static int sq_inplace_repeat = 0; +//} PySequenceMethods; +//typedef struct { + public static int bf_getreadbuffer = 0; + public static int bf_getwritebuffer = 0; + public static int bf_getsegcount = 0; + public static int bf_getcharbuffer = 0; +#if (PYTHON26 || PYTHON27) + // This addition is not actually noted in the 2.6.5 object.h + public static int bf_getbuffer = 0; + public static int bf_releasebuffer = 0; +//} PyBufferProcs; +#endif + //PyObject *ht_name, *ht_slots; + public static int name = 0; + public static int slots = 0; + /* here are optional user slots, followed by the members. */ + public static int members = 0; + } + + /// + /// TypeFlags(): The actual bit values for the Type Flags stored + /// in a class. + /// Note that the two values reserved for stackless have been put + /// to good use as PythonNet specific flags (Managed and Subclass) + /// + internal class TypeFlags { + public static int HaveGetCharBuffer = (1 << 0); + public static int HaveSequenceIn = (1 << 1); + public static int GC = 0; + public static int HaveInPlaceOps = (1 << 3); + public static int CheckTypes = (1 << 4); + public static int HaveRichCompare = (1 << 5); + public static int HaveWeakRefs = (1 << 6); + public static int HaveIter = (1 << 7); + public static int HaveClass = (1 << 8); + public static int HeapType = (1 << 9); + public static int BaseType = (1 << 10); + public static int Ready = (1 << 12); + public static int Readying = (1 << 13); + public static int HaveGC = (1 << 14); + // 15 and 16 are reserved for stackless + public static int HaveStacklessExtension = 0; + /* XXX Reusing reserved constants */ + public static int Managed = (1 << 15); // PythonNet specific + public static int Subclass = (1 << 16); // PythonNet specific +#if (PYTHON25 || PYTHON26 || PYTHON27) + public static int HaveIndex = (1 << 17); +#endif +#if (PYTHON26 || PYTHON27) + /* Objects support nb_index in PyNumberMethods */ + public static int HaveVersionTag = (1 << 18); + public static int ValidVersionTag = (1 << 19); + public static int IsAbstract = (1 << 20); + public static int HaveNewBuffer = (1 << 21); + // TODO: Implement FastSubclass functions + public static int IntSubclass = (1 << 23); + public static int LongSubclass = (1 << 24); + public static int ListSubclass = (1 << 25); + public static int TupleSubclass = (1 << 26); + public static int StringSubclass = (1 << 27); + public static int UnicodeSubclass = (1 << 28); + public static int DictSubclass = (1 << 29); + public static int BaseExceptionSubclass = (1 << 30); + public static int TypeSubclass = (1 << 31); +#endif + public static int Default = (HaveGetCharBuffer | + HaveSequenceIn | + HaveInPlaceOps | + HaveRichCompare | + HaveWeakRefs | + HaveIter | + HaveClass | + HaveStacklessExtension | +#if (PYTHON25 || PYTHON26 || PYTHON27) + HaveIndex | +#endif + 0); + } + + + // This class defines the function prototypes (delegates) used for low + // level integration with the CPython runtime. It also provides name + // based lookup of the correct prototype for a particular Python type + // slot and utilities for generating method thunks for managed methods. + + internal class Interop { + + static ArrayList keepAlive; + static Hashtable pmap; + + static Interop() { + + // Here we build a mapping of PyTypeObject slot names to the + // appropriate prototype (delegate) type to use for the slot. + + Type[] items = typeof(Interop).GetNestedTypes(); + Hashtable p = new Hashtable(); + + for (int i = 0; i < items.Length; i++) { + Type item = items[i]; + p[item.Name] = item; + } + + keepAlive = new ArrayList(); + Marshal.AllocHGlobal(IntPtr.Size); + pmap = new Hashtable(); + + pmap["tp_dealloc"] = p["DestructorFunc"]; + pmap["tp_print"] = p["PrintFunc"]; + pmap["tp_getattr"] = p["BinaryFunc"]; + pmap["tp_setattr"] = p["ObjObjArgFunc"]; + pmap["tp_compare"] = p["ObjObjFunc"]; + pmap["tp_repr"] = p["UnaryFunc"]; + pmap["tp_hash"] = p["UnaryFunc"]; + pmap["tp_call"] = p["TernaryFunc"]; + pmap["tp_str"] = p["UnaryFunc"]; + pmap["tp_getattro"] = p["BinaryFunc"]; + pmap["tp_setattro"] = p["ObjObjArgFunc"]; + pmap["tp_traverse"] = p["ObjObjArgFunc"]; + pmap["tp_clear"] = p["InquiryFunc"]; + pmap["tp_richcompare"] = p["RichCmpFunc"]; + pmap["tp_iter"] = p["UnaryFunc"]; + pmap["tp_iternext"] = p["UnaryFunc"]; + pmap["tp_descr_get"] = p["TernaryFunc"]; + pmap["tp_descr_set"] = p["ObjObjArgFunc"]; + pmap["tp_init"] = p["ObjObjArgFunc"]; + pmap["tp_alloc"] = p["IntArgFunc"]; + pmap["tp_new"] = p["TernaryFunc"]; + pmap["tp_free"] = p["DestructorFunc"]; + pmap["tp_is_gc"] = p["InquiryFunc"]; + + pmap["nb_add"] = p["BinaryFunc"]; + pmap["nb_subtract"] = p["BinaryFunc"]; + pmap["nb_multiply"] = p["BinaryFunc"]; + pmap["nb_divide"] = p["BinaryFunc"]; + pmap["nb_remainder"] = p["BinaryFunc"]; + pmap["nb_divmod"] = p["BinaryFunc"]; + pmap["nb_power"] = p["TernaryFunc"]; + pmap["nb_negative"] = p["UnaryFunc"]; + pmap["nb_positive"] = p["UnaryFunc"]; + pmap["nb_absolute"] = p["UnaryFunc"]; + pmap["nb_nonzero"] = p["InquiryFunc"]; + pmap["nb_invert"] = p["UnaryFunc"]; + pmap["nb_lshift"] = p["BinaryFunc"]; + pmap["nb_rshift"] = p["BinaryFunc"]; + pmap["nb_and"] = p["BinaryFunc"]; + pmap["nb_xor"] = p["BinaryFunc"]; + pmap["nb_or"] = p["BinaryFunc"]; + pmap["nb_coerce"] = p["ObjObjFunc"]; + pmap["nb_int"] = p["UnaryFunc"]; + pmap["nb_long"] = p["UnaryFunc"]; + pmap["nb_float"] = p["UnaryFunc"]; + pmap["nb_oct"] = p["UnaryFunc"]; + pmap["nb_hex"] = p["UnaryFunc"]; + pmap["nb_inplace_add"] = p["BinaryFunc"]; + pmap["nb_inplace_subtract"] = p["BinaryFunc"]; + pmap["nb_inplace_multiply"] = p["BinaryFunc"]; + pmap["nb_inplace_divide"] = p["BinaryFunc"]; + pmap["nb_inplace_remainder"] = p["BinaryFunc"]; + pmap["nb_inplace_power"] = p["TernaryFunc"]; + pmap["nb_inplace_lshift"] = p["BinaryFunc"]; + pmap["nb_inplace_rshift"] = p["BinaryFunc"]; + pmap["nb_inplace_and"] = p["BinaryFunc"]; + pmap["nb_inplace_xor"] = p["BinaryFunc"]; + pmap["nb_inplace_or"] = p["BinaryFunc"]; + pmap["nb_floor_divide"] = p["BinaryFunc"]; + pmap["nb_true_divide"] = p["BinaryFunc"]; + pmap["nb_inplace_floor_divide"] = p["BinaryFunc"]; + pmap["nb_inplace_true_divide"] = p["BinaryFunc"]; +#if (PYTHON25 || PYTHON26 || PYTHON27) + pmap["nb_index"] = p["UnaryFunc"]; +#endif + + pmap["sq_length"] = p["InquiryFunc"]; + pmap["sq_concat"] = p["BinaryFunc"]; + pmap["sq_repeat"] = p["IntArgFunc"]; + pmap["sq_item"] = p["IntArgFunc"]; + pmap["sq_slice"] = p["IntIntArgFunc"]; + pmap["sq_ass_item"] = p["IntObjArgFunc"]; + pmap["sq_ass_slice"] = p["IntIntObjArgFunc"]; + pmap["sq_contains"] = p["ObjObjFunc"]; + pmap["sq_inplace_concat"] = p["BinaryFunc"]; + pmap["sq_inplace_repeat"] = p["IntArgFunc"]; + + pmap["mp_length"] = p["InquiryFunc"]; + pmap["mp_subscript"] = p["BinaryFunc"]; + pmap["mp_ass_subscript"] = p["ObjObjArgFunc"]; + + pmap["bf_getreadbuffer"] = p["IntObjArgFunc"]; + pmap["bf_getwritebuffer"] = p["IntObjArgFunc"]; + pmap["bf_getsegcount"] = p["ObjObjFunc"]; + pmap["bf_getcharbuffer"] = p["IntObjArgFunc"]; + + pmap["__import__"] = p["TernaryFunc"]; + } + + internal static Type GetPrototype(string name) { + return pmap[name] as Type; + } + + internal static IntPtr GetThunk(MethodInfo method) { + Type dt = Interop.GetPrototype(method.Name); + if (dt != null) { + IntPtr tmp = Marshal.AllocHGlobal(IntPtr.Size); + Delegate d = Delegate.CreateDelegate(dt, method); + Thunk cb = new Thunk(d); + Marshal.StructureToPtr(cb, tmp, false); + IntPtr fp = Marshal.ReadIntPtr(tmp, 0); + Marshal.FreeHGlobal(tmp); + keepAlive.Add(d); + return fp; + } + return IntPtr.Zero; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr UnaryFunc(IntPtr ob); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr BinaryFunc(IntPtr ob, IntPtr arg); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr TernaryFunc(IntPtr ob, IntPtr a1, IntPtr a2); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int InquiryFunc(IntPtr ob); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr IntArgFunc(IntPtr ob, int arg); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr IntIntArgFunc(IntPtr ob, int a1, int a2); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int IntObjArgFunc(IntPtr ob, int a1, IntPtr a2); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int IntIntObjArgFunc(IntPtr o, int a, int b, IntPtr c); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int ObjObjArgFunc(IntPtr o, IntPtr a, IntPtr b); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int ObjObjFunc(IntPtr ob, IntPtr arg); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void DestructorFunc(IntPtr ob); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int PrintFunc(IntPtr ob, IntPtr a, int b); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate IntPtr RichCmpFunc(IntPtr ob, IntPtr a, int b); + + } + + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] + internal struct Thunk { + public Delegate fn; + + public Thunk(Delegate d) { + fn = d; + } + } + +} diff --git a/Pythonnet.Runtime/iterator.cs b/Pythonnet.Runtime/iterator.cs new file mode 100644 index 0000000000..3d34760c39 --- /dev/null +++ b/Pythonnet.Runtime/iterator.cs @@ -0,0 +1,52 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // Implements a generic Python iterator for IEnumerable objects and + // managed array objects. This supports 'for i in object:' in Python. + //======================================================================== + + internal class Iterator : ExtensionType { + + IEnumerator iter; + + public Iterator(IEnumerator e) : base() { + this.iter = e; + } + + + //==================================================================== + // Implements support for the Python iteration protocol. + //==================================================================== + + public static IntPtr tp_iternext(IntPtr ob) { + Iterator self = GetManagedObject(ob) as Iterator; + if (!self.iter.MoveNext()) { + Exceptions.SetError(Exceptions.StopIteration, Runtime.PyNone); + return IntPtr.Zero; + } + object item = self.iter.Current; + return Converter.ToPythonImplicit(item); + } + + public static IntPtr tp_iter(IntPtr ob) { + Runtime.Incref(ob); + return ob; + } + + } + + +} diff --git a/Pythonnet.Runtime/managedtype.cs b/Pythonnet.Runtime/managedtype.cs new file mode 100644 index 0000000000..670bcd2b36 --- /dev/null +++ b/Pythonnet.Runtime/managedtype.cs @@ -0,0 +1,97 @@ +// ========================================================================== +// 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.Runtime.InteropServices; +using System.Collections; +using System.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // Common base class for all objects that are implemented in managed + // code. It defines the common fields that associate CLR and Python + // objects and common utilities to convert between those identities. + //======================================================================== + + internal abstract class ManagedType { + + internal GCHandle gcHandle; // Native handle + internal IntPtr pyHandle; // PyObject * + internal IntPtr tpHandle; // PyType * + + + //==================================================================== + // Given a Python object, return the associated managed object or null. + //==================================================================== + + internal static ManagedType GetManagedObject(IntPtr ob) { + if (ob != IntPtr.Zero) { + IntPtr tp = Runtime.PyObject_TYPE(ob); + if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType) { + tp = ob; + } + + int flags = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_flags); + if ((flags & TypeFlags.Managed) != 0) { + IntPtr op = (tp == ob) ? + Marshal.ReadIntPtr(tp, TypeOffset.magic()) : + Marshal.ReadIntPtr(ob, ObjectOffset.magic()); + GCHandle gc = (GCHandle)op; + return (ManagedType)gc.Target; + } + + // In certain situations, we need to recognize a wrapped + // exception class and be willing to unwrap the class :( + + if (Runtime.wrap_exceptions) { + IntPtr e = Exceptions.UnwrapExceptionClass(ob); + if ((e != IntPtr.Zero) && (e != ob)) { + ManagedType m = GetManagedObject(e); + Runtime.Decref(e); + return m; + } + } + } + return null; + } + + + internal static ManagedType GetManagedObjectErr(IntPtr ob) { + ManagedType result = GetManagedObject(ob); + if (result == null) { + Exceptions.SetError(Exceptions.TypeError, + "invalid argument, expected CLR type" + ); + } + return result; + } + + + internal static bool IsManagedType(IntPtr ob) { + if (ob != IntPtr.Zero) { + IntPtr tp = Runtime.PyObject_TYPE(ob); + if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType) { + tp = ob; + } + + int flags = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_flags); + if ((flags & TypeFlags.Managed) != 0) { + return true; + } + } + return false; + } + + + } + + +} + diff --git a/Pythonnet.Runtime/metatype.cs b/Pythonnet.Runtime/metatype.cs new file mode 100644 index 0000000000..305437c84e --- /dev/null +++ b/Pythonnet.Runtime/metatype.cs @@ -0,0 +1,265 @@ +// ========================================================================== +// 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.Runtime.InteropServices; +using System.Collections; +using System.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // The managed metatype. This object implements the type of all reflected + // types. It also provides support for single-inheritance from reflected + // managed types. + //======================================================================== + + internal class MetaType : ManagedType { + + static IntPtr PyCLRMetaType; + + + //==================================================================== + // Metatype initialization. This bootstraps the CLR metatype to life. + //==================================================================== + + public static IntPtr Initialize() { + PyCLRMetaType = TypeManager.CreateMetaType(typeof(MetaType)); + return PyCLRMetaType; + } + + + //==================================================================== + // Metatype __new__ implementation. This is called to create a new + // class / type when a reflected class is subclassed. + //==================================================================== + + public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { + int len = Runtime.PyTuple_Size(args); + if (len < 3) { + return Exceptions.RaiseTypeError("invalid argument list"); + } + + //IntPtr name = Runtime.PyTuple_GetItem(args, 0); + IntPtr bases = Runtime.PyTuple_GetItem(args, 1); + IntPtr dict = Runtime.PyTuple_GetItem(args, 2); + + // We do not support multiple inheritance, so the bases argument + // should be a 1-item tuple containing the type we are subtyping. + // That type must itself have a managed implementation. We check + // that by making sure its metatype is the CLR metatype. + + if (Runtime.PyTuple_Size(bases) != 1) { + return Exceptions.RaiseTypeError( + "cannot use multiple inheritance with managed classes" + ); + + } + + IntPtr base_type = Runtime.PyTuple_GetItem(bases, 0); + IntPtr mt = Runtime.PyObject_TYPE(base_type); + + if (!((mt == PyCLRMetaType) || (mt == Runtime.PyTypeType))) { + return Exceptions.RaiseTypeError("invalid metatype"); + } + + // Ensure that the reflected type is appropriate for subclassing, + // disallowing subclassing of delegates, enums and array types. + + ClassBase cb = GetManagedObject(base_type) as ClassBase; + if (cb != null) { + if (! cb.CanSubclass() ) { + return Exceptions.RaiseTypeError( + "delegates, enums and array types cannot be subclassed" + ); + } + } + + IntPtr slots = Runtime.PyDict_GetItemString(dict, "__slots__"); + if (slots != IntPtr.Zero) { + return Exceptions.RaiseTypeError( + "subclasses of managed classes do not support __slots__" + ); + } + + // hack for now... fix for 1.0 + //return TypeManager.CreateSubType(args); + + + // right way + + IntPtr func = Marshal.ReadIntPtr(Runtime.PyTypeType, + TypeOffset.tp_new); + IntPtr type = NativeCall.Call_3(func, tp, args, kw); + if (type == IntPtr.Zero) { + return IntPtr.Zero; + } + + int flags = TypeFlags.Default; + flags |= TypeFlags.Managed; + flags |= TypeFlags.HeapType; + flags |= TypeFlags.BaseType; + flags |= TypeFlags.Subclass; + flags |= TypeFlags.HaveGC; + Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + + TypeManager.CopySlot(base_type, type, TypeOffset.tp_dealloc); + + // Hmm - the standard subtype_traverse, clear look at ob_size to + // do things, so to allow gc to work correctly we need to move + // our hidden handle out of ob_size. Then, in theory we can + // comment this out and still not crash. + TypeManager.CopySlot(base_type, type, TypeOffset.tp_traverse); + TypeManager.CopySlot(base_type, type, TypeOffset.tp_clear); + + + // for now, move up hidden handle... + IntPtr gc = Marshal.ReadIntPtr(base_type, TypeOffset.magic()); + Marshal.WriteIntPtr(type, TypeOffset.magic(), gc); + + //DebugUtil.DumpType(base_type); + //DebugUtil.DumpType(type); + + return type; + } + + + public static IntPtr tp_alloc(IntPtr mt, int n) { + IntPtr type = Runtime.PyType_GenericAlloc(mt, n); + return type; + } + + + public static void tp_free(IntPtr tp) { + Runtime.PyObject_GC_Del(tp); + } + + + //==================================================================== + // Metatype __call__ implementation. This is needed to ensure correct + // initialization (__init__ support), because the tp_call we inherit + // from PyType_Type won't call __init__ for metatypes it doesnt know. + //==================================================================== + + public static IntPtr tp_call(IntPtr tp, IntPtr args, IntPtr kw) { + IntPtr func = Marshal.ReadIntPtr(tp, TypeOffset.tp_new); + if (func == IntPtr.Zero) { + return Exceptions.RaiseTypeError("invalid object"); + } + + IntPtr obj = NativeCall.Call_3(func, tp, args, kw); + if (obj == IntPtr.Zero) { + return IntPtr.Zero; + } + + IntPtr py__init__ = Runtime.PyString_FromString("__init__"); + IntPtr type = Runtime.PyObject_TYPE(obj); + IntPtr init = Runtime._PyType_Lookup(type, py__init__); + Runtime.Decref(py__init__); + Runtime.PyErr_Clear(); + + if (init != IntPtr.Zero) { + IntPtr bound = Runtime.GetBoundArgTuple(obj, args); + if (bound == IntPtr.Zero) { + Runtime.Decref(obj); + return IntPtr.Zero; + } + + IntPtr result = Runtime.PyObject_Call(init, bound, kw); + Runtime.Decref(bound); + + if (result == IntPtr.Zero) { + Runtime.Decref(obj); + return IntPtr.Zero; + } + + Runtime.Decref(result); + } + + return obj; + } + + + //==================================================================== + // Type __setattr__ implementation for reflected types. Note that this + // is slightly different than the standard setattr implementation for + // the normal Python metatype (PyTypeType). We need to look first in + // the type object of a reflected type for a descriptor in order to + // support the right setattr behavior for static fields and properties. + //==================================================================== + + public static int tp_setattro(IntPtr tp, IntPtr name, IntPtr value) { + IntPtr descr = Runtime._PyType_Lookup(tp, name); + + if (descr != IntPtr.Zero) { + IntPtr dt = Runtime.PyObject_TYPE(descr); + IntPtr fp = Marshal.ReadIntPtr(dt, TypeOffset.tp_descr_set); + if (fp != IntPtr.Zero) { + return NativeCall.Impl.Int_Call_3(fp, descr, name, value); + } + Exceptions.SetError(Exceptions.AttributeError, + "attribute is read-only"); + return -1; + } + + if (Runtime.PyObject_GenericSetAttr(tp, name, value) < 0) { + return -1; + } + + return 0; + } + + //==================================================================== + // The metatype has to implement [] semantics for generic types, so + // here we just delegate to the generic type def implementation. Its + // own mp_subscript + //==================================================================== + public static IntPtr mp_subscript(IntPtr tp, IntPtr idx) { + ClassBase cb = GetManagedObject(tp) as ClassBase; + if (cb != null) { + return cb.type_subscript(idx); + } + return Exceptions.RaiseTypeError("unsubscriptable object"); + } + + //==================================================================== + // Dealloc implementation. This is called when a Python type generated + // by this metatype is no longer referenced from the Python runtime. + //==================================================================== + + public static void tp_dealloc(IntPtr tp) { + // Fix this when we dont cheat on the handle for subclasses! + + int flags = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_flags); + if ((flags & TypeFlags.Subclass) == 0) { + IntPtr gc = Marshal.ReadIntPtr(tp, TypeOffset.magic()); + ((GCHandle)gc).Free(); + } + + IntPtr op = Marshal.ReadIntPtr(tp, TypeOffset.ob_type); + Runtime.Decref(op); + + // Delegate the rest of finalization the Python metatype. Note + // that the PyType_Type implementation of tp_dealloc will call + // tp_free on the type of the type being deallocated - in this + // case our CLR metatype. That is why we implement tp_free. + + op = Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_dealloc); + NativeCall.Void_Call_1(op, tp); + + return; + } + + + + + } + + +} diff --git a/Pythonnet.Runtime/methodbinder.cs b/Pythonnet.Runtime/methodbinder.cs new file mode 100644 index 0000000000..80d3968fd4 --- /dev/null +++ b/Pythonnet.Runtime/methodbinder.cs @@ -0,0 +1,465 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // A MethodBinder encapsulates information about a (possibly overloaded) + // managed method, and is responsible for selecting the right method given + // a set of Python arguments. This is also used as a base class for the + // ConstructorBinder, a minor variation used to invoke constructors. + //======================================================================== + + internal class MethodBinder { + + public ArrayList list; + public MethodBase[] methods; + public bool init = false; + public bool allow_threads = true; + + internal MethodBinder () { + this.list = new ArrayList(); + } + + internal MethodBinder(MethodInfo mi) : base () { + this.list = new ArrayList(); + this.list.Add(mi); + } + + public int Count { + get { return this.list.Count; } + } + + internal void AddMethod(MethodBase m) { + this.list.Add(m); + } + + //==================================================================== + // Given a sequence of MethodInfo and a sequence of types, return the + // MethodInfo that matches the signature represented by those types. + //==================================================================== + + internal static MethodInfo MatchSignature(MethodInfo[] mi, Type[] tp) { + int count = tp.Length; + for (int i = 0; i < mi.Length; i++) { + ParameterInfo[] pi = mi[i].GetParameters(); + if (pi.Length != count) { + continue; + } + for (int n = 0; n < pi.Length; n++) { + if (tp[n]!= pi[n].ParameterType) { + break; + } + if (n == (pi.Length - 1)) { + return mi[i]; + } + } + } + return null; + } + + //==================================================================== + // Given a sequence of MethodInfo and a sequence of type parameters, + // return the MethodInfo that represents the matching closed generic. + //==================================================================== + + internal static MethodInfo MatchParameters(MethodInfo[] mi,Type[] tp) { + int count = tp.Length; + for (int i = 0; i < mi.Length; i++) { + if (!mi[i].IsGenericMethodDefinition) { + continue; + } + Type[] args = mi[i].GetGenericArguments(); + if (args.Length != count) { + continue; + } + return mi[i].MakeGenericMethod(tp); + } + return null; + } + + + //==================================================================== + // Given a sequence of MethodInfo and two sequences of type parameters, + // return the MethodInfo that matches the signature and the closed generic. + //==================================================================== + + internal static MethodInfo MatchSignatureAndParameters(MethodInfo[] mi, Type[] genericTp, Type[] sigTp) + { + int genericCount = genericTp.Length; + int signatureCount = sigTp.Length; + for (int i = 0; i < mi.Length; i++) + { + if (!mi[i].IsGenericMethodDefinition) + { + continue; + } + Type[] genericArgs = mi[i].GetGenericArguments(); + if (genericArgs.Length != genericCount) + { + continue; + } + ParameterInfo[] pi = mi[i].GetParameters(); + if (pi.Length != signatureCount) + { + continue; + } + for (int n = 0; n < pi.Length; n++) + { + if (sigTp[n] != pi[n].ParameterType) + { + break; + } + if (n == (pi.Length - 1)) + { + MethodInfo match = mi[i]; + if (match.IsGenericMethodDefinition) + { + Type[] typeArgs = match.GetGenericArguments(); + return match.MakeGenericMethod(genericTp); + } + return match; + } + } + } + return null; + } + + + //==================================================================== + // Return the array of MethodInfo for this method. The result array + // is arranged in order of precendence (done lazily to avoid doing it + // at all for methods that are never called). + //==================================================================== + + internal MethodBase[] GetMethods() { + if (!init) { + // I'm sure this could be made more efficient. + list.Sort(new MethodSorter()); + methods = (MethodBase[])list.ToArray(typeof(MethodBase)); + init = true; + } + return methods; + } + + //==================================================================== + // Precedence algorithm largely lifted from jython - the concerns are + // generally the same so we'll start w/this and tweak as necessary. + //==================================================================== + + internal static int GetPrecedence(MethodBase mi) { + ParameterInfo[] pi = mi.GetParameters(); + int val = mi.IsStatic ? 3000 : 0; + int num = pi.Length; + + val += (mi.IsGenericMethod ? 1 : 0); + for (int i = 0; i < num; i++) { + val += ArgPrecedence(pi[i].ParameterType); + } + + return val; + } + + //==================================================================== + // Return a precedence value for a particular Type object. + //==================================================================== + + internal static int ArgPrecedence(Type t) { + Type objectType = typeof(Object); + if (t == objectType) return 3000; + + TypeCode tc = Type.GetTypeCode(t); + if (tc == TypeCode.Object) return 1; + if (tc == TypeCode.UInt64) return 10; + if (tc == TypeCode.UInt32) return 11; + if (tc == TypeCode.UInt16) return 12; + if (tc == TypeCode.Int64) return 13; + if (tc == TypeCode.Int32) return 14; + if (tc == TypeCode.Int16) return 15; + if (tc == TypeCode.Char) return 16; + if (tc == TypeCode.SByte) return 17; + if (tc == TypeCode.Byte) return 18; + if (tc == TypeCode.Single) return 20; + if (tc == TypeCode.Double) return 21; + if (tc == TypeCode.String) return 30; + if (tc == TypeCode.Boolean) return 40; + + if (t.IsArray) { + Type e = t.GetElementType(); + if (e == objectType) + return 2500; + return 100 + ArgPrecedence(e); + } + + return 2000; + } + + //==================================================================== + // Bind the given Python instance and arguments to a particular method + // overload and return a structure that contains the converted Python + // instance, converted arguments and the correct method to call. + //==================================================================== + + internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw) { + return this.Bind(inst, args, kw, null, null); + } + + internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, + MethodBase info) { + return this.Bind(inst, args, kw, info, null); + } + + internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, + MethodBase info, MethodInfo[] methodinfo) { + // loop to find match, return invoker w/ or /wo error + MethodBase[] _methods = null; + int pynargs = Runtime.PyTuple_Size(args); + object arg; + bool isGeneric = false; + + if (info != null) { + _methods = (MethodBase[])Array.CreateInstance( + typeof(MethodBase), 1 + ); + _methods.SetValue(info, 0); + } + else { + _methods = GetMethods(); + } + + for (int i = 0; i < _methods.Length; i++) { + MethodBase mi = _methods[i]; + if (mi.IsGenericMethod) { isGeneric = true; } + ParameterInfo[] pi = mi.GetParameters(); + int clrnargs = pi.Length; + bool match = false; + int arrayStart = -1; + int outs = 0; + + if (pynargs == clrnargs) { + match = true; + } else if ((pynargs > clrnargs) && (clrnargs > 0) && + (pi[clrnargs-1].ParameterType.IsArray)) { + // The last argument of the mananged functions seems to + // accept multiple arguments as a array. Hopefully it's a + // spam(params object[] egg) style method + match = true; + arrayStart = clrnargs - 1; + } + + if (match) { + Object[] margs = new Object[clrnargs]; + + for (int n = 0; n < clrnargs; n++) { + IntPtr op; + if (arrayStart == n) { + // map remaining Python arguments to a tuple since + // the managed function accepts it - hopefully :] + op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs); + } + else { + op = Runtime.PyTuple_GetItem(args, n); + } + Type type = pi[n].ParameterType; + if (pi[n].IsOut || type.IsByRef) { + outs++; + } + + if (!Converter.ToManaged(op, type, out arg, false)) { + Exceptions.Clear(); + margs = null; + break; + } + if (arrayStart == n) { + // GetSlice() creates a new reference but GetItem() + // returns only a borrow reference. + Runtime.Decref(op); + } + margs[n] = arg; + } + + if (margs == null) { + continue; + } + + Object target = null; + if ((!mi.IsStatic) && (inst != IntPtr.Zero)) { + //CLRObject co = (CLRObject)ManagedType.GetManagedObject(inst); + // InvalidCastException: Unable to cast object of type + // 'Python.Runtime.ClassObject' to type 'Python.Runtime.CLRObject' + CLRObject co = ManagedType.GetManagedObject(inst) as CLRObject; + + // Sanity check: this ensures a graceful exit if someone does + // something intentionally wrong like call a non-static method + // on the class rather than on an instance of the class. + // XXX maybe better to do this before all the other rigmarole. + if (co == null) { + return null; + } + target = co.inst; + } + + return new Binding(mi, target, margs, outs); + } + } + // We weren't able to find a matching method but at least one + // is a generic method and info is null. That happens when a generic + // method was not called using the [] syntax. Let's introspect the + // type of the arguments and use it to construct the correct method. + if (isGeneric && (info == null) && (methodinfo != null)) + { + Type[] types = Runtime.PythonArgsToTypeArray(args, true); + MethodInfo mi = MethodBinder.MatchParameters(methodinfo, types); + return Bind(inst, args, kw, mi, null); + } + return null; + } + + internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) { + return this.Invoke(inst, args, kw, null, null); + + } + + internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, + MethodBase info) { + return this.Invoke(inst, args, kw, info, null); + } + + internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, + MethodBase info, MethodInfo[] methodinfo) { + Binding binding = this.Bind(inst, args, kw, info, methodinfo); + Object result; + IntPtr ts = IntPtr.Zero; + + if (binding == null) { + Exceptions.SetError(Exceptions.TypeError, + "No method matches given arguments" + ); + return IntPtr.Zero; + } + + if (allow_threads) { + ts = PythonEngine.BeginAllowThreads(); + } + + try { + result = binding.info.Invoke(binding.inst, + BindingFlags.Default, + null, + binding.args, + null); + } + catch (Exception e) { + if (e.InnerException != null) { + e = e.InnerException; + } + if (allow_threads) { + PythonEngine.EndAllowThreads(ts); + } + Exceptions.SetError(e); + return IntPtr.Zero; + } + + if (allow_threads) { + PythonEngine.EndAllowThreads(ts); + } + + // If there are out parameters, we return a tuple containing + // the result followed by the out parameters. If there is only + // one out parameter and the return type of the method is void, + // we return the out parameter as the result to Python (for + // code compatibility with ironpython). + + MethodInfo mi = (MethodInfo)binding.info; + + if ((binding.outs == 1) && (mi.ReturnType == typeof(void))) { + + } + + if (binding.outs > 0) { + ParameterInfo[] pi = mi.GetParameters(); + int c = pi.Length; + int n = 0; + + IntPtr t = Runtime.PyTuple_New(binding.outs + 1); + IntPtr v = Converter.ToPython(result, mi.ReturnType); + Runtime.PyTuple_SetItem(t, n, v); + n++; + + for (int i=0; i < c; i++) { + Type pt = pi[i].ParameterType; + if (pi[i].IsOut || pt.IsByRef) { + v = Converter.ToPython(binding.args[i], pt); + Runtime.PyTuple_SetItem(t, n, v); + n++; + } + } + + if ((binding.outs == 1) && (mi.ReturnType == typeof(void))) { + v = Runtime.PyTuple_GetItem(t, 1); + Runtime.Incref(v); + Runtime.Decref(t); + return v; + } + + return t; + } + + return Converter.ToPython(result, mi.ReturnType); + } + + } + + + + //======================================================================== + // Utility class to sort method info by parameter type precedence. + //======================================================================== + + internal class MethodSorter : IComparer { + + int IComparer.Compare(Object m1, Object m2) { + int p1 = MethodBinder.GetPrecedence((MethodBase)m1); + int p2 = MethodBinder.GetPrecedence((MethodBase)m2); + if (p1 < p2) return -1; + if (p1 > p2) return 1; + return 0; + } + + } + + + //======================================================================== + // A Binding is a utility instance that bundles together a MethodInfo + // representing a method to call, a (possibly null) target instance for + // the call, and the arguments for the call (all as managed values). + //======================================================================== + + internal class Binding { + + public MethodBase info; + public Object[] args; + public Object inst; + public int outs; + + internal Binding(MethodBase info, Object inst, Object[] args, + int outs) { + this.info = info; + this.inst = inst; + this.args = args; + this.outs = outs; + } + + } + +} diff --git a/Pythonnet.Runtime/methodbinding.cs b/Pythonnet.Runtime/methodbinding.cs new file mode 100644 index 0000000000..0459d36b29 --- /dev/null +++ b/Pythonnet.Runtime/methodbinding.cs @@ -0,0 +1,193 @@ +// ========================================================================== +// 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.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // Implements a Python binding type for CLR methods. These work much like + // standard Python method bindings, but the same type is used to bind + // both static and instance methods. + //======================================================================== + + internal class MethodBinding : ExtensionType { + + internal MethodInfo info; + internal MethodObject m; + internal IntPtr target; + + public MethodBinding(MethodObject m, IntPtr target) : base() { + Runtime.Incref(target); + this.target = target; + this.info = null; + this.m = m; + } + + //==================================================================== + // Implement binding of generic methods using the subscript syntax []. + //==================================================================== + + public static IntPtr mp_subscript(IntPtr tp, IntPtr idx) { + MethodBinding self = (MethodBinding)GetManagedObject(tp); + + Type[] types = Runtime.PythonArgsToTypeArray(idx); + if (types == null) { + return Exceptions.RaiseTypeError("type(s) expected"); + } + + MethodInfo mi = MethodBinder.MatchParameters(self.m.info, types); + if (mi == null) { + string e = "No match found for given type params"; + return Exceptions.RaiseTypeError(e); + } + + MethodBinding mb = new MethodBinding(self.m, self.target); + mb.info = mi; + Runtime.Incref(mb.pyHandle); + return mb.pyHandle; + } + + + //==================================================================== + // MethodBinding __getattribute__ implementation. + //==================================================================== + + public static IntPtr tp_getattro(IntPtr ob, IntPtr key) { + MethodBinding self = (MethodBinding)GetManagedObject(ob); + + if (!Runtime.PyString_Check(key)) { + Exceptions.SetError(Exceptions.TypeError, "string expected"); + return IntPtr.Zero; + } + + string name = Runtime.GetManagedString(key); + if (name == "__doc__") { + IntPtr doc = self.m.GetDocString(); + Runtime.Incref(doc); + return doc; + } + + // XXX deprecate __overloads__ soon... + if (name == "__overloads__" || name == "Overloads") { + OverloadMapper om = new OverloadMapper(self.m, self.target); + Runtime.Incref(om.pyHandle); + return om.pyHandle; + } + + return Runtime.PyObject_GenericGetAttr(ob, key); + } + + + //==================================================================== + // MethodBinding __call__ implementation. + //==================================================================== + + public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { + MethodBinding self = (MethodBinding)GetManagedObject(ob); + + // This works around a situation where the wrong generic method is picked, + // for example this method in the tests: string Overloaded(int arg1, int arg2, string arg3) + if (self.info != null) + { + if (self.info.IsGenericMethod) + { + int len = Runtime.PyTuple_Size(args); + Type[] sigTp = Runtime.PythonArgsToTypeArray(args, true); + if (sigTp != null) + { + Type[] genericTp = self.info.GetGenericArguments(); + MethodInfo betterMatch = MethodBinder.MatchSignatureAndParameters(self.m.info, genericTp, sigTp); + if (betterMatch != null) self.info = betterMatch; + } + } + } + + // This supports calling a method 'unbound', passing the instance + // as the first argument. Note that this is not supported if any + // of the overloads are static since we can't know if the intent + // was to call the static method or the unbound instance method. + + if ((self.target == IntPtr.Zero) && (!self.m.IsStatic())) + { + int len = Runtime.PyTuple_Size(args); + if (len < 1) + { + Exceptions.SetError(Exceptions.TypeError, "not enough arguments"); + return IntPtr.Zero; + } + IntPtr uargs = Runtime.PyTuple_GetSlice(args, 1, len); + IntPtr inst = Runtime.PyTuple_GetItem(args, 0); + Runtime.Incref(inst); + IntPtr r = self.m.Invoke(inst, uargs, kw, self.info); + Runtime.Decref(inst); + Runtime.Decref(uargs); + return r; + } + + return self.m.Invoke(self.target, args, kw, self.info); + } + + + //==================================================================== + // MethodBinding __hash__ implementation. + //==================================================================== + + public static IntPtr tp_hash(IntPtr ob) { + MethodBinding self = (MethodBinding)GetManagedObject(ob); + long x = 0; + long y = 0; + + if (self.target != IntPtr.Zero) { + x = Runtime.PyObject_Hash(self.target).ToInt64(); + if (x == -1) { + return new IntPtr(-1); + } + } + + y = Runtime.PyObject_Hash(self.m.pyHandle).ToInt64(); + if (y == -1) { + return new IntPtr(-1); + } + + x ^= y; + + if (x == -1) { + x = -1; + } + + return new IntPtr(x); + } + + //==================================================================== + // MethodBinding __repr__ implementation. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + MethodBinding self = (MethodBinding)GetManagedObject(ob); + string type = (self.target == IntPtr.Zero) ? "unbound" : "bound"; + string s = String.Format("<{0} method '{1}'>", type, self.m.name); + return Runtime.PyString_FromStringAndSize(s, s.Length); + } + + //==================================================================== + // MethodBinding dealloc implementation. + //==================================================================== + + public static new void tp_dealloc(IntPtr ob) { + MethodBinding self = (MethodBinding)GetManagedObject(ob); + Runtime.Decref(self.target); + ExtensionType.FinalizeObject(self); + } + + } + + +} diff --git a/Pythonnet.Runtime/methodobject.cs b/Pythonnet.Runtime/methodobject.cs new file mode 100644 index 0000000000..15a5cd5478 --- /dev/null +++ b/Pythonnet.Runtime/methodobject.cs @@ -0,0 +1,189 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // Implements a Python type that represents a CLR method. Method objects + // support a subscript syntax [] to allow explicit overload selection. + //======================================================================== + // TODO: ForbidPythonThreadsAttribute per method info + + internal class MethodObject : ExtensionType { + + internal MethodInfo[] info; + internal string name; + internal MethodBinding unbound; + internal MethodBinder binder; + internal bool is_static = false; + internal IntPtr doc; + + public MethodObject(string name, MethodInfo[] info) : base() { + _MethodObject(name, info); + } + + public MethodObject(string name, MethodInfo[] info, bool allow_threads) : base() + { + _MethodObject(name, info); + binder.allow_threads = allow_threads; + } + + private void _MethodObject(string name, MethodInfo[] info) + { + this.name = name; + this.info = info; + binder = new MethodBinder(); + for (int i = 0; i < info.Length; i++) + { + MethodInfo item = (MethodInfo)info[i]; + binder.AddMethod(item); + if (item.IsStatic) + { + this.is_static = true; + } + } + } + + public virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) { + return this.Invoke(inst, args, kw, null); + } + + public virtual IntPtr Invoke(IntPtr target, IntPtr args, IntPtr kw, + MethodBase info) { + return binder.Invoke(target, args, kw, info, this.info); + } + + //==================================================================== + // Helper to get docstrings from reflected method / param info. + //==================================================================== + + internal IntPtr GetDocString() { + if (doc != IntPtr.Zero) { + return doc; + } + string str = ""; + Type marker = typeof(DocStringAttribute); + MethodBase[] methods = binder.GetMethods(); + foreach (MethodBase method in methods) { + if (str.Length > 0) + str += Environment.NewLine; + Attribute[] attrs = (Attribute[]) method.GetCustomAttributes(marker, false); + if (attrs.Length == 0) { + str += method.ToString(); + } + else { + DocStringAttribute attr = (DocStringAttribute)attrs[0]; + str += attr.DocString; + } + } + doc = Runtime.PyString_FromString(str); + return doc; + } + + + //==================================================================== + // This is a little tricky: a class can actually have a static method + // and instance methods all with the same name. That makes it tough + // to support calling a method 'unbound' (passing the instance as the + // first argument), because in this case we can't know whether to call + // the instance method unbound or call the static method. + // + // The rule we is that if there are both instance and static methods + // with the same name, then we always call the static method. So this + // method returns true if any of the methods that are represented by + // the descriptor are static methods (called by MethodBinding). + //==================================================================== + + internal bool IsStatic() { + return this.is_static; + } + + //==================================================================== + // Descriptor __getattribute__ implementation. + //==================================================================== + + public static IntPtr tp_getattro(IntPtr ob, IntPtr key) { + MethodObject self = (MethodObject)GetManagedObject(ob); + + if (!Runtime.PyString_Check(key)) { + return Exceptions.RaiseTypeError("string expected"); + } + + string name = Runtime.GetManagedString(key); + if (name == "__doc__") { + IntPtr doc = self.GetDocString(); + Runtime.Incref(doc); + return doc; + } + + return Runtime.PyObject_GenericGetAttr(ob, key); + } + + //==================================================================== + // Descriptor __get__ implementation. Accessing a CLR method returns + // a "bound" method similar to a Python bound method. + //==================================================================== + + public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { + MethodObject self = (MethodObject)GetManagedObject(ds); + MethodBinding binding; + + // If the method is accessed through its type (rather than via + // an instance) we return an 'unbound' MethodBinding that will + // cached for future accesses through the type. + + if (ob == IntPtr.Zero) { + if (self.unbound == null) { + self.unbound = new MethodBinding(self, IntPtr.Zero); + } + binding = self.unbound; + Runtime.Incref(binding.pyHandle);; + return binding.pyHandle; + } + + if (Runtime.PyObject_IsInstance(ob, tp) < 1) { + return Exceptions.RaiseTypeError("invalid argument"); + } + + binding = new MethodBinding(self, ob); + return binding.pyHandle; + } + + //==================================================================== + // Descriptor __repr__ implementation. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + MethodObject self = (MethodObject)GetManagedObject(ob); + string s = String.Format("", self.name); + return Runtime.PyString_FromStringAndSize(s, s.Length); + } + + //==================================================================== + // Descriptor dealloc implementation. + //==================================================================== + + public static new void tp_dealloc(IntPtr ob) { + MethodObject self = (MethodObject)GetManagedObject(ob); + Runtime.Decref(self.doc); + if (self.unbound != null) { + Runtime.Decref(self.unbound.pyHandle); + } + ExtensionType.FinalizeObject(self); + } + + + } + + +} diff --git a/Pythonnet.Runtime/methodwrapper.cs b/Pythonnet.Runtime/methodwrapper.cs new file mode 100644 index 0000000000..04a49d5924 --- /dev/null +++ b/Pythonnet.Runtime/methodwrapper.cs @@ -0,0 +1,61 @@ +// ========================================================================== +// 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.Collections; +using System.Runtime.InteropServices; + +namespace Python.Runtime { + + /// + /// A MethodWrapper wraps a static method of a managed type, + /// making it callable by Python as a PyCFunction object. This is + /// currently used mainly to implement special cases like the CLR + /// import hook. + /// + + internal class MethodWrapper { + + public IntPtr mdef; + public IntPtr ptr; + + public MethodWrapper(Type type, string name) { + + // Turn the managed method into a function pointer + + IntPtr fp = Interop.GetThunk(type.GetMethod(name)); + + // XXX - here we create a Python string object, then take the + // char * of the internal string to pass to our methoddef + // structure. Its a hack, and the name is leaked! + + IntPtr ps = Runtime.PyString_FromString(name); + IntPtr sp = Runtime.PyString_AS_STRING(ps); + + // Allocate and initialize a PyMethodDef structure to represent + // the managed method, then create a PyCFunction. + + mdef = Runtime.PyMem_Malloc(4 * IntPtr.Size); + Marshal.WriteIntPtr(mdef, sp); + Marshal.WriteIntPtr(mdef, (1 * IntPtr.Size), fp); + Marshal.WriteIntPtr(mdef, (2 * IntPtr.Size), (IntPtr)0x0002); + Marshal.WriteIntPtr(mdef, (3 * IntPtr.Size), IntPtr.Zero); + ptr = Runtime.PyCFunction_New(mdef, IntPtr.Zero); + } + + public IntPtr Call(IntPtr args, IntPtr kw) { + return Runtime.PyCFunction_Call(ptr, args, kw); + } + + + } + + +} + diff --git a/Pythonnet.Runtime/modulefunctionobject.cs b/Pythonnet.Runtime/modulefunctionobject.cs new file mode 100644 index 0000000000..5c9a4de21c --- /dev/null +++ b/Pythonnet.Runtime/modulefunctionobject.cs @@ -0,0 +1,58 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; + +namespace Python.Runtime +{ + /// + /// Module level functions + /// + internal class ModuleFunctionObject : MethodObject + { + + public ModuleFunctionObject(string name, MethodInfo[] info, bool allow_threads) + : base(name, info, allow_threads) + { + for (int i = 0; i < info.Length; i++) + { + MethodInfo item = (MethodInfo)info[i]; + if (!item.IsStatic) + { + throw new Exception("Module function must be static."); + } + } + } + + //==================================================================== + // __call__ implementation. + //==================================================================== + + public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) + { + ModuleFunctionObject self = (ModuleFunctionObject)GetManagedObject(ob); + return self.Invoke(ob, args, kw); + } + + //==================================================================== + // __repr__ implementation. + //==================================================================== + + public static new IntPtr tp_repr(IntPtr ob) + { + ModuleFunctionObject self = (ModuleFunctionObject)GetManagedObject(ob); + string s = String.Format("", self.name); + return Runtime.PyString_FromStringAndSize(s, s.Length); + } + + } +} + diff --git a/Pythonnet.Runtime/moduleobject.cs b/Pythonnet.Runtime/moduleobject.cs new file mode 100644 index 0000000000..3a39919477 --- /dev/null +++ b/Pythonnet.Runtime/moduleobject.cs @@ -0,0 +1,432 @@ +// ========================================================================== +// 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.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Collections; +using System.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // Implements a Python type that provides access to CLR namespaces. The + // type behaves like a Python module, and can contain other sub-modules. + //======================================================================== + + internal class ModuleObject : ExtensionType { + + Dictionary cache; + internal string moduleName; + internal IntPtr dict; + protected string _namespace; + + public ModuleObject(string name) : base() { + if (name == String.Empty) + { + throw new ArgumentException("Name must not be empty!"); + } + moduleName = name; + cache = new Dictionary(); + _namespace = name; + + dict = Runtime.PyDict_New(); + IntPtr pyname = Runtime.PyString_FromString(moduleName); + Runtime.PyDict_SetItemString(dict, "__name__", pyname); + Runtime.PyDict_SetItemString(dict, "__file__", Runtime.PyNone); + Runtime.PyDict_SetItemString(dict, "__doc__", Runtime.PyNone); + Runtime.Decref(pyname); + + Marshal.WriteIntPtr(this.pyHandle, ObjectOffset.ob_dict, dict); + + InitializeModuleMembers(); + } + + + //=================================================================== + // Returns a ClassBase object representing a type that appears in + // this module's namespace or a ModuleObject representing a child + // namespace (or null if the name is not found). This method does + // not increment the Python refcount of the returned object. + //=================================================================== + + public ManagedType GetAttribute(string name, bool guess) { + ManagedType cached = null; + this.cache.TryGetValue(name, out cached); + if (cached != null) { + return cached; + } + + ModuleObject m; + ClassBase c; + Type type; + + //if (AssemblyManager.IsValidNamespace(name)) + //{ + // IntPtr py_mod_name = Runtime.PyString_FromString(name); + // IntPtr modules = Runtime.PyImport_GetModuleDict(); + // IntPtr module = Runtime.PyDict_GetItem(modules, py_mod_name); + // if (module != IntPtr.Zero) + // return (ManagedType)this; + // return null; + //} + + string qname = (_namespace == String.Empty) ? name : + _namespace + "." + name; + + // If the fully-qualified name of the requested attribute is + // a namespace exported by a currently loaded assembly, return + // a new ModuleObject representing that namespace. + + if (AssemblyManager.IsValidNamespace(qname)) { + m = new ModuleObject(qname); + StoreAttribute(name, m); + return (ManagedType) m; + } + + // Look for a type in the current namespace. Note that this + // includes types, delegates, enums, interfaces and structs. + // Only public namespace members are exposed to Python. + + type = AssemblyManager.LookupType(qname); + if (type != null) { + if (!type.IsPublic) { + return null; + } + c = ClassManager.GetClass(type); + StoreAttribute(name, c); + return (ManagedType) c; + } + + // This is a little repetitive, but it ensures that the right + // thing happens with implicit assembly loading at a reasonable + // cost. Ask the AssemblyManager to do implicit loading for each + // of the steps in the qualified name, then try it again. + bool fromFile; + if (AssemblyManager.LoadImplicit(qname, out fromFile)) { + bool ignore = name.StartsWith("__"); + if (true == fromFile && (!ignore)) { + string deprWarning = String.Format("\nThe module was found, but not in a referenced namespace.\n" + + "Implicit loading is deprecated. Please use clr.AddReference(\"{0}\").", qname); + Exceptions.deprecation(deprWarning); + } + if (AssemblyManager.IsValidNamespace(qname)) { + m = new ModuleObject(qname); + StoreAttribute(name, m); + return (ManagedType) m; + } + + type = AssemblyManager.LookupType(qname); + if (type != null) { + if (!type.IsPublic) { + return null; + } + c = ClassManager.GetClass(type); + StoreAttribute(name, c); + return (ManagedType) c; + } + } + + // We didn't find the name, so we may need to see if there is a + // generic type with this base name. If so, we'll go ahead and + // return it. Note that we store the mapping of the unmangled + // name to generic type - it is technically possible that some + // future assembly load could contribute a non-generic type to + // the current namespace with the given basename, but unlikely + // enough to complicate the implementation for now. + + if (guess) { + string gname = GenericUtil.GenericNameForBaseName( + _namespace, name); + if (gname != null) { + ManagedType o = GetAttribute(gname, false); + if (o != null) { + StoreAttribute(name, o); + return o; + } + } + } + + return null; + } + + + //=================================================================== + // Stores an attribute in the instance dict for future lookups. + //=================================================================== + + private void StoreAttribute(string name, ManagedType ob) { + Runtime.PyDict_SetItemString(dict, name, ob.pyHandle); + cache[name] = ob; + } + + + //=================================================================== + // Preloads all currently-known names for the module namespace. This + // can be called multiple times, to add names from assemblies that + // may have been loaded since the last call to the method. + //=================================================================== + + public void LoadNames() { + ManagedType m = null; + foreach (string name in AssemblyManager.GetNames(_namespace)) { + this.cache.TryGetValue(name, out m); + if (m == null) { + ManagedType attr = this.GetAttribute(name, true); + if (Runtime.wrap_exceptions) { + if (attr is ExceptionClassObject) { + ExceptionClassObject c = attr as ExceptionClassObject; + if (c != null) { + IntPtr p = attr.pyHandle; + IntPtr r =Exceptions.GetExceptionClassWrapper(p); + Runtime.PyDict_SetItemString(dict, name, r); + Runtime.Incref(r); + + } + } + } + } + } + } + + /// + /// Initialize module level functions and attributes + /// + internal void InitializeModuleMembers() + { + Type funcmarker = typeof(ModuleFunctionAttribute); + Type propmarker = typeof(ModulePropertyAttribute); + Type ftmarker = typeof(ForbidPythonThreadsAttribute); + Type type = this.GetType(); + + BindingFlags flags = BindingFlags.Public | BindingFlags.Static; + + while (type != null) + { + MethodInfo[] methods = type.GetMethods(flags); + for (int i = 0; i < methods.Length; i++) + { + MethodInfo method = methods[i]; + object[] attrs = method.GetCustomAttributes(funcmarker, false); + object[] forbid = method.GetCustomAttributes(ftmarker, false); + bool allow_threads = (forbid.Length == 0); + if (attrs.Length > 0) + { + string name = method.Name; + MethodInfo[] mi = new MethodInfo[1]; + mi[0] = method; + ModuleFunctionObject m = new ModuleFunctionObject(name, mi, allow_threads); + StoreAttribute(name, m); + } + } + + PropertyInfo[] properties = type.GetProperties(); + for (int i = 0; i < properties.Length; i++) + { + PropertyInfo property = properties[i]; + object[] attrs = property.GetCustomAttributes(propmarker, false); + if (attrs.Length > 0) + { + string name = property.Name; + ModulePropertyObject p = new ModulePropertyObject(property); + StoreAttribute(name, p); + } + } + type = type.BaseType; + } + } + + + //==================================================================== + // ModuleObject __getattribute__ implementation. Module attributes + // are always either classes or sub-modules representing subordinate + // namespaces. CLR modules implement a lazy pattern - the sub-modules + // and classes are created when accessed and cached for future use. + //==================================================================== + + public static IntPtr tp_getattro(IntPtr ob, IntPtr key) { + ModuleObject self = (ModuleObject)GetManagedObject(ob); + + if (!Runtime.PyString_Check(key)) { + Exceptions.SetError(Exceptions.TypeError, "string expected"); + return IntPtr.Zero; + } + + IntPtr op = Runtime.PyDict_GetItem(self.dict, key); + if (op != IntPtr.Zero) { + Runtime.Incref(op); + return op; + } + + string name = Runtime.GetManagedString(key); + if (name == "__dict__") { + Runtime.Incref(self.dict); + return self.dict; + } + + ManagedType attr = self.GetAttribute(name, true); + + if (attr == null) { + Exceptions.SetError(Exceptions.AttributeError, name); + return IntPtr.Zero; + } + + // XXX - hack required to recognize exception types. These types + // may need to be wrapped in old-style class wrappers in versions + // of Python where new-style classes cannot be used as exceptions. + + if (Runtime.wrap_exceptions) { + if (attr is ExceptionClassObject) { + ExceptionClassObject c = attr as ExceptionClassObject; + if (c != null) { + IntPtr p = attr.pyHandle; + IntPtr r = Exceptions.GetExceptionClassWrapper(p); + Runtime.PyDict_SetItemString(self.dict, name, r); + Runtime.Incref(r); + return r; + } + } + } + + Runtime.Incref(attr.pyHandle); + return attr.pyHandle; + } + + //==================================================================== + // ModuleObject __repr__ implementation. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + ModuleObject self = (ModuleObject)GetManagedObject(ob); + string s = String.Format("", self.moduleName); + return Runtime.PyString_FromString(s); + } + + + + } + + /// + /// The CLR module is the root handler used by the magic import hook + /// to import assemblies. It has a fixed module name "clr" and doesn't + /// provide a namespace. + /// + internal class CLRModule : ModuleObject + { + protected static bool hacked = false; + protected static bool interactive_preload = true; + internal static bool preload; + // XXX Test performance of new features // + internal static bool _SuppressDocs = false; + internal static bool _SuppressOverloads = false; + + public CLRModule() : base("clr") { + _namespace = String.Empty; + + // This hackery is required in order to allow a plain Python to + // import the managed runtime via the CLR bootstrapper module. + // The standard Python machinery in control at the time of the + // import requires the module to pass PyModule_Check. :( + if (!hacked) + { + IntPtr type = this.tpHandle; + IntPtr mro = Marshal.ReadIntPtr(type, TypeOffset.tp_mro); + IntPtr ext = Runtime.ExtendTuple(mro, Runtime.PyModuleType); + Marshal.WriteIntPtr(type, TypeOffset.tp_mro, ext); + Runtime.Decref(mro); + hacked = true; + } + } + + /// + /// The initializing of the preload hook has to happen as late as + /// possible since sys.ps1 is created after the CLR module is + /// created. + /// + internal void InitializePreload() { + if (interactive_preload) { + interactive_preload = false; + if (Runtime.PySys_GetObject("ps1") != IntPtr.Zero) { + preload = true; + } else { + Exceptions.Clear(); + preload = false; + } + } + } + + [ModuleFunctionAttribute()] + public static bool getPreload() { + return preload; + } + + [ModuleFunctionAttribute()] + public static void setPreload(bool preloadFlag) + { + preload = preloadFlag; + } + + //[ModulePropertyAttribute] + public static bool SuppressDocs { + get { return _SuppressDocs; } + set { _SuppressDocs = value; } + } + + //[ModulePropertyAttribute] + public static bool SuppressOverloads { + get { return _SuppressOverloads; } + set { _SuppressOverloads = value; } + } + + [ModuleFunctionAttribute()] + [ForbidPythonThreadsAttribute()] + public static Assembly AddReference(string name) + { + AssemblyManager.UpdatePath(); + Assembly assembly = null; + assembly = AssemblyManager.LoadAssemblyPath(name); + if (assembly == null) + { + assembly = AssemblyManager.LoadAssembly(name); + } + if (assembly == null) + { + string msg = String.Format("Unable to find assembly '{0}'.", name); + throw new System.IO.FileNotFoundException(msg); + } + return assembly ; + } + + [ModuleFunctionAttribute()] + [ForbidPythonThreadsAttribute()] + public static string FindAssembly(string name) + { + AssemblyManager.UpdatePath(); + return AssemblyManager.FindAssembly(name); + } + + [ModuleFunctionAttribute()] + public static String[] ListAssemblies(bool verbose) + { + AssemblyName[] assnames = AssemblyManager.ListAssemblies(); + String[] names = new String[assnames.Length]; + for (int i = 0; i < assnames.Length; i++) + { + if (verbose) + names[i] = assnames[i].FullName; + else + names[i] = assnames[i].Name; + } + return names; + } + + } + +} diff --git a/Pythonnet.Runtime/modulepropertyobject.cs b/Pythonnet.Runtime/modulepropertyobject.cs new file mode 100644 index 0000000000..f833696bf8 --- /dev/null +++ b/Pythonnet.Runtime/modulepropertyobject.cs @@ -0,0 +1,30 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; +using System.Security.Permissions; + +namespace Python.Runtime { + + /// + /// Module level properties (attributes) + /// + internal class ModulePropertyObject : ExtensionType { + + public ModulePropertyObject(PropertyInfo md) : base() + { + throw new NotImplementedException("ModulePropertyObject"); + } + + } + +} + diff --git a/Pythonnet.Runtime/monosupport.cs b/Pythonnet.Runtime/monosupport.cs new file mode 100644 index 0000000000..6208b498ec --- /dev/null +++ b/Pythonnet.Runtime/monosupport.cs @@ -0,0 +1,60 @@ +// ========================================================================== +// 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. +// ========================================================================== + +#if (UCS4) +using System; +using System.Runtime.InteropServices; +using System.Text; +using Mono.Unix; + +namespace Python.Runtime { + // The Utf32Marshaler was written Jonathan Pryor and has been placed + // in the PUBLIC DOMAIN. + public class Utf32Marshaler : ICustomMarshaler { + private static Utf32Marshaler instance = new + Utf32Marshaler (); + + public static ICustomMarshaler GetInstance (string s) + { + return instance; + } + + public void CleanUpManagedData (object o) + { + } + + public void CleanUpNativeData (IntPtr pNativeData) + { + UnixMarshal.FreeHeap (pNativeData); + } + + public int GetNativeDataSize () + { + return IntPtr.Size; + } + + public IntPtr MarshalManagedToNative (object obj) + { + string s = obj as string; + if (s == null) + return IntPtr.Zero; + return UnixMarshal.StringToHeap (s, + Encoding.UTF32); + } + + public object MarshalNativeToManaged (IntPtr + pNativeData) + { + return UnixMarshal.PtrToString (pNativeData, + Encoding.UTF32); + } + } +} +#endif + diff --git a/Pythonnet.Runtime/nativecall.cs b/Pythonnet.Runtime/nativecall.cs new file mode 100644 index 0000000000..d2c4bf5b3a --- /dev/null +++ b/Pythonnet.Runtime/nativecall.cs @@ -0,0 +1,166 @@ +// ========================================================================== +// 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; +using System.Runtime.InteropServices; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; + +namespace Python.Runtime { + + /// + /// Provides support for calling native code indirectly through + /// function pointers. Most of the important parts of the Python + /// C API can just be wrapped with p/invoke, but there are some + /// situations (specifically, calling functions through Python + /// type structures) where we need to call functions indirectly. + /// + /// This class uses Reflection.Emit to generate IJW thunks that + /// support indirect calls to native code using various common + /// call signatures. This is mainly a workaround for the fact + /// that you can't spell an indirect call in C# (but can in IL). + /// + /// Another approach that would work is for this to be turned + /// into a separate utility program that could be run during the + /// build process to generate the thunks as a separate assembly + /// that could then be referenced by the main Python runtime. + /// + + internal class NativeCall { + + static AssemblyBuilder aBuilder; + static ModuleBuilder mBuilder; + + public static INativeCall Impl; + + static NativeCall() { + + // The static constructor is responsible for generating the + // assembly and the methods that implement the IJW thunks. + // + // To do this, we actually use reflection on the INativeCall + // interface (defined below) and generate the required thunk + // code based on the method signatures. + + AssemblyName aname = new AssemblyName(); + aname.Name = "e__NativeCall_Assembly"; + AssemblyBuilderAccess aa = AssemblyBuilderAccess.Run; + + aBuilder = Thread.GetDomain().DefineDynamicAssembly(aname, aa); + mBuilder = aBuilder.DefineDynamicModule("e__NativeCall_Module"); + + TypeAttributes ta = TypeAttributes.Public; + TypeBuilder tBuilder = mBuilder.DefineType("e__NativeCall", ta); + + Type iType = typeof(INativeCall); + tBuilder.AddInterfaceImplementation(iType); + + // Use reflection to loop over the INativeCall interface methods, + // calling GenerateThunk to create a managed thunk for each one. + + foreach (MethodInfo method in iType.GetMethods()) { + GenerateThunk(tBuilder, method); + } + + Type theType = tBuilder.CreateType(); + + Impl = (INativeCall)Activator.CreateInstance(theType); + + } + + private static void GenerateThunk(TypeBuilder tb, MethodInfo method) { + + ParameterInfo[] pi = method.GetParameters(); + int count = pi.Length; + int argc = count - 1; + + Type[] args = new Type[count]; + for (int i = 0; i < count; i++) { + args[i] = pi[i].ParameterType; + } + + MethodBuilder mb = tb.DefineMethod( + method.Name, + MethodAttributes.Public | + MethodAttributes.Virtual, + method.ReturnType, + args + ); + + // Build the method signature for the actual native function. + // This is essentially the signature of the wrapper method + // minus the first argument (the passed in function pointer). + + Type[] nargs = new Type[argc]; + for (int i = 1; i < count; i++) { + nargs[(i - 1)] = args[i]; + } + + // IL generation: the (implicit) first argument of the method + // is the 'this' pointer and the second is the function pointer. + // This code pushes the real args onto the stack, followed by + // the function pointer, then the calli opcode to make the call. + + ILGenerator il = mb.GetILGenerator(); + + for (int i = 0; i < argc; i++) { + il.Emit(OpCodes.Ldarg_S, (i + 2)); + } + + il.Emit(OpCodes.Ldarg_1); + + il.EmitCalli(OpCodes.Calli, + CallingConvention.Cdecl, + method.ReturnType, + nargs + ); + + il.Emit(OpCodes.Ret); + + tb.DefineMethodOverride(mb, method); + return; + } + + + public static void Void_Call_1(IntPtr fp, IntPtr a1) { + Impl.Void_Call_1(fp, a1); + } + + public static IntPtr Call_3(IntPtr fp, IntPtr a1, IntPtr a2, + IntPtr a3) { + return Impl.Call_3(fp, a1, a2, a3); + } + + public static int Int_Call_3(IntPtr fp, IntPtr a1, IntPtr a2, + IntPtr a3) { + return Impl.Int_Call_3(fp, a1, a2, a3); + } + + } + + + /// + /// Defines native call signatures to be generated by NativeCall. + /// + + public interface INativeCall { + + void Void_Call_0(IntPtr funcPtr); + + void Void_Call_1(IntPtr funcPtr, IntPtr arg1); + + int Int_Call_3(IntPtr funcPtr, IntPtr t, IntPtr n, IntPtr v); + + IntPtr Call_3(IntPtr funcPtr, IntPtr a1, IntPtr a2, IntPtr a3); + + } + +} diff --git a/Pythonnet.Runtime/oldmodule.il b/Pythonnet.Runtime/oldmodule.il new file mode 100644 index 0000000000..6ecd2c1363 --- /dev/null +++ b/Pythonnet.Runtime/oldmodule.il @@ -0,0 +1,274 @@ +// ========================================================================== +// 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. +// ========================================================================== + +//============================================================================ +// This file is a hand-maintained stub - it implements clr.dll, which can be +// loaded by a standard CPython interpreter as an extension module. When it +// is loaded, it bootstraps the managed runtime integration layer and defers +// to it to do initialization and put the clr module into sys.modules, etc. + +// The "USE_PYTHON_RUNTIME_*" defines control what extra evidence is used +// to help the CLR find the appropriate Python.Runtime assembly. + +// If defined, the "pythonRuntimeVersionString" variable must be set to +// Python.Runtime's current version. +#define USE_PYTHON_RUNTIME_VERSION + +// If defined, the "PythonRuntimePublicKeyTokenData" data array must be +// set to Python.Runtime's public key token. +//#define USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN + +// If DEBUG_PRINT is defined, a few System.Console.WriteLine calls are made +// to indicate what's going on during the load... +//#define DEBUG_PRINT +//============================================================================ + +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 2:0:0:0 +} + +.assembly clr +{ + .hash algorithm 0x00008004 + .ver 2:0:0:2 +} + +.module clr.dll +.imagebase 0x00400000 +.subsystem 0x00000003 +.file alignment 512 + +// This includes the platform-specific IL. The include search path +// is set depending on whether we're compiling 32 or 64 bit. +// This MUST come before any other .data directives! +// Why, oh why, can't ilasm support command line #defines? :( +#include "clrmodule-platform.il" + +#ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN +.data PythonRuntimePublicKeyTokenData = bytearray (64 e1 4e 84 5a bf 2e 60) +#endif + +.class public auto ansi beforefieldinit clrModule extends [mscorlib]System.Object +{ +#ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN + .field static assembly int64 PythonRuntimePublicKeyToken at PythonRuntimePublicKeyTokenData +#endif + + .method public hidebysig specialname rtspecialname instance void + .ctor() cil managed + { + .maxstack 1 + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } + + .method public hidebysig static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) + initclr() cil managed + { + .vtentry 1:1 + .export [1] as initclr + + .maxstack 6 + .locals init ( + class [mscorlib]System.Reflection.Assembly pythonRuntime, + class [mscorlib]System.Reflection.Assembly executingAssembly, + class [mscorlib]System.Reflection.AssemblyName pythonRuntimeName, + class [mscorlib]System.Type pythonEngineType, + int8[] publicKeyToken, + string assemblyDirectory, + string pythonRuntimeVersionString, + string pythonRuntimeDllPath) + + // pythonRuntime = null; + ldnull + stloc pythonRuntime + + .try + { +#ifdef DEBUG_PRINT + ldstr "Attempting to load Python.Runtime using standard binding rules... " + call void [mscorlib]System.Console::Write(string) +#endif + + // Attempt to find and load Python.Runtime using standard assembly binding rules. + // This roughly translates into looking in order: + // - GAC + // - ApplicationBase + // - A PrivateBinPath under ApplicationBase + // With an unsigned assembly, the GAC is skipped. + + // System.Reflection.AssemblyName pythonRuntimeName = new System.Reflection.AssemblyName(); + newobj instance void [mscorlib]System.Reflection.AssemblyName::.ctor() + stloc pythonRuntimeName + + // pythonRuntimeName.Name = "Python.Runtime"; + ldloc pythonRuntimeName + ldstr "Python.Runtime" + callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_Name(string) + +#ifdef USE_PYTHON_RUNTIME_VERSION + // pythonRuntimeVersionString = "..."; + ldstr "2.0.0.2" + stloc pythonRuntimeVersionString + + // pythonRuntimeName.Version = new Version(pythonRuntimeVersionString); + ldloc pythonRuntimeName + ldloc pythonRuntimeVersionString + newobj instance void [mscorlib]System.Version::.ctor(string) + callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_Version(class [mscorlib]System.Version) +#endif + +#ifdef USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN + // publicKeyToken = new byte[] { ... }; + ldc.i4.8 + newarr [mscorlib]System.Byte + dup + ldtoken field int64 clrModule::PythonRuntimePublicKeyToken + call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) + stloc publicKeyToken + + // pythonRuntimeName.SetPublicKeyToken(publicKeyToken); + ldloc pythonRuntimeName + ldloc publicKeyToken + callvirt instance void [mscorlib]System.Reflection.AssemblyName::SetPublicKeyToken(uint8[]) +#endif + + // pythonRuntimeName.CultureInfo = System.Globalization.CultureInfo.InvariantCulture; + ldloc pythonRuntimeName + call class [mscorlib]System.Globalization.CultureInfo [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture() + callvirt instance void [mscorlib]System.Reflection.AssemblyName::set_CultureInfo(class [mscorlib]System.Globalization.CultureInfo) + + // return System.Reflection.Assembly.Load(pythonRuntimeName); + ldloc pythonRuntimeName + call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::Load(class [mscorlib]System.Reflection.AssemblyName) + stloc pythonRuntime + +#ifdef DEBUG_PRINT + ldstr "Success!" + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s LOADED_PYTHON_RUNTIME + } + catch [mscorlib]System.Object + { +#ifdef DEBUG_PRINT + ldstr "Failed." + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s EXIT_CLR_LOAD + } + EXIT_CLR_LOAD: nop + + .try + { + // If the above fails for any reason, we fallback to attempting to load "Python.Runtime.dll" + // from the directory this assembly is running in. "This assembly" is probably "clr.pyd", + // sitting somewhere in PYTHONPATH. This is using Assembly.LoadFrom, and inherits all the + // caveats of that call. See MSDN docs for details. + // Suzanne Cook's blog is also an excellent source of info on this: + // http://blogs.msdn.com/suzcook/ + // http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx + // http://blogs.msdn.com/suzcook/archive/2003/06/13/57180.aspx + // executingAssembly = System.Reflection.Assembly.GetExecutingAssembly(); + call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::GetExecutingAssembly() + stloc executingAssembly + + // assemblyDirectory = System.IO.Path.GetDirectoryName(executingAssembly.Location); + ldloc executingAssembly + callvirt instance string [mscorlib]System.Reflection.Assembly::get_Location() + call string [mscorlib]System.IO.Path::GetDirectoryName(string) + stloc assemblyDirectory + + // pythonRuntimeDllPath = System.IO.Path.Combine(assemblyDirectory, "Python.Runtime.dll"); + ldloc assemblyDirectory + ldstr "Python.Runtime.dll" + call string [mscorlib]System.IO.Path::Combine(string, string) + stloc pythonRuntimeDllPath + +#ifdef DEBUG_PRINT + ldstr "Attempting to load Python.Runtime from: '{0}'... " + ldloc pythonRuntimeDllPath + call void [mscorlib]System.Console::Write(string, object) +#endif + + // pythonRuntime = System.Reflection.Assembly.LoadFrom(pythonRuntimeDllPath); + ldloc pythonRuntimeDllPath + call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::LoadFrom(string) + stloc pythonRuntime + +#ifdef DEBUG_PRINT + ldstr "Success!" + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s LOADED_PYTHON_RUNTIME + } + catch [mscorlib]System.Object + { +#ifdef DEBUG_PRINT + ldstr "Failed." + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s EXIT_PYTHONPATH_LOAD + } + EXIT_PYTHONPATH_LOAD: nop + + // If we get here, we haven't loaded Python.Runtime, so bail. +#ifdef DEBUG_PRINT + ldstr "Could not load Python.Runtime, so sad." + call void [mscorlib]System.Console::WriteLine(string) +#endif + ret; + + // Once here, we've successfully loaded SOME version of Python.Runtime + // So now we get the PythonEngine and execute the InitExt method on it. + LOADED_PYTHON_RUNTIME: nop + .try + { +#ifdef DEBUG_PRINT + ldstr "Running Python.Runtime.PythonEngine.InitExt()" + call void [mscorlib]System.Console::WriteLine(string) +#endif + // pythonEngineType = pythonRuntime.GetType("Python.Runtime.PythonEngine"); + ldloc pythonRuntime + ldstr "Python.Runtime.PythonEngine" + callvirt instance class [mscorlib]System.Type [mscorlib]System.Reflection.Assembly::GetType(string) + stloc pythonEngineType + + // pythonEngineType.InvokeMember("InitExt", System.Reflection.BindingFlags.InvokeMethod, null, null, null); + ldloc pythonEngineType + ldstr "InitExt" + ldc.i4 0x100 + ldnull + ldnull + ldnull + callvirt instance object [mscorlib]System.Type::InvokeMember( string, + valuetype [mscorlib]System.Reflection.BindingFlags, + class [mscorlib]System.Reflection.Binder, + object, + object[]) + pop + leave.s EXIT_TRY_INVOKE + } + catch [mscorlib]System.Object + { +#ifdef DEBUG_PRINT + ldstr "Error calling Python.Runtime.PythonEngine.InitExt()." + call void [mscorlib]System.Console::WriteLine(string) +#endif + leave.s EXIT_TRY_INVOKE + } + EXIT_TRY_INVOKE: nop + + ret + } +} + diff --git a/Pythonnet.Runtime/overload.cs b/Pythonnet.Runtime/overload.cs new file mode 100644 index 0000000000..0b7ef248cc --- /dev/null +++ b/Pythonnet.Runtime/overload.cs @@ -0,0 +1,83 @@ +// ========================================================================== +// 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.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // Implements the __overloads__ attribute of method objects. This object + // supports the [] syntax to explicitly select an overload by signature. + //======================================================================== + + internal class OverloadMapper : ExtensionType { + + MethodObject m; + IntPtr target; + + public OverloadMapper(MethodObject m, IntPtr target) : base() { + Runtime.Incref(target); + this.target = target; + this.m = m; + } + + //==================================================================== + // Implement explicit overload selection using subscript syntax ([]). + //==================================================================== + + public static IntPtr mp_subscript(IntPtr tp, IntPtr idx) { + OverloadMapper self = (OverloadMapper)GetManagedObject(tp); + + // Note: if the type provides a non-generic method with N args + // and a generic method that takes N params, then we always + // prefer the non-generic version in doing overload selection. + + Type[] types = Runtime.PythonArgsToTypeArray(idx); + if (types == null) { + return Exceptions.RaiseTypeError("type(s) expected"); + } + + MethodInfo mi = MethodBinder.MatchSignature(self.m.info, types); + if (mi == null) { + string e = "No match found for signature"; + return Exceptions.RaiseTypeError(e); + } + + MethodBinding mb = new MethodBinding(self.m, self.target); + mb.info = mi; + Runtime.Incref(mb.pyHandle); + return mb.pyHandle; + } + + //==================================================================== + // OverloadMapper __repr__ implementation. + //==================================================================== + + public static IntPtr tp_repr(IntPtr op) { + OverloadMapper self = (OverloadMapper)GetManagedObject(op); + IntPtr doc = self.m.GetDocString(); + Runtime.Incref(doc); + return doc; + } + + //==================================================================== + // OverloadMapper dealloc implementation. + //==================================================================== + + public static new void tp_dealloc(IntPtr ob) { + OverloadMapper self = (OverloadMapper)GetManagedObject(ob); + Runtime.Decref(self.target); + ExtensionType.FinalizeObject(self); + } + + } + + +} diff --git a/Pythonnet.Runtime/propertyobject.cs b/Pythonnet.Runtime/propertyobject.cs new file mode 100644 index 0000000000..d61dc01344 --- /dev/null +++ b/Pythonnet.Runtime/propertyobject.cs @@ -0,0 +1,164 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; +using System.Security.Permissions; + +namespace Python.Runtime { + + //======================================================================== + // Implements a Python descriptor type that manages CLR properties. + //======================================================================== + + internal class PropertyObject : ExtensionType { + + PropertyInfo info; + MethodInfo getter; + MethodInfo setter; + + [StrongNameIdentityPermissionAttribute(SecurityAction.Assert)] + public PropertyObject(PropertyInfo md) : base() { + getter = md.GetGetMethod(true); + setter = md.GetSetMethod(true); + info = md; + } + + + //==================================================================== + // Descriptor __get__ implementation. This method returns the + // value of the property on the given object. The returned value + // is converted to an appropriately typed Python object. + //==================================================================== + + public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { + PropertyObject self = (PropertyObject)GetManagedObject(ds); + MethodInfo getter = self.getter; + Object result; + + + if (getter == null) { + return Exceptions.RaiseTypeError("property cannot be read"); + } + + if ((ob == IntPtr.Zero) || (ob == Runtime.PyNone)) { + if (!(getter.IsStatic)) { + Exceptions.SetError(Exceptions.TypeError, + "instance property must be accessed through " + + "a class instance" + ); + return IntPtr.Zero; + } + + try { + result = self.info.GetValue(null, null); + return Converter.ToPython(result, self.info.PropertyType); + } + catch(Exception e) { + return Exceptions.RaiseTypeError(e.Message); + } + } + + CLRObject co = GetManagedObject(ob) as CLRObject; + if (co == null) { + return Exceptions.RaiseTypeError("invalid target"); + } + + try { + result = self.info.GetValue(co.inst, null); + return Converter.ToPython(result, self.info.PropertyType); + } + catch(Exception e) { + if (e.InnerException != null) { + e = e.InnerException; + } + Exceptions.SetError(e); + return IntPtr.Zero; + } + } + + + //==================================================================== + // Descriptor __set__ implementation. This method sets the value of + // a property based on the given Python value. The Python value must + // be convertible to the type of the property. + //==================================================================== + + public static new int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val) { + PropertyObject self = (PropertyObject)GetManagedObject(ds); + MethodInfo setter = self.setter; + Object newval; + + if (val == IntPtr.Zero) { + Exceptions.RaiseTypeError("cannot delete property"); + return -1; + } + + if (setter == null) { + Exceptions.RaiseTypeError("property is read-only"); + return -1; + } + + + if (!Converter.ToManaged(val, self.info.PropertyType, out newval, + true)) { + return -1; + } + + bool is_static = setter.IsStatic; + + if ((ob == IntPtr.Zero) || (ob == Runtime.PyNone)) { + if (!(is_static)) { + Exceptions.RaiseTypeError( + "instance property must be set on an instance" + ); + return -1; + } + } + + try { + if (!is_static) { + CLRObject co = GetManagedObject(ob) as CLRObject; + if (co == null) { + Exceptions.RaiseTypeError("invalid target"); + return -1; + } + self.info.SetValue(co.inst, newval, null); + } + else { + self.info.SetValue(null, newval, null); + } + return 0; + } + catch(Exception e) { + if (e.InnerException != null) { + e = e.InnerException; + } + Exceptions.SetError(e); + return -1; + } + + } + + + //==================================================================== + // Descriptor __repr__ implementation. + //==================================================================== + + public static IntPtr tp_repr(IntPtr ob) { + PropertyObject self = (PropertyObject)GetManagedObject(ob); + string s = String.Format("", self.info.Name); + return Runtime.PyString_FromStringAndSize(s, s.Length); + } + + } + + +} diff --git a/Pythonnet.Runtime/pyansistring.cs b/Pythonnet.Runtime/pyansistring.cs new file mode 100644 index 0000000000..db8e249a16 --- /dev/null +++ b/Pythonnet.Runtime/pyansistring.cs @@ -0,0 +1,82 @@ +// ========================================================================== +// This is a user contribution to the pythondotnet project. +// 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; + +namespace Python.Runtime { + + public class PyAnsiString : PySequence + { + /// + /// PyAnsiString Constructor + /// + /// + /// + /// Creates a new PyAnsiString from an existing object reference. Note + /// that the instance assumes ownership of the object reference. + /// The object reference is not checked for type-correctness. + /// + + public PyAnsiString(IntPtr ptr) : base(ptr) { } + + + /// + /// PyString Constructor + /// + /// + /// + /// Copy constructor - obtain a PyAnsiString from a generic PyObject. + /// An ArgumentException will be thrown if the given object is not + /// a Python string object. + /// + + public PyAnsiString(PyObject o) + : base() + { + if (!IsStringType(o)) + { + throw new ArgumentException("object is not a string"); + } + Runtime.Incref(o.obj); + obj = o.obj; + } + + + /// + /// PyAnsiString Constructor + /// + /// + /// + /// Creates a Python string from a managed string. + /// + + public PyAnsiString(string s) + : base() + { + obj = Runtime.PyString_FromStringAndSize(s, s.Length); + if (obj == IntPtr.Zero) + { + throw new PythonException(); + } + } + + + /// + /// IsStringType Method + /// + /// + /// + /// Returns true if the given object is a Python string. + /// + + public static bool IsStringType(PyObject value) + { + return Runtime.PyString_Check(value.obj); + } + } +} \ No newline at end of file diff --git a/Pythonnet.Runtime/pydict.cs b/Pythonnet.Runtime/pydict.cs new file mode 100644 index 0000000000..cd85c7126d --- /dev/null +++ b/Pythonnet.Runtime/pydict.cs @@ -0,0 +1,208 @@ +// ========================================================================== +// 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.Runtime.InteropServices; + +namespace Python.Runtime { + + /// + /// Represents a Python dictionary object. See the documentation at + /// http://www.python.org/doc/current/api/dictObjects.html for details. + /// + + public class PyDict : PyObject { + + /// + /// PyDict Constructor + /// + /// + /// + /// Creates a new PyDict from an existing object reference. Note + /// that the instance assumes ownership of the object reference. + /// The object reference is not checked for type-correctness. + /// + + public PyDict(IntPtr ptr) : base(ptr) {} + + + /// + /// PyDict Constructor + /// + /// + /// + /// Creates a new Python dictionary object. + /// + + public PyDict() : base() { + obj = Runtime.PyDict_New(); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyDict Constructor + /// + /// + /// + /// Copy constructor - obtain a PyDict from a generic PyObject. An + /// ArgumentException will be thrown if the given object is not a + /// Python dictionary object. + /// + + public PyDict(PyObject o) : base() { + if (!IsDictType(o)) { + throw new ArgumentException("object is not a dict"); + } + Runtime.Incref(o.obj); + obj = o.obj; + } + + + /// + /// IsDictType Method + /// + /// + /// + /// Returns true if the given object is a Python dictionary. + /// + + public static bool IsDictType(PyObject value) { + return Runtime.PyDict_Check(value.obj); + } + + + /// + /// HasKey Method + /// + /// + /// + /// Returns true if the object key appears in the dictionary. + /// + + public bool HasKey(PyObject key) { + return (Runtime.PyMapping_HasKey(obj, key.obj) != 0); + } + + + /// + /// HasKey Method + /// + /// + /// + /// Returns true if the string key appears in the dictionary. + /// + + public bool HasKey(string key) { + return HasKey(new PyString(key)); + } + + + /// + /// Keys Method + /// + /// + /// + /// Returns a sequence containing the keys of the dictionary. + /// + + public PyObject Keys() { + IntPtr items = Runtime.PyDict_Keys(obj); + if (items == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(items); + } + + + /// + /// Values Method + /// + /// + /// + /// Returns a sequence containing the values of the dictionary. + /// + + public PyObject Values() { + IntPtr items = Runtime.PyDict_Values(obj); + if (items == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(items); + } + + + /// + /// Items Method + /// + /// + /// + /// Returns a sequence containing the items of the dictionary. + /// + + public PyObject Items() { + IntPtr items = Runtime.PyDict_Items(obj); + if (items == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(items); + } + + + /// + /// Copy Method + /// + /// + /// + /// Returns a copy of the dictionary. + /// + + public PyDict Copy() { + IntPtr op = Runtime.PyDict_Copy(obj); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyDict(op); + } + + + /// + /// Update Method + /// + /// + /// + /// Update the dictionary from another dictionary. + /// + + public void Update(PyObject other) { + int result = Runtime.PyDict_Update(obj, other.obj); + if (result < 0) { + throw new PythonException(); + } + } + + + /// + /// Clear Method + /// + /// + /// + /// Clears the dictionary. + /// + + public void Clear() { + Runtime.PyDict_Clear(obj); + } + + + } + +} diff --git a/Pythonnet.Runtime/pyfloat.cs b/Pythonnet.Runtime/pyfloat.cs new file mode 100644 index 0000000000..960892594c --- /dev/null +++ b/Pythonnet.Runtime/pyfloat.cs @@ -0,0 +1,122 @@ +// ========================================================================== +// 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.Runtime.InteropServices; + +namespace Python.Runtime { + + /// + /// Represents a Python float object. See the documentation at + /// http://www.python.org/doc/current/api/floatObjects.html + /// + + public class PyFloat : PyNumber { + + /// + /// PyFloat Constructor + /// + /// + /// + /// Creates a new PyFloat from an existing object reference. Note + /// that the instance assumes ownership of the object reference. + /// The object reference is not checked for type-correctness. + /// + + public PyFloat(IntPtr ptr) : base(ptr) {} + + + /// + /// PyFloat Constructor + /// + /// + /// + /// Copy constructor - obtain a PyFloat from a generic PyObject. An + /// ArgumentException will be thrown if the given object is not a + /// Python float object. + /// + + public PyFloat(PyObject o) : base() { + if (!IsFloatType(o)) { + throw new ArgumentException("object is not a float"); + } + Runtime.Incref(o.obj); + obj = o.obj; + } + + + /// + /// PyFloat Constructor + /// + /// + /// + /// Creates a new Python float from a double value. + /// + + public PyFloat(double value) : base() { + obj = Runtime.PyFloat_FromDouble(value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyFloat Constructor + /// + /// + /// + /// Creates a new Python float from a string value. + /// + + public PyFloat(string value) : base() { + PyString s = new PyString(value); + obj = Runtime.PyFloat_FromString(s.obj, IntPtr.Zero); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// IsFloatType Method + /// + /// + /// + /// Returns true if the given object is a Python float. + /// + + public static bool IsFloatType(PyObject value) { + return Runtime.PyFloat_Check(value.obj); + } + + + /// + /// AsFloat Method + /// + /// + /// + /// + /// Convert a Python object to a Python float if possible, raising + /// a PythonException if the conversion is not possible. This is + /// equivalent to the Python expression "float(object)". + /// + + public static PyFloat AsFloat(PyObject value) { + IntPtr op = Runtime.PyNumber_Float(value.obj); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyFloat(op); + } + + + } + +} diff --git a/Pythonnet.Runtime/pyint.cs b/Pythonnet.Runtime/pyint.cs new file mode 100644 index 0000000000..7e5f07b6aa --- /dev/null +++ b/Pythonnet.Runtime/pyint.cs @@ -0,0 +1,257 @@ +// ========================================================================== +// 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.Runtime.InteropServices; + +namespace Python.Runtime { + + /// + /// Represents a Python integer object. See the documentation at + /// http://www.python.org/doc/current/api/intObjects.html for details. + /// + + public class PyInt : PyNumber { + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new PyInt from an existing object reference. Note + /// that the instance assumes ownership of the object reference. + /// The object reference is not checked for type-correctness. + /// + + public PyInt(IntPtr ptr) : base(ptr) {} + + + /// + /// PyInt Constructor + /// + /// + /// + /// Copy constructor - obtain a PyInt from a generic PyObject. An + /// ArgumentException will be thrown if the given object is not a + /// Python int object. + /// + + public PyInt(PyObject o) : base() { + if (!IsIntType(o)) { + throw new ArgumentException("object is not an int"); + } + Runtime.Incref(o.obj); + obj = o.obj; + } + + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new Python int from an int32 value. + /// + + public PyInt(int value) : base() { + obj = Runtime.PyInt_FromInt32(value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new Python int from a uint32 value. + /// + + [CLSCompliant(false)] + public PyInt(uint value) : base(IntPtr.Zero) { + obj = Runtime.PyInt_FromInt64((long)value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new Python int from an int64 value. + /// + + public PyInt(long value) : base(IntPtr.Zero) { + obj = Runtime.PyInt_FromInt64(value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new Python int from a uint64 value. + /// + + [CLSCompliant(false)] + public PyInt(ulong value) : base(IntPtr.Zero) { + obj = Runtime.PyInt_FromInt64((long)value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new Python int from an int16 value. + /// + + public PyInt(short value) : this((int)value) {} + + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new Python int from a uint16 value. + /// + + [CLSCompliant(false)] + public PyInt(ushort value) : this((int)value) {} + + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new Python int from a byte value. + /// + + public PyInt(byte value) : this((int)value) {} + + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new Python int from an sbyte value. + /// + + [CLSCompliant(false)] + public PyInt(sbyte value) : this((int)value) {} + + + /// + /// PyInt Constructor + /// + /// + /// + /// Creates a new Python int from a string value. + /// + + public PyInt(string value) : base() { + obj = Runtime.PyInt_FromString(value, IntPtr.Zero, 0); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// IsIntType Method + /// + /// + /// + /// Returns true if the given object is a Python int. + /// + + public static bool IsIntType(PyObject value) { + return Runtime.PyInt_Check(value.obj); + } + + + /// + /// AsInt Method + /// + /// + /// + /// + /// Convert a Python object to a Python int if possible, raising + /// a PythonException if the conversion is not possible. This is + /// equivalent to the Python expression "int(object)". + /// + + public static PyInt AsInt(PyObject value) { + IntPtr op = Runtime.PyNumber_Int(value.obj); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyInt(op); + } + + + /// + /// ToInt16 Method + /// + /// + /// + /// Return the value of the Python int object as an int16. + /// + + public short ToInt16() { + return System.Convert.ToInt16(this.ToInt32()); + } + + + /// + /// ToInt32 Method + /// + /// + /// + /// Return the value of the Python int object as an int32. + /// + + public int ToInt32() { + return Runtime.PyInt_AsLong(obj); + } + + + /// + /// ToInt64 Method + /// + /// + /// + /// Return the value of the Python int object as an int64. + /// + + public long ToInt64() { + return System.Convert.ToInt64(this.ToInt32()); + } + + + + } + +} diff --git a/Pythonnet.Runtime/pyiter.cs b/Pythonnet.Runtime/pyiter.cs new file mode 100644 index 0000000000..8d8ad44d7d --- /dev/null +++ b/Pythonnet.Runtime/pyiter.cs @@ -0,0 +1,76 @@ +// ========================================================================== +// 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.Collections.Generic; + +namespace Python.Runtime +{ + /// + /// Represents a standard Python iterator object. See the documentation at + /// http://www.python.org/doc/2.4.4/api/iterator.html for details. + /// + public class PyIter : PyObject, IEnumerator + { + private PyObject _current = null; + + /// + /// PyIter Constructor + /// + /// + /// + /// Creates a new PyIter from an existing iterator reference. Note + /// that the instance assumes ownership of the object reference. + /// The object reference is not checked for type-correctness. + /// + + public PyIter(IntPtr ptr) : base(ptr) {} + + /// + /// PyIter Constructor + /// + /// + /// + /// Creates a Python iterator from an iterable. Like doing "iter(iterable)" in python. + /// + + public PyIter(PyObject iterable) : base() + { + obj = Runtime.PyObject_GetIter(iterable.obj); + if (obj == IntPtr.Zero) + throw new PythonException(); + } + + #region IEnumerator Members + + public bool MoveNext() + { + IntPtr next = Runtime.PyIter_Next(obj); + if (next == IntPtr.Zero) + { + _current = null; //release reference + return false; + } + _current = new PyObject(next); + return true; + } + + public void Reset() + { + //Not supported in python. + } + + public object Current + { + get { return _current; } + } + + #endregion + } +} diff --git a/Pythonnet.Runtime/pylist.cs b/Pythonnet.Runtime/pylist.cs new file mode 100644 index 0000000000..3954823327 --- /dev/null +++ b/Pythonnet.Runtime/pylist.cs @@ -0,0 +1,189 @@ +// ========================================================================== +// 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; + +namespace Python.Runtime { + + /// + /// Represents a standard Python list object. See the documentation at + /// http://www.python.org/doc/current/api/listObjects.html for details. + /// + + public class PyList : PySequence { + + /// + /// PyList Constructor + /// + /// + /// + /// Creates a new PyList from an existing object reference. Note + /// that the instance assumes ownership of the object reference. + /// The object reference is not checked for type-correctness. + /// + + public PyList(IntPtr ptr) : base(ptr) {} + + + /// + /// PyList Constructor + /// + /// + /// + /// Copy constructor - obtain a PyList from a generic PyObject. An + /// ArgumentException will be thrown if the given object is not a + /// Python list object. + /// + + public PyList(PyObject o) : base() { + if (!IsListType(o)) { + throw new ArgumentException("object is not a list"); + } + Runtime.Incref(o.obj); + obj = o.obj; + } + + + /// + /// PyList Constructor + /// + /// + /// + /// Creates a new empty Python list object. + /// + + public PyList() : base() { + obj = Runtime.PyList_New(0); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyList Constructor + /// + /// + /// + /// Creates a new Python list object from an array of PyObjects. + /// + + public PyList(PyObject[] items) : base() { + int count = items.Length; + obj = Runtime.PyList_New(count); + for (int i = 0; i < count; i++) { + IntPtr ptr = items[i].obj; + Runtime.Incref(ptr); + int r = Runtime.PyList_SetItem(obj, i, ptr); + if (r < 0) { + throw new PythonException(); + } + } + } + + + /// + /// IsListType Method + /// + /// + /// + /// Returns true if the given object is a Python list. + /// + + public static bool IsListType(PyObject value) { + return Runtime.PyList_Check(value.obj); + } + + + /// + /// AsList Method + /// + /// + /// + /// Converts a Python object to a Python list if possible, raising + /// a PythonException if the conversion is not possible. This is + /// equivalent to the Python expression "list(object)". + /// + + public static PyList AsList(PyObject value) { + IntPtr op = Runtime.PySequence_List(value.obj); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyList(op); + } + + + /// + /// Append Method + /// + /// + /// + /// Append an item to the list object. + /// + + public void Append(PyObject item) { + int r = Runtime.PyList_Append(obj, item.obj); + if (r < 0) { + throw new PythonException(); + } + } + + /// + /// Insert Method + /// + /// + /// + /// Insert an item in the list object at the given index. + /// + + public void Insert(int index, PyObject item) { + int r = Runtime.PyList_Insert(obj, index, item.obj); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// Reverse Method + /// + /// + /// + /// Reverse the order of the list object in place. + /// + + public void Reverse() { + int r = Runtime.PyList_Reverse(obj); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// Sort Method + /// + /// + /// + /// Sort the list in place. + /// + + public void Sort() { + int r = Runtime.PyList_Sort(obj); + if (r < 0) { + throw new PythonException(); + } + } + + + } + + +} diff --git a/Pythonnet.Runtime/pylong.cs b/Pythonnet.Runtime/pylong.cs new file mode 100644 index 0000000000..999c75adce --- /dev/null +++ b/Pythonnet.Runtime/pylong.cs @@ -0,0 +1,291 @@ +// ========================================================================== +// 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; + +namespace Python.Runtime { + + /// + /// Represents a Python long int object. See the documentation at + /// http://www.python.org/doc/current/api/longObjects.html + /// + + public class PyLong : PyNumber { + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from an existing object reference. Note + /// that the instance assumes ownership of the object reference. + /// The object reference is not checked for type-correctness. + /// + + public PyLong(IntPtr ptr) : base(ptr) {} + + + /// + /// PyLong Constructor + /// + /// + /// + /// Copy constructor - obtain a PyLong from a generic PyObject. An + /// ArgumentException will be thrown if the given object is not a + /// Python long object. + /// + + public PyLong(PyObject o) : base() { + if (!IsLongType(o)) { + throw new ArgumentException("object is not a long"); + } + Runtime.Incref(o.obj); + obj = o.obj; + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from an int32 value. + /// + + public PyLong(int value) : base() { + obj = Runtime.PyLong_FromLong((long)value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from a uint32 value. + /// + + [CLSCompliant(false)] + public PyLong(uint value) : base() { + obj = Runtime.PyLong_FromLong((long)value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from an int64 value. + /// + + public PyLong(long value) : base() { + obj = Runtime.PyLong_FromLongLong(value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from a uint64 value. + /// + + [CLSCompliant(false)] + public PyLong(ulong value) : base() { + obj = Runtime.PyLong_FromUnsignedLongLong(value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from an int16 value. + /// + + public PyLong(short value) : base() { + obj = Runtime.PyLong_FromLong((long)value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from an uint16 value. + /// + + [CLSCompliant(false)] + public PyLong(ushort value) : base() { + obj = Runtime.PyLong_FromLong((long)value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from a byte value. + /// + + public PyLong(byte value) : base() { + obj = Runtime.PyLong_FromLong((long)value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from an sbyte value. + /// + + [CLSCompliant(false)] + public PyLong(sbyte value) : base() { + obj = Runtime.PyLong_FromLong((long)value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from an double value. + /// + + public PyLong(double value) : base() { + obj = Runtime.PyLong_FromDouble(value); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyLong Constructor + /// + /// + /// + /// Creates a new PyLong from a string value. + /// + + public PyLong(string value) : base() { + obj = Runtime.PyLong_FromString(value, IntPtr.Zero, 0); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// IsLongType Method + /// + /// + /// + /// Returns true if the given object is a Python long. + /// + + public static bool IsLongType(PyObject value) { + return Runtime.PyLong_Check(value.obj); + } + + + /// + /// AsLong Method + /// + /// + /// + /// + /// Convert a Python object to a Python long if possible, raising + /// a PythonException if the conversion is not possible. This is + /// equivalent to the Python expression "long(object)". + /// + + public static PyLong AsLong(PyObject value) { + IntPtr op = Runtime.PyNumber_Long(value.obj); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyLong(op); + } + + /// + /// ToInt16 Method + /// + /// + /// + /// Return the value of the Python long object as an int16. + /// + + public short ToInt16() + { + return System.Convert.ToInt16(this.ToInt64()); + } + + + /// + /// ToInt32 Method + /// + /// + /// + /// Return the value of the Python long object as an int32. + /// + + public int ToInt32() + { + return System.Convert.ToInt32(this.ToInt64()); + } + + + /// + /// ToInt64 Method + /// + /// + /// + /// Return the value of the Python long object as an int64. + /// + + public long ToInt64() + { + return Runtime.PyLong_AsLongLong(obj); + } + } + +} diff --git a/Pythonnet.Runtime/pynumber.cs b/Pythonnet.Runtime/pynumber.cs new file mode 100644 index 0000000000..16927dac30 --- /dev/null +++ b/Pythonnet.Runtime/pynumber.cs @@ -0,0 +1,47 @@ +// ========================================================================== +// 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; + +namespace Python.Runtime { + + /// + /// Represents a generic Python number. The methods of this class are + /// equivalent to the Python "abstract number API". See + /// http://www.python.org/doc/current/api/number.html for details. + /// + + public class PyNumber : PyObject { + + protected PyNumber(IntPtr ptr) : base(ptr) {} + + protected PyNumber() : base() {} + + + /// + /// IsNumberType Method + /// + /// + /// + /// Returns true if the given object is a Python numeric type. + /// + + public static bool IsNumberType(PyObject value) { + return Runtime.PyNumber_Check(value.obj); + } + + + // TODO: add all of the PyNumber_XXX methods. + + + + + } + +} diff --git a/Pythonnet.Runtime/pyobject.cs b/Pythonnet.Runtime/pyobject.cs new file mode 100644 index 0000000000..099b9fdf46 --- /dev/null +++ b/Pythonnet.Runtime/pyobject.cs @@ -0,0 +1,869 @@ +// ========================================================================== +// 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; + +namespace Python.Runtime { + + /// + /// Represents a generic Python object. The methods of this class are + /// generally equivalent to the Python "abstract object API". See + /// http://www.python.org/doc/current/api/object.html for details. + /// + + public class PyObject : IDisposable { + + protected internal IntPtr obj = IntPtr.Zero; + private bool disposed = false; + + /// + /// PyObject Constructor + /// + /// + /// + /// Creates a new PyObject from an IntPtr object reference. Note that + /// the PyObject instance assumes ownership of the object reference + /// and the reference will be DECREFed when the PyObject is garbage + /// collected or explicitly disposed. + /// + + public PyObject(IntPtr ptr) { + obj = ptr; + } + + // Protected default constructor to allow subclasses to manage + // initialization in different ways as appropriate. + + protected PyObject() {} + + // Ensure that encapsulated Python object is decref'ed appropriately + // when the managed wrapper is garbage-collected. + + ~PyObject() { + Dispose(); + } + + + /// + /// Handle Property + /// + /// + /// + /// Gets the native handle of the underlying Python object. This + /// value is generally for internal use by the PythonNet runtime. + /// + + public IntPtr Handle { + get { return obj; } + } + + + /// + /// FromManagedObject Method + /// + /// + /// + /// Given an arbitrary managed object, return a Python instance that + /// reflects the managed object. + /// + + public static PyObject FromManagedObject(object ob) { + // Special case: if ob is null, we return None. + if (ob == null) { + Runtime.Incref(Runtime.PyNone); + return new PyObject(Runtime.PyNone); + } + IntPtr op = CLRObject.GetInstHandle(ob); + return new PyObject(op); + } + + + /// + /// AsManagedObject Method + /// + /// + /// + /// Return a managed object of the given type, based on the + /// value of the Python object. + /// + + public object AsManagedObject(Type t) { + Object result; + if (!Converter.ToManaged(this.Handle, t, out result, false)) { + throw new InvalidCastException("cannot convert object to target type"); + } + return result; + } + + + /// + /// Dispose Method + /// + /// + /// + /// The Dispose method provides a way to explicitly release the + /// Python object represented by a PyObject instance. It is a good + /// idea to call Dispose on PyObjects that wrap resources that are + /// limited or need strict lifetime control. Otherwise, references + /// to Python objects will not be released until a managed garbage + /// collection occurs. + /// + + public void Dispose() { + if (!disposed) { + if (Runtime.Py_IsInitialized() > 0) { + IntPtr gs = PythonEngine.AcquireLock(); + Runtime.Decref(obj); + obj = IntPtr.Zero; + PythonEngine.ReleaseLock(gs); + } + GC.SuppressFinalize(this); + disposed = true; + } + } + + + /// + /// GetPythonType Method + /// + /// + /// + /// Returns the Python type of the object. This method is equivalent + /// to the Python expression: type(object). + /// + + public PyObject GetPythonType() { + IntPtr tp = Runtime.PyObject_Type(obj); + return new PyObject(tp); + } + + + /// + /// TypeCheck Method + /// + /// + /// + /// Returns true if the object o is of type typeOrClass or a subtype + /// of typeOrClass. + /// + + public bool TypeCheck(PyObject typeOrClass) { + return Runtime.PyObject_TypeCheck(obj, typeOrClass.obj); + } + + + /// + /// HasAttr Method + /// + /// + /// + /// Returns true if the object has an attribute with the given name. + /// + + public bool HasAttr(string name) { + return (Runtime.PyObject_HasAttrString(obj, name) != 0); + } + + + /// + /// HasAttr Method + /// + /// + /// + /// Returns true if the object has an attribute with the given name, + /// where name is a PyObject wrapping a string or unicode object. + /// + + public bool HasAttr(PyObject name) { + return (Runtime.PyObject_HasAttr(obj, name.obj) != 0); + } + + + /// + /// GetAttr Method + /// + /// + /// + /// Returns the named attribute of the Python object, or raises a + /// PythonException if the attribute access fails. + /// + + public PyObject GetAttr(string name) { + IntPtr op = Runtime.PyObject_GetAttrString(obj, name); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(op); + } + + + /// + /// GetAttr Method + /// + /// + /// + /// Returns the named attribute of the Python object, or the given + /// default object if the attribute access fails. + /// + + public PyObject GetAttr(string name, PyObject _default) { + IntPtr op = Runtime.PyObject_GetAttrString(obj, name); + if (op == IntPtr.Zero) { + Runtime.PyErr_Clear(); + return _default; + } + return new PyObject(op); + } + + + /// + /// GetAttr Method + /// + /// + /// + /// Returns the named attribute of the Python object or raises a + /// PythonException if the attribute access fails. The name argument + /// is a PyObject wrapping a Python string or unicode object. + /// + + public PyObject GetAttr(PyObject name) { + IntPtr op = Runtime.PyObject_GetAttr(obj, name.obj); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(op); + } + + + /// + /// GetAttr Method + /// + /// + /// + /// Returns the named attribute of the Python object, or the given + /// default object if the attribute access fails. The name argument + /// is a PyObject wrapping a Python string or unicode object. + /// + + public PyObject GetAttr(PyObject name, PyObject _default) { + IntPtr op = Runtime.PyObject_GetAttr(obj, name.obj); + if (op == IntPtr.Zero) { + Runtime.PyErr_Clear(); + return _default; + } + return new PyObject(op); + } + + + /// + /// SetAttr Method + /// + /// + /// + /// Set an attribute of the object with the given name and value. This + /// method throws a PythonException if the attribute set fails. + /// + + public void SetAttr(string name, PyObject value) { + int r = Runtime.PyObject_SetAttrString(obj, name, value.obj); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// SetAttr Method + /// + /// + /// + /// Set an attribute of the object with the given name and value, + /// where the name is a Python string or unicode object. This method + /// throws a PythonException if the attribute set fails. + /// + + public void SetAttr(PyObject name, PyObject value) { + int r = Runtime.PyObject_SetAttr(obj, name.obj, value.obj); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// DelAttr Method + /// + /// + /// + /// Delete the named attribute of the Python object. This method + /// throws a PythonException if the attribute set fails. + /// + + public void DelAttr(string name) { + int r = Runtime.PyObject_SetAttrString(obj, name, IntPtr.Zero); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// DelAttr Method + /// + /// + /// + /// Delete the named attribute of the Python object, where name is a + /// PyObject wrapping a Python string or unicode object. This method + /// throws a PythonException if the attribute set fails. + /// + + public void DelAttr(PyObject name) { + int r = Runtime.PyObject_SetAttr(obj, name.obj, IntPtr.Zero); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// GetItem Method + /// + /// + /// + /// For objects that support the Python sequence or mapping protocols, + /// return the item at the given object index. This method raises a + /// PythonException if the indexing operation fails. + /// + + public virtual PyObject GetItem(PyObject key) { + IntPtr op = Runtime.PyObject_GetItem(obj, key.obj); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(op); + } + + + /// + /// GetItem Method + /// + /// + /// + /// For objects that support the Python sequence or mapping protocols, + /// return the item at the given string index. This method raises a + /// PythonException if the indexing operation fails. + /// + + public virtual PyObject GetItem(string key) { + return GetItem(new PyString(key)); + } + + + /// + /// GetItem Method + /// + /// + /// + /// For objects that support the Python sequence or mapping protocols, + /// return the item at the given numeric index. This method raises a + /// PythonException if the indexing operation fails. + /// + + public virtual PyObject GetItem(int index) { + PyInt key = new PyInt(index); + return GetItem((PyObject)key); + } + + + /// + /// SetItem Method + /// + /// + /// + /// For objects that support the Python sequence or mapping protocols, + /// set the item at the given object index to the given value. This + /// method raises a PythonException if the set operation fails. + /// + + public virtual void SetItem(PyObject key, PyObject value) { + int r = Runtime.PyObject_SetItem(obj, key.obj, value.obj); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// SetItem Method + /// + /// + /// + /// For objects that support the Python sequence or mapping protocols, + /// set the item at the given string index to the given value. This + /// method raises a PythonException if the set operation fails. + /// + + public virtual void SetItem(string key, PyObject value) { + SetItem(new PyString(key), value); + } + + + /// + /// SetItem Method + /// + /// + /// + /// For objects that support the Python sequence or mapping protocols, + /// set the item at the given numeric index to the given value. This + /// method raises a PythonException if the set operation fails. + /// + + public virtual void SetItem(int index, PyObject value) { + SetItem(new PyInt(index), value); + } + + + /// + /// DelItem Method + /// + /// + /// + /// For objects that support the Python sequence or mapping protocols, + /// delete the item at the given object index. This method raises a + /// PythonException if the delete operation fails. + /// + + public virtual void DelItem(PyObject key) { + int r = Runtime.PyObject_DelItem(obj, key.obj); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// DelItem Method + /// + /// + /// + /// For objects that support the Python sequence or mapping protocols, + /// delete the item at the given string index. This method raises a + /// PythonException if the delete operation fails. + /// + + public virtual void DelItem(string key) { + DelItem(new PyString(key)); + } + + + /// + /// DelItem Method + /// + /// + /// + /// For objects that support the Python sequence or mapping protocols, + /// delete the item at the given numeric index. This method raises a + /// PythonException if the delete operation fails. + /// + + public virtual void DelItem(int index) { + DelItem(new PyInt(index)); + } + + + /// + /// Length Method + /// + /// + /// + /// Returns the length for objects that support the Python sequence + /// protocol, or 0 if the object does not support the protocol. + /// + + public virtual int Length() { + int s = Runtime.PyObject_Size(obj); + if (s < 0) { + Runtime.PyErr_Clear(); + return 0; + } + return s; + } + + + /// + /// String Indexer + /// + /// + /// + /// Provides a shorthand for the string versions of the GetItem and + /// SetItem methods. + /// + + public virtual PyObject this[string key] { + get { return GetItem(key); } + set { SetItem(key, value); } + } + + + /// + /// PyObject Indexer + /// + /// + /// + /// Provides a shorthand for the object versions of the GetItem and + /// SetItem methods. + /// + + public virtual PyObject this[PyObject key] { + get { return GetItem(key); } + set { SetItem(key, value); } + } + + + /// + /// Numeric Indexer + /// + /// + /// + /// Provides a shorthand for the numeric versions of the GetItem and + /// SetItem methods. + /// + + public virtual PyObject this[int index] { + get { return GetItem(index); } + set { SetItem(index, value); } + } + + + /// + /// GetIterator Method + /// + /// + /// + /// Return a new (Python) iterator for the object. This is equivalent + /// to the Python expression "iter(object)". A PythonException will be + /// raised if the object cannot be iterated. + /// + + public PyObject GetIterator() { + IntPtr r = Runtime.PyObject_GetIter(obj); + if (r == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(r); + } + + + /// + /// Invoke Method + /// + /// + /// + /// Invoke the callable object with the given arguments, passed as a + /// PyObject[]. A PythonException is raised if the invokation fails. + /// + + public PyObject Invoke(params PyObject[] args) { + PyTuple t = new PyTuple(args); + IntPtr r = Runtime.PyObject_Call(obj, t.obj, IntPtr.Zero); + t.Dispose(); + if (r == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(r); + } + + + /// + /// Invoke Method + /// + /// + /// + /// Invoke the callable object with the given arguments, passed as a + /// Python tuple. A PythonException is raised if the invokation fails. + /// + + public PyObject Invoke(PyTuple args) { + IntPtr r = Runtime.PyObject_Call(obj, args.obj, IntPtr.Zero); + if (r == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(r); + } + + + /// + /// Invoke Method + /// + /// + /// + /// Invoke the callable object with the given positional and keyword + /// arguments. A PythonException is raised if the invokation fails. + /// + + public PyObject Invoke(PyObject[] args, PyDict kw) { + PyTuple t = new PyTuple(args); + IntPtr r = Runtime.PyObject_Call(obj, t.obj, kw.obj); + t.Dispose(); + if (r == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(r); + } + + + /// + /// Invoke Method + /// + /// + /// + /// Invoke the callable object with the given positional and keyword + /// arguments. A PythonException is raised if the invokation fails. + /// + + public PyObject Invoke(PyTuple args, PyDict kw) { + IntPtr r = Runtime.PyObject_Call(obj, args.obj, kw.obj); + if (r == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(r); + } + + + /// + /// InvokeMethod Method + /// + /// + /// + /// Invoke the named method of the object with the given arguments. + /// A PythonException is raised if the invokation is unsuccessful. + /// + + public PyObject InvokeMethod(string name, params PyObject[] args) { + PyObject method = GetAttr(name); + PyObject result = method.Invoke(args); + method.Dispose(); + return result; + } + + + /// + /// InvokeMethod Method + /// + /// + /// + /// Invoke the named method of the object with the given arguments. + /// A PythonException is raised if the invokation is unsuccessful. + /// + + public PyObject InvokeMethod(string name, PyTuple args) { + PyObject method = GetAttr(name); + PyObject result = method.Invoke(args); + method.Dispose(); + return result; + } + + + /// + /// InvokeMethod Method + /// + /// + /// + /// Invoke the named method of the object with the given arguments + /// and keyword arguments. Keyword args are passed as a PyDict object. + /// A PythonException is raised if the invokation is unsuccessful. + /// + + public PyObject InvokeMethod(string name, PyObject[] args, PyDict kw) { + PyObject method = GetAttr(name); + PyObject result = method.Invoke(args, kw); + method.Dispose(); + return result; + } + + + /// + /// InvokeMethod Method + /// + /// + /// + /// Invoke the named method of the object with the given arguments + /// and keyword arguments. Keyword args are passed as a PyDict object. + /// A PythonException is raised if the invokation is unsuccessful. + /// + + public PyObject InvokeMethod(string name, PyTuple args, PyDict kw) { + PyObject method = GetAttr(name); + PyObject result = method.Invoke(args, kw); + method.Dispose(); + return result; + } + + + /// + /// IsInstance Method + /// + /// + /// + /// Return true if the object is an instance of the given Python type + /// or class. This method always succeeds. + /// + + public bool IsInstance(PyObject typeOrClass) { + int r = Runtime.PyObject_IsInstance(obj, typeOrClass.obj); + if (r < 0) { + Runtime.PyErr_Clear(); + return false; + } + return (r != 0); + } + + + /// + /// IsSubclass Method + /// + /// + /// + /// Return true if the object is identical to or derived from the + /// given Python type or class. This method always succeeds. + /// + + public bool IsSubclass(PyObject typeOrClass) { + int r = Runtime.PyObject_IsSubclass(obj, typeOrClass.obj); + if (r < 0) { + Runtime.PyErr_Clear(); + return false; + } + return (r != 0); + } + + + /// + /// IsCallable Method + /// + /// + /// + /// Returns true if the object is a callable object. This method + /// always succeeds. + /// + + public bool IsCallable() { + return (Runtime.PyCallable_Check(obj) != 0); + } + + + /// + /// IsTrue Method + /// + /// + /// + /// Return true if the object is true according to Python semantics. + /// This method always succeeds. + /// + + public bool IsTrue() { + return (Runtime.PyObject_IsTrue(obj) != 0); + } + + + /// + /// Dir Method + /// + /// + /// + /// Return a list of the names of the attributes of the object. This + /// is equivalent to the Python expression "dir(object)". + /// + + public PyList Dir() { + IntPtr r = Runtime.PyObject_Dir(obj); + if (r == IntPtr.Zero) { + throw new PythonException(); + } + return new PyList(r); + } + + + /// + /// Repr Method + /// + /// + /// + /// Return a string representation of the object. This method is + /// the managed equivalent of the Python expression "repr(object)". + /// + + public string Repr() { + IntPtr strval = Runtime.PyObject_Repr(obj); + string result = Runtime.GetManagedString(strval); + Runtime.Decref(strval); + return result; + } + + + /// + /// ToString Method + /// + /// + /// + /// Return the string representation of the object. This method is + /// the managed equivalent of the Python expression "str(object)". + /// + + public override string ToString() { + IntPtr strval = Runtime.PyObject_Unicode(obj); + string result = Runtime.GetManagedString(strval); + Runtime.Decref(strval); + return result; + } + + + /// + /// Equals Method + /// + /// + /// + /// Return true if this object is equal to the given object. This + /// method is based on Python equality semantics. + /// + + public override bool Equals(object o) { + if (!(o is PyObject)) { + return false; + } + if (obj == ((PyObject) o).obj) { + return true; + } + int r = Runtime.PyObject_Compare(obj, ((PyObject) o).obj); + if (Exceptions.ErrorOccurred()) { + throw new PythonException(); + } + return (r == 0); + } + + + /// + /// GetHashCode Method + /// + /// + /// + /// Return a hashcode based on the Python object. This returns the + /// hash as computed by Python, equivalent to the Python expression + /// "hash(obj)". + /// + + public override int GetHashCode() { + return Runtime.PyObject_Hash(obj).ToInt32(); + } + + + } + + +} diff --git a/Pythonnet.Runtime/pysequence.cs b/Pythonnet.Runtime/pysequence.cs new file mode 100644 index 0000000000..9b41c308b2 --- /dev/null +++ b/Pythonnet.Runtime/pysequence.cs @@ -0,0 +1,172 @@ +// ========================================================================== +// 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.Collections; + +namespace Python.Runtime { + + /// + /// Represents a generic Python sequence. The methods of this class are + /// equivalent to the Python "abstract sequence API". See + /// http://www.python.org/doc/current/api/sequence.html for details. + /// + + public class PySequence : PyObject, IEnumerable { + + protected PySequence(IntPtr ptr) : base(ptr) {} + + protected PySequence() : base() {} + + + /// + /// IsSequenceType Method + /// + /// + /// + /// Returns true if the given object implements the sequence protocol. + /// + + public static bool IsSequenceType(PyObject value) { + return Runtime.PySequence_Check(value.obj); + } + + + /// + /// GetSlice Method + /// + /// + /// + /// Return the slice of the sequence with the given indices. + /// + + public PyObject GetSlice(int i1, int i2) { + IntPtr op = Runtime.PySequence_GetSlice(obj, i1, i2); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(op); + } + + + /// + /// SetSlice Method + /// + /// + /// + /// Sets the slice of the sequence with the given indices. + /// + + public void SetSlice(int i1, int i2, PyObject v) { + int r = Runtime.PySequence_SetSlice(obj, i1, i2, v.obj); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// DelSlice Method + /// + /// + /// + /// Deletes the slice of the sequence with the given indices. + /// + + public void DelSlice(int i1, int i2) { + int r = Runtime.PySequence_DelSlice(obj, i1, i2); + if (r < 0) { + throw new PythonException(); + } + } + + + /// + /// Index Method + /// + /// + /// + /// Return the index of the given item in the sequence, or -1 if + /// the item does not appear in the sequence. + /// + + public int Index(PyObject item) { + int r = Runtime.PySequence_Index(obj, item.obj); + if (r < 0) { + Runtime.PyErr_Clear(); + return -1; + } + return r; + } + + + /// + /// Contains Method + /// + /// + /// + /// Return true if the sequence contains the given item. This method + /// throws a PythonException if an error occurs during the check. + /// + + public bool Contains(PyObject item) { + int r = Runtime.PySequence_Contains(obj, item.obj); + if (r < 0) { + throw new PythonException(); + } + return (r != 0); + } + + + /// + /// Concat Method + /// + /// + /// + /// Return the concatenation of the sequence object with the passed in + /// sequence object. + /// + + public PyObject Concat(PyObject other) { + IntPtr op = Runtime.PySequence_Concat(obj, other.obj); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(op); + } + + + /// + /// Repeat Method + /// + /// + /// + /// Return the sequence object repeated N times. This is equivalent + /// to the Python expression "object * count". + /// + + public PyObject Repeat(int count) { + IntPtr op = Runtime.PySequence_Repeat(obj, count); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyObject(op); + } + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + return new PyIter(this); + } + + #endregion +} + +} diff --git a/Pythonnet.Runtime/pystring.cs b/Pythonnet.Runtime/pystring.cs new file mode 100644 index 0000000000..818e124cd5 --- /dev/null +++ b/Pythonnet.Runtime/pystring.cs @@ -0,0 +1,85 @@ +// ========================================================================== +// 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; + +namespace Python.Runtime { + + /// + /// Represents a Python (ansi) string object. See the documentation at + /// http://www.python.org/doc/current/api/stringObjects.html for details. + /// 2011-01-29: ...Then why does the string constructor call PyUnicode_FromUnicode()??? + /// + + public class PyString : PySequence { + + /// + /// PyString Constructor + /// + /// + /// + /// Creates a new PyString from an existing object reference. Note + /// that the instance assumes ownership of the object reference. + /// The object reference is not checked for type-correctness. + /// + + public PyString(IntPtr ptr) : base(ptr) {} + + + /// + /// PyString Constructor + /// + /// + /// + /// Copy constructor - obtain a PyString from a generic PyObject. + /// An ArgumentException will be thrown if the given object is not + /// a Python string object. + /// + + public PyString(PyObject o) : base() { + if (!IsStringType(o)) { + throw new ArgumentException("object is not a string"); + } + Runtime.Incref(o.obj); + obj = o.obj; + } + + + /// + /// PyString Constructor + /// + /// + /// + /// Creates a Python string from a managed string. + /// + + public PyString(string s) : base() { + obj = Runtime.PyUnicode_FromUnicode(s, s.Length); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// IsStringType Method + /// + /// + /// + /// Returns true if the given object is a Python string. + /// + + public static bool IsStringType(PyObject value) { + return Runtime.PyString_Check(value.obj); + } + + } + + +} diff --git a/Pythonnet.Runtime/pythonengine.cs b/Pythonnet.Runtime/pythonengine.cs new file mode 100644 index 0000000000..07326185f3 --- /dev/null +++ b/Pythonnet.Runtime/pythonengine.cs @@ -0,0 +1,355 @@ +// ========================================================================== +// 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); + } + + + + } + + +} diff --git a/Pythonnet.Runtime/pythonexception.cs b/Pythonnet.Runtime/pythonexception.cs new file mode 100644 index 0000000000..592e9ea371 --- /dev/null +++ b/Pythonnet.Runtime/pythonexception.cs @@ -0,0 +1,150 @@ +// ========================================================================== +// 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; + +namespace Python.Runtime { + + /// + /// Provides a managed interface to exceptions thrown by the Python + /// runtime. + /// + + public class PythonException : System.Exception { + + private IntPtr _pyType = IntPtr.Zero; + private IntPtr _pyValue = IntPtr.Zero; + private IntPtr _pyTB = IntPtr.Zero; + private string _tb = ""; + private string _message = ""; + private bool disposed = false; + + public PythonException() : base() + { + IntPtr gs = PythonEngine.AcquireLock(); + Runtime.PyErr_Fetch(ref _pyType, ref _pyValue, ref _pyTB); + Runtime.Incref(_pyType); + Runtime.Incref(_pyValue); + Runtime.Incref(_pyTB); + if ((_pyType != IntPtr.Zero) && (_pyValue != IntPtr.Zero)) + { + string type = new PyObject(_pyType).GetAttr("__name__").ToString(); + string message = Runtime.GetManagedString(_pyValue); + _message = type + " : " + message; + } + if (_pyTB != IntPtr.Zero) + { + PyObject tb_module = PythonEngine.ImportModule("traceback"); + _tb = tb_module.InvokeMethod("format_tb", new PyObject(_pyTB)).ToString(); + } + PythonEngine.ReleaseLock(gs); + } + + // Ensure that encapsulated Python objects are decref'ed appropriately + // when the managed exception wrapper is garbage-collected. + + ~PythonException() { + Dispose(); + } + + + /// + /// PyType Property + /// + /// + /// + /// Returns the exception type as a Python object. + /// + + public IntPtr PyType + { + get { return _pyType; } + } + + /// + /// PyValue Property + /// + /// + /// + /// Returns the exception value as a Python object. + /// + + public IntPtr PyValue + { + get { return _pyValue; } + } + + /// + /// Message Property + /// + /// + /// + /// A string representing the python exception message. + /// + + public override string Message + { + get { return _message; } + } + + /// + /// StackTrace Property + /// + /// + /// + /// A string representing the python exception stack trace. + /// + + public override string StackTrace + { + get { return _tb; } + } + + + /// + /// Dispose Method + /// + /// + /// + /// The Dispose method provides a way to explicitly release the + /// Python objects represented by a PythonException. + /// + + public void Dispose() { + if (!disposed) { + if (Runtime.Py_IsInitialized() > 0) { + IntPtr gs = PythonEngine.AcquireLock(); + Runtime.Decref(_pyType); + Runtime.Decref(_pyValue); + // XXX Do we ever get TraceBack? // + if (_pyTB != IntPtr.Zero) { + Runtime.Decref(_pyTB); + } + PythonEngine.ReleaseLock(gs); + } + GC.SuppressFinalize(this); + disposed = true; + } + } + + /// + /// Matches Method + /// + /// + /// + /// Returns true if the Python exception type represented by the + /// PythonException instance matches the given exception type. + /// + + public static bool Matches(IntPtr ob) { + return Runtime.PyErr_ExceptionMatches(ob) != 0; + } + + } +} diff --git a/Pythonnet.Runtime/pytuple.cs b/Pythonnet.Runtime/pytuple.cs new file mode 100644 index 0000000000..cac41acf44 --- /dev/null +++ b/Pythonnet.Runtime/pytuple.cs @@ -0,0 +1,126 @@ +// ========================================================================== +// 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; + +namespace Python.Runtime { + + /// + /// Represents a Python tuple object. See the documentation at + /// http://www.python.org/doc/current/api/tupleObjects.html for details. + /// + + public class PyTuple : PySequence { + + /// + /// PyTuple Constructor + /// + /// + /// + /// Creates a new PyTuple from an existing object reference. Note + /// that the instance assumes ownership of the object reference. + /// The object reference is not checked for type-correctness. + /// + + public PyTuple(IntPtr ptr) : base(ptr) {} + + + /// + /// PyTuple Constructor + /// + /// + /// + /// Copy constructor - obtain a PyTuple from a generic PyObject. An + /// ArgumentException will be thrown if the given object is not a + /// Python tuple object. + /// + + public PyTuple(PyObject o) : base() { + if (!IsTupleType(o)) { + throw new ArgumentException("object is not a tuple"); + } + Runtime.Incref(o.obj); + obj = o.obj; + } + + + /// + /// PyTuple Constructor + /// + /// + /// + /// Creates a new empty PyTuple. + /// + + public PyTuple() : base() { + obj = Runtime.PyTuple_New(0); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } + } + + + /// + /// PyTuple Constructor + /// + /// + /// + /// Creates a new PyTuple from an array of PyObject instances. + /// + + public PyTuple(PyObject[] items) : base() { + int count = items.Length; + obj = Runtime.PyTuple_New(count); + for (int i = 0; i < count; i++) { + IntPtr ptr = items[i].obj; + Runtime.Incref(ptr); + int r = Runtime.PyTuple_SetItem(obj, i, ptr); + if (r < 0) { + throw new PythonException(); + } + } + } + + + /// + /// IsTupleType Method + /// + /// + /// + /// Returns true if the given object is a Python tuple. + /// + + public static bool IsTupleType(PyObject value) { + return Runtime.PyTuple_Check(value.obj); + } + + + /// + /// AsTuple Method + /// + /// + /// + /// Convert a Python object to a Python tuple if possible, raising + /// a PythonException if the conversion is not possible. This is + /// equivalent to the Python expression "tuple(object)". + /// + + public static PyTuple AsTuple(PyObject value) { + IntPtr op = Runtime.PySequence_Tuple(value.obj); + if (op == IntPtr.Zero) { + throw new PythonException(); + } + return new PyTuple(op); + } + + + } + + +} diff --git a/Pythonnet.Runtime/runtime.cs b/Pythonnet.Runtime/runtime.cs new file mode 100644 index 0000000000..49cf58b4be --- /dev/null +++ b/Pythonnet.Runtime/runtime.cs @@ -0,0 +1,1601 @@ +// ========================================================================== +// 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.Runtime.InteropServices; +using System.Security; +#if (UCS4) +using System.Text; +using Mono.Unix; +#endif + +namespace Python.Runtime { + + [SuppressUnmanagedCodeSecurityAttribute()] + + public class 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. + /// +#if (UCS4) + public const int UCS = 4; +#endif +#if (UCS2) + public const int UCS = 2; +#endif +#if ! (UCS2 || UCS4) +#error You must define either UCS2 or UCS4! +#endif + +#if (PYTHON23) + public const string dll = "python23"; + public const string pyversion = "2.3"; + public const int pyversionnumber = 23; +#endif +#if (PYTHON24) + public const string dll = "python24"; + public const string pyversion = "2.4"; + public const int pyversionnumber = 24; +#endif +#if (PYTHON25) + public const string dll = "python25"; + public const string pyversion = "2.5"; + public const int pyversionnumber = 25; +#endif +#if (PYTHON26) + public const string dll = "python26"; + public const string pyversion = "2.6"; + public const int pyversionnumber = 26; +#endif +#if (PYTHON27) + public const string dll = "python27"; + public const string pyversion = "2.7"; + public const int pyversionnumber = 27; +#endif +#if ! (PYTHON23 || PYTHON24 || PYTHON25 || PYTHON26 || PYTHON27) +#error You must define one of PYTHON23 to PYTHON27 +#endif + + public static bool wrap_exceptions; + public static bool is32bit; + + /// + /// Intitialize the runtime... + /// + public static void Initialize() { + + is32bit = IntPtr.Size == 4; + + Runtime.Py_Initialize(); + Runtime.PyEval_InitThreads(); + + IntPtr dict = Runtime.PyImport_GetModuleDict(); + IntPtr op = Runtime.PyDict_GetItemString(dict, "__builtin__"); + + PyBaseObjectType = Runtime.PyObject_GetAttrString(op, "object"); + + PyModuleType = Runtime.PyObject_Type(op); + PyNone = Runtime.PyObject_GetAttrString(op, "None"); + PyTrue = Runtime.PyObject_GetAttrString(op, "True"); + PyFalse = Runtime.PyObject_GetAttrString(op, "False"); + + PyBoolType = Runtime.PyObject_Type(PyTrue); + PyNoneType = Runtime.PyObject_Type(PyNone); + PyTypeType = Runtime.PyObject_Type(PyNoneType); + + op = Runtime.PyObject_GetAttrString(dict, "keys"); + PyMethodType = Runtime.PyObject_Type(op); + Runtime.Decref(op); + + op = Runtime.PyString_FromString("string"); + PyStringType = Runtime.PyObject_Type(op); + Runtime.Decref(op); + + op = Runtime.PyUnicode_FromString("unicode"); + PyUnicodeType = Runtime.PyObject_Type(op); + Runtime.Decref(op); + + op = Runtime.PyTuple_New(0); + PyTupleType = Runtime.PyObject_Type(op); + Runtime.Decref(op); + + op = Runtime.PyList_New(0); + PyListType = Runtime.PyObject_Type(op); + Runtime.Decref(op); + + op = Runtime.PyDict_New(); + PyDictType = Runtime.PyObject_Type(op); + Runtime.Decref(op); + + op = Runtime.PyInt_FromInt32(0); + PyIntType = Runtime.PyObject_Type(op); + Runtime.Decref(op); + + op = Runtime.PyLong_FromLong(0); + PyLongType = Runtime.PyObject_Type(op); + Runtime.Decref(op); + + op = Runtime.PyFloat_FromDouble(0); + PyFloatType = Runtime.PyObject_Type(op); + Runtime.Decref(op); + + IntPtr s = Runtime.PyString_FromString("_temp"); + IntPtr d = Runtime.PyDict_New(); + IntPtr c = Runtime.PyClass_New(IntPtr.Zero, d, s); + PyClassType = Runtime.PyObject_Type(c); + + IntPtr i = Runtime.PyInstance_New(c, IntPtr.Zero, IntPtr.Zero); + PyInstanceType = Runtime.PyObject_Type(i); + + Runtime.Decref(s); + Runtime.Decref(i); + Runtime.Decref(c); + Runtime.Decref(d); + + Error = new IntPtr(-1); + + // Determine whether we need to wrap exceptions for versions of + // of the Python runtime that do not allow new-style classes to + // be used as exceptions (Python versions 2.4 and lower). + +#if (PYTHON25 || PYTHON26 || PYTHON27) + wrap_exceptions = false; +#else + IntPtr m = PyImport_ImportModule("exceptions"); + Exceptions.ErrorCheck(m); + op = Runtime.PyObject_GetAttrString(m, "Exception"); + Exceptions.ErrorCheck(op); + if (Runtime.PyObject_TYPE(op) == PyClassType) { + wrap_exceptions = true; + } + Runtime.Decref(op); + Runtime.Decref(m); +#endif + + // 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 = Runtime.PySys_GetObject("path"); + IntPtr item = Runtime.PyString_FromString(rtdir); + Runtime.PyList_Append(path, item); + Runtime.Decref(item); + AssemblyManager.UpdatePath(); + } + + public static void Shutdown() { + AssemblyManager.Shutdown(); + Exceptions.Shutdown(); + ImportHook.Shutdown(); + Py_Finalize(); + } + + public static IntPtr Py_single_input = (IntPtr)256; + public static IntPtr Py_file_input = (IntPtr)257; + public static IntPtr Py_eval_input = (IntPtr)258; + + public static IntPtr PyBaseObjectType; + public static IntPtr PyModuleType; + public static IntPtr PyClassType; + public static IntPtr PyInstanceType; + public static IntPtr PyCLRMetaType; + public static IntPtr PyMethodType; + + public static IntPtr PyUnicodeType; + public static IntPtr PyStringType; + public static IntPtr PyTupleType; + public static IntPtr PyListType; + public static IntPtr PyDictType; + public static IntPtr PyIntType; + public static IntPtr PyLongType; + public static IntPtr PyFloatType; + public static IntPtr PyBoolType; + public static IntPtr PyNoneType; + public static IntPtr PyTypeType; + + public static IntPtr PyTrue; + public static IntPtr PyFalse; + public static IntPtr PyNone; + public static IntPtr Error; + + + + public static IntPtr GetBoundArgTuple(IntPtr obj, IntPtr args) { + if (Runtime.PyObject_TYPE(args) != Runtime.PyTupleType) { + Exceptions.SetError(Exceptions.TypeError, "tuple expected"); + return IntPtr.Zero; + } + int size = Runtime.PyTuple_Size(args); + IntPtr items = Runtime.PyTuple_New(size + 1); + Runtime.PyTuple_SetItem(items, 0, obj); + Runtime.Incref(obj); + + for (int i = 0; i < size; i++) { + IntPtr item = Runtime.PyTuple_GetItem(args, i); + Runtime.Incref(item); + Runtime.PyTuple_SetItem(items, i + 1, item); + } + + return items; + } + + + public static IntPtr ExtendTuple(IntPtr t, params IntPtr[] args) { + int size = Runtime.PyTuple_Size(t); + int add = args.Length; + IntPtr item; + + IntPtr items = Runtime.PyTuple_New(size + add); + for (int i = 0; i < size; i++) { + item = Runtime.PyTuple_GetItem(t, i); + Runtime.Incref(item); + Runtime.PyTuple_SetItem(items, i, item); + } + + for (int n = 0; n < add; n++) { + item = args[n]; + Runtime.Incref(item); + Runtime.PyTuple_SetItem(items, size + n, item); + } + + return items; + } + + public static Type[] PythonArgsToTypeArray(IntPtr arg) { + return PythonArgsToTypeArray(arg, false); + } + + public 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; + bool free = false; + + if (!Runtime.PyTuple_Check(arg)) { + args = Runtime.PyTuple_New(1); + Runtime.Incref(arg); + Runtime.PyTuple_SetItem(args, 0, arg); + free = true; + } + + int n = Runtime.PyTuple_Size(args); + Type[] types = new Type[n]; + Type t = null; + + for (int i = 0; i < n; i++) { + IntPtr op = Runtime.PyTuple_GetItem(args, i); + if (mangleObjects && (!Runtime.PyType_Check(op))) { + op = Runtime.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) { + Runtime.Decref(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). + //=================================================================== + + public unsafe static void Incref(IntPtr op) { +#if (Py_DEBUG) + Py_IncRef(op); + return; +#else + void *p = (void *)op; + if ((void *)0 != p) { + if (is32bit) { (*(int *)p)++; } + else { (*(long *)p)++; } + } +#endif + } + + public unsafe static void Decref(IntPtr op) { + if (op == IntPtr.Zero) { + DebugUtil.Print("Decref(NULL)"); + } +#if (Py_DEBUG) + // Py_DecRef calls Python's Py_DECREF + Py_DecRef(op); + return; +#else + void* p = (void*) op; + if ((void*) 0 != p) { + if (is32bit) { --(*(int*) p); } + else { --(*(long*) p); } + if ((*(int*) p) == 0) { + // PyObject_HEAD: struct _typeobject *ob_type + void* t = is32bit ? (void*) (*((uint*) p + 1)) : + (void*) (*((ulong*) p + 1)); + // PyTypeObject: destructor tp_dealloc + void* f = is32bit ? (void*) (*((uint*) t + 6)) : + (void*) (*((ulong*) t + 6)); + if ((void*) 0 == f) { + return; + } + NativeCall.Impl.Void_Call_1(new IntPtr(f), op); + return; + } + } +#endif + } + +#if (Py_DEBUG) + // Py_IncRef and Py_DecRef are taking care of the extra payload + // in Py_DEBUG builds of Python like _Py_RefTotal + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + private unsafe static extern void + Py_IncRef(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + private unsafe static extern void + Py_DecRef(IntPtr ob); +#endif + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + Py_Initialize(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + Py_IsInitialized(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + Py_Finalize(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + Py_NewInterpreter(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + Py_EndInterpreter(IntPtr threadState); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyThreadState_New(IntPtr istate); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyThreadState_Get(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyThread_get_key_value(IntPtr key); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyThread_get_thread_ident(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyThread_set_key_value(IntPtr key, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyThreadState_Swap(IntPtr key); + + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyGILState_Ensure(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyGILState_Release(IntPtr gs); + + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyGILState_GetThisThreadState(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + Py_Main(int argc, string[] argv); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyEval_InitThreads(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyEval_AcquireLock(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyEval_ReleaseLock(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyEval_AcquireThread(IntPtr tstate); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyEval_ReleaseThread(IntPtr tstate); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyEval_SaveThread(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyEval_RestoreThread(IntPtr tstate); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyEval_GetBuiltins(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyEval_GetGlobals(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyEval_GetLocals(); + + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern string + Py_GetProgramName(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + Py_SetProgramName(string name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern string + Py_GetPythonHome(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + Py_SetPythonHome(string home); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern string + Py_GetVersion(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern string + Py_GetPlatform(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern string + Py_GetCopyright(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern string + Py_GetCompiler(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern string + Py_GetBuildInfo(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyRun_SimpleString(string code); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + Py_CompileString(string code, string file, IntPtr tok); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyImport_ExecCodeModule(string name, IntPtr code); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyCFunction_New(IntPtr ml, IntPtr self); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyCFunction_NewEx(IntPtr ml, IntPtr self, IntPtr mod); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyCFunction_Call(IntPtr func, IntPtr args, IntPtr kw); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyClass_New(IntPtr bases, IntPtr dict, IntPtr name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyInstance_New(IntPtr cls, IntPtr args, IntPtr kw); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyInstance_NewRaw(IntPtr cls, IntPtr dict); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyMethod_New(IntPtr func, IntPtr self, IntPtr cls); + + + //==================================================================== + // 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. + + public unsafe static IntPtr + PyObject_TYPE(IntPtr op) { + void* p = (void*)op; + if ((void*)0 == p) { + return IntPtr.Zero; + } +#if (Py_DEBUG) + int n = 3; +#else + int n = 1; +#endif + if (is32bit) { + return new IntPtr((void*)(*((uint*)p + n))); + } + else { + return 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. + + public unsafe static IntPtr + PyObject_Type(IntPtr op) { + IntPtr tp = PyObject_TYPE(op); + Runtime.Incref(tp); + return tp; + } + + public 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); + } + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_HasAttrString(IntPtr pointer, string name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_GetAttrString(IntPtr pointer, string name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_SetAttrString(IntPtr pointer, string name, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_HasAttr(IntPtr pointer, IntPtr name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_GetAttr(IntPtr pointer, IntPtr name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_SetAttr(IntPtr pointer, IntPtr name, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_GetItem(IntPtr pointer, IntPtr key); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_SetItem(IntPtr pointer, IntPtr key, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_DelItem(IntPtr pointer, IntPtr key); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_GetIter(IntPtr op); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_Call(IntPtr pointer, IntPtr args, IntPtr kw); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_CallObject(IntPtr pointer, IntPtr args); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_Compare(IntPtr value1, IntPtr value2); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_IsInstance(IntPtr ob, IntPtr type); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_IsSubclass(IntPtr ob, IntPtr type); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyCallable_Check(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_IsTrue(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_Size(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_Hash(IntPtr op); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_Repr(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_Str(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_Unicode(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_Dir(IntPtr pointer); + + + //==================================================================== + // Python number API + //==================================================================== + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyNumber_Int(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyNumber_Long(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyNumber_Float(IntPtr ob); + + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern bool + PyNumber_Check(IntPtr ob); + + + public static bool PyInt_Check(IntPtr ob) { + return PyObject_TypeCheck(ob, Runtime.PyIntType); + } + + public static bool PyBool_Check(IntPtr ob) { + return PyObject_TypeCheck(ob, Runtime.PyBoolType); + } + + + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + private unsafe static extern IntPtr + PyInt_FromLong(IntPtr value); + + public static IntPtr PyInt_FromInt32(int value) { + IntPtr v = new IntPtr(value); + return PyInt_FromLong(v); + } + + public static IntPtr PyInt_FromInt64(long value) { + IntPtr v = new IntPtr(value); + return PyInt_FromLong(v); + } + + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyInt_AsLong(IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyInt_FromString(string value, IntPtr end, int radix); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyInt_GetMax(); + + + public static bool PyLong_Check(IntPtr ob) { + return PyObject_TYPE(ob) == Runtime.PyLongType; + } + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyLong_FromLong(long value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyLong_FromUnsignedLong(uint value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyLong_FromDouble(double value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyLong_FromLongLong(long value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyLong_FromUnsignedLongLong(ulong value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyLong_FromString(string value, IntPtr end, int radix); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyLong_AsLong(IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern uint + PyLong_AsUnsignedLong(IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern long + PyLong_AsLongLong(IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern ulong + PyLong_AsUnsignedLongLong(IntPtr value); + + + public static bool PyFloat_Check(IntPtr ob) { + return PyObject_TYPE(ob) == Runtime.PyFloatType; + } + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyFloat_FromDouble(double value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyFloat_FromString(IntPtr value, IntPtr junk); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern double + PyFloat_AsDouble(IntPtr ob); + + + //==================================================================== + // Python sequence API + //==================================================================== + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern bool + PySequence_Check(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PySequence_GetItem(IntPtr pointer, int index); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PySequence_SetItem(IntPtr pointer, int index, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PySequence_DelItem(IntPtr pointer, int index); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PySequence_GetSlice(IntPtr pointer, int i1, int i2); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PySequence_SetSlice(IntPtr pointer, int i1, int i2, IntPtr v); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PySequence_DelSlice(IntPtr pointer, int i1, int i2); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PySequence_Size(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PySequence_Contains(IntPtr pointer, IntPtr item); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PySequence_Concat(IntPtr pointer, IntPtr other); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PySequence_Repeat(IntPtr pointer, int count); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PySequence_Index(IntPtr pointer, IntPtr item); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PySequence_Count(IntPtr pointer, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PySequence_Tuple(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PySequence_List(IntPtr pointer); + + + //==================================================================== + // Python string API + //==================================================================== + + public static bool IsStringType(IntPtr op) { + IntPtr t = PyObject_TYPE(op); + return (t == PyStringType) || (t == PyUnicodeType); + } + + public static bool PyString_Check(IntPtr ob) { + return PyObject_TYPE(ob) == Runtime.PyStringType; + } + + public static IntPtr PyString_FromString(string value) { + return PyString_FromStringAndSize(value, value.Length); + } + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyString_FromStringAndSize(string value, int size); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint="PyString_AsString", + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyString_AS_STRING(IntPtr op); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyString_Size(IntPtr pointer); + + public static bool PyUnicode_Check(IntPtr ob) { + return PyObject_TYPE(ob) == Runtime.PyUnicodeType; + } + +#if (UCS2) + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint="PyUnicodeUCS2_FromObject", + ExactSpelling=true, CharSet=CharSet.Unicode)] + public unsafe static extern IntPtr + PyUnicode_FromObject(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint="PyUnicodeUCS2_FromEncodedObject", + ExactSpelling=true, CharSet=CharSet.Unicode)] + public unsafe static extern IntPtr + PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint="PyUnicodeUCS2_FromUnicode", + ExactSpelling=true, CharSet=CharSet.Unicode)] + public unsafe static extern IntPtr + PyUnicode_FromUnicode(string s, int size); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint="PyUnicodeUCS2_GetSize", + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyUnicode_GetSize(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint="PyUnicodeUCS2_AsUnicode", + ExactSpelling=true)] + public unsafe static extern char * + PyUnicode_AsUnicode(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint="PyUnicodeUCS2_AsUnicode", + ExactSpelling=true, CharSet=CharSet.Unicode)] + public unsafe static extern IntPtr + PyUnicode_AS_UNICODE(IntPtr op); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint="PyUnicodeUCS2_FromOrdinal", + ExactSpelling=true, CharSet=CharSet.Unicode)] + public unsafe static extern IntPtr + PyUnicode_FromOrdinal(int c); + + public static IntPtr PyUnicode_FromString(string s) + { + return PyUnicode_FromUnicode(s, (s.Length)); + } + + public unsafe static string GetManagedString(IntPtr op) + { + IntPtr type = PyObject_TYPE(op); + + if (type == Runtime.PyStringType) + { + return Marshal.PtrToStringAnsi( + PyString_AS_STRING(op), + Runtime.PyString_Size(op) + ); + } + + if (type == Runtime.PyUnicodeType) + { + char* p = Runtime.PyUnicode_AsUnicode(op); + int size = Runtime.PyUnicode_GetSize(op); + return new String(p, 0, size); + } + + return null; + } + +#endif +#if (UCS4) + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicodeUCS4_FromObject", + ExactSpelling = true, CharSet = CharSet.Unicode)] + public unsafe static extern IntPtr + PyUnicode_FromObject(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicodeUCS4_FromEncodedObject", + ExactSpelling = true, CharSet = CharSet.Unicode)] + public unsafe static extern IntPtr + PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicodeUCS4_FromUnicode", + ExactSpelling = true)] + public unsafe static extern IntPtr + PyUnicode_FromUnicode( + [MarshalAs (UnmanagedType.CustomMarshaler, + MarshalTypeRef=typeof(Utf32Marshaler))] + string s, int size); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicodeUCS4_GetSize", + ExactSpelling = true, CharSet = CharSet.Ansi)] + public unsafe static extern int + PyUnicode_GetSize(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicodeUCS4_AsUnicode", + ExactSpelling = true)] + public unsafe static extern IntPtr + PyUnicode_AsUnicode(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicodeUCS4_AsUnicode", + ExactSpelling = true, CharSet = CharSet.Unicode)] + public unsafe static extern IntPtr + PyUnicode_AS_UNICODE(IntPtr op); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicodeUCS4_FromOrdinal", + ExactSpelling = true, CharSet = CharSet.Unicode)] + public unsafe static extern IntPtr + PyUnicode_FromOrdinal(int c); + + public static IntPtr PyUnicode_FromString(string s) + { + return PyUnicode_FromUnicode(s, (s.Length)); + } + + public unsafe static string GetManagedString(IntPtr op) + { + IntPtr type = PyObject_TYPE(op); + + if (type == Runtime.PyStringType) + { + return Marshal.PtrToStringAnsi( + PyString_AS_STRING(op), + Runtime.PyString_Size(op) + ); + } + + if (type == Runtime.PyUnicodeType) + { + IntPtr p = Runtime.PyUnicode_AsUnicode(op); + return UnixMarshal.PtrToString(p, Encoding.UTF32); + } + + return null; + } +#endif + + //==================================================================== + // Python dictionary API + //==================================================================== + + public static bool PyDict_Check(IntPtr ob) { + return PyObject_TYPE(ob) == Runtime.PyDictType; + } + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyDict_New(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyDictProxy_New(IntPtr dict); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyDict_GetItem(IntPtr pointer, IntPtr key); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyDict_GetItemString(IntPtr pointer, string key); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyDict_SetItem(IntPtr pointer, IntPtr key, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyDict_SetItemString(IntPtr pointer, string key, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyDict_DelItem(IntPtr pointer, IntPtr key); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyMapping_HasKey(IntPtr pointer, IntPtr key); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyDict_Keys(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyDict_Values(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyDict_Items(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyDict_Copy(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyDict_Update(IntPtr pointer, IntPtr other); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyDict_Clear(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyDict_Size(IntPtr pointer); + + + //==================================================================== + // Python list API + //==================================================================== + + public static bool PyList_Check(IntPtr ob) { + return PyObject_TYPE(ob) == Runtime.PyListType; + } + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyList_New(int size); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyList_AsTuple(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyList_GetItem(IntPtr pointer, int index); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyList_SetItem(IntPtr pointer, int index, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyList_Insert(IntPtr pointer, int index, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyList_Append(IntPtr pointer, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyList_Reverse(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyList_Sort(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyList_GetSlice(IntPtr pointer, int start, int end); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyList_SetSlice(IntPtr pointer, int start, int end, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyList_Size(IntPtr pointer); + + + //==================================================================== + // Python tuple API + //==================================================================== + + public static bool PyTuple_Check(IntPtr ob) { + return PyObject_TYPE(ob) == Runtime.PyTupleType; + } + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyTuple_New(int size); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyTuple_GetItem(IntPtr pointer, int index); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyTuple_SetItem(IntPtr pointer, int index, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyTuple_GetSlice(IntPtr pointer, int start, int end); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyTuple_Size(IntPtr pointer); + + + //==================================================================== + // Python iterator API + //==================================================================== + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + public unsafe static extern bool + PyIter_Check(IntPtr pointer); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + public unsafe static extern IntPtr + PyIter_Next(IntPtr pointer); + + //==================================================================== + // Python module API + //==================================================================== + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern string + PyModule_GetName(IntPtr module); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyModule_GetDict(IntPtr module); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern string + PyModule_GetFilename(IntPtr module); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyImport_Import(IntPtr name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyImport_ImportModule(string name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyImport_ReloadModule(IntPtr module); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyImport_AddModule(string name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyImport_GetModuleDict(); + + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PySys_SetArgv(int argc, IntPtr argv); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PySys_GetObject(string name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PySys_SetObject(string name, IntPtr ob); + + + //==================================================================== + // Python type object API + //==================================================================== + + public static bool PyType_Check(IntPtr ob) { + return PyObject_TypeCheck(ob, Runtime.PyTypeType); + } + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern bool + PyType_IsSubtype(IntPtr t1, IntPtr t2); + + public static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) { + IntPtr t = PyObject_TYPE(ob); + return (t == tp) || PyType_IsSubtype(t, tp); + } + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyType_GenericAlloc(IntPtr type, int n); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyType_Ready(IntPtr type); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + _PyType_Lookup(IntPtr type, IntPtr name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_GenericGetAttr(IntPtr obj, IntPtr name); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyObject_GenericSetAttr(IntPtr obj, IntPtr name, IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + _PyObject_GetDictPtr(IntPtr obj); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyObject_GC_New(IntPtr tp); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyObject_GC_Del(IntPtr tp); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyObject_GC_Track(IntPtr tp); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyObject_GC_UnTrack(IntPtr tp); + + + //==================================================================== + // Python memory API + //==================================================================== + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyMem_Malloc(int size); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyMem_Realloc(IntPtr ptr, int size); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyMem_Free(IntPtr ptr); + + + //==================================================================== + // Python exception API + //==================================================================== + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyErr_SetString(IntPtr ob, string message); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyErr_SetObject(IntPtr ob, IntPtr message); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyErr_SetFromErrno(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyErr_SetNone(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyErr_ExceptionMatches(IntPtr exception); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyErr_GivenExceptionMatches(IntPtr ob, IntPtr val); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyErr_NormalizeException(IntPtr ob, IntPtr val, IntPtr tb); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + PyErr_Occurred(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyErr_Fetch(ref IntPtr ob, ref IntPtr val, ref IntPtr tb); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyErr_Restore(IntPtr ob, IntPtr val, IntPtr tb); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyErr_Clear(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern void + PyErr_Print(); + + + //==================================================================== + // Miscellaneous + //==================================================================== + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyMethod_Self(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern IntPtr + PyMethod_Function(IntPtr ob); + + } + + +} diff --git a/Pythonnet.Runtime/typemanager.cs b/Pythonnet.Runtime/typemanager.cs new file mode 100644 index 0000000000..41b845737c --- /dev/null +++ b/Pythonnet.Runtime/typemanager.cs @@ -0,0 +1,452 @@ +// ========================================================================== +// 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.Runtime.InteropServices; +using System.Reflection.Emit; +using System.Collections.Generic; +using System.Collections; +using System.Reflection; +using System.Threading; + +namespace Python.Runtime { + + //======================================================================= + // The TypeManager class is responsible for building binary-compatible + // Python type objects that are implemented in managed code. + //======================================================================= + + internal class TypeManager { + + static BindingFlags tbFlags; + static Dictionary cache; + static int obSize; + + static TypeManager() { + tbFlags = BindingFlags.Public | BindingFlags.Static; + obSize = 5 * IntPtr.Size; + cache = new Dictionary(128); + } + + + //==================================================================== + // Given a managed Type derived from ExtensionType, get the handle to + // a Python type object that delegates its implementation to the Type + // object. These Python type instances are used to implement internal + // descriptor and utility types like ModuleObject, PropertyObject, etc. + //==================================================================== + + internal static IntPtr GetTypeHandle(Type type) { + // Note that these types are cached with a refcount of 1, so they + // effectively exist until the CPython runtime is finalized. + IntPtr handle = IntPtr.Zero; + cache.TryGetValue(type, out handle); + if (handle != IntPtr.Zero) { + return handle; + } + handle = CreateType(type); + cache[type] = handle; + return handle; + } + + + //==================================================================== + // Get the handle of a Python type that reflects the given CLR type. + // The given ManagedType instance is a managed object that implements + // the appropriate semantics in Python for the reflected managed type. + //==================================================================== + + internal static IntPtr GetTypeHandle(ManagedType obj, Type type) { + IntPtr handle = IntPtr.Zero; + cache.TryGetValue(type, out handle); + if (handle != IntPtr.Zero) { + return handle; + } + handle = CreateType(obj, type); + cache[type] = handle; + return handle; + } + + + //==================================================================== + // The following CreateType implementations do the necessary work to + // create Python types to represent managed extension types, reflected + // types, subclasses of reflected types and the managed metatype. The + // dance is slightly different for each kind of type due to different + // behavior needed and the desire to have the existing Python runtime + // do as much of the allocation and initialization work as possible. + //==================================================================== + + internal static IntPtr CreateType(Type impl) { + + IntPtr type = AllocateTypeObject(impl.Name); + + // Set tp_basicsize to the size of our managed instance objects. + Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)obSize); + + IntPtr offset = (IntPtr)ObjectOffset.ob_dict; + Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); + + InitializeSlots(type, impl); + + int flags = TypeFlags.Default | TypeFlags.Managed | + TypeFlags.HeapType | TypeFlags.HaveGC; + Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + + Runtime.PyType_Ready(type); + + IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); + IntPtr mod = Runtime.PyString_FromString("CLR"); + Runtime.PyDict_SetItemString(dict, "__module__", mod); + + InitMethods(type, impl); + + return type; + } + + + internal static IntPtr CreateType(ManagedType impl, Type clrType) { + // Cleanup the type name to get rid of funny nested type names. + string name = "CLR." + clrType.FullName; + int i = name.LastIndexOf('+'); + if (i > -1) { + name = name.Substring(i + 1); + } + i = name.LastIndexOf('.'); + if (i > -1) { + name = name.Substring(i + 1); + } + + IntPtr base_ = IntPtr.Zero; + // XXX Hack, use a different base class for System.Exception + // Python 2.5+ allows new style class exceptions but they *must* + // subclass BaseException (or better Exception). +#if (PYTHON25 || PYTHON26 || PYTHON27) + if (clrType == typeof(System.Exception)) { + base_ = Exceptions.Exception; + Runtime.Incref(base_); + } else +#endif + if (clrType.BaseType != null) { + ClassBase bc = ClassManager.GetClass(clrType.BaseType); + base_ = bc.pyHandle; + } + + IntPtr type = AllocateTypeObject(name); + + Marshal.WriteIntPtr(type,TypeOffset.ob_type,Runtime.PyCLRMetaType); + Runtime.Incref(Runtime.PyCLRMetaType); + + Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)obSize); + Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); + + IntPtr offset = (IntPtr)ObjectOffset.ob_dict; + Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); + + InitializeSlots(type, impl.GetType()); + + if (base_ != IntPtr.Zero) { + Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_); + Runtime.Incref(base_); + } + + int flags = TypeFlags.Default; + flags |= TypeFlags.Managed; + flags |= TypeFlags.HeapType; + flags |= TypeFlags.BaseType; + flags |= TypeFlags.HaveGC; + Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + + // Leverage followup initialization from the Python runtime. Note + // that the type of the new type must PyType_Type at the time we + // call this, else PyType_Ready will skip some slot initialization. + + Runtime.PyType_Ready(type); + + IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); + string mn = clrType.Namespace != null ? clrType.Namespace : ""; + IntPtr mod = Runtime.PyString_FromString(mn); + Runtime.PyDict_SetItemString(dict, "__module__", mod); + + // Hide the gchandle of the implementation in a magic type slot. + GCHandle gc = GCHandle.Alloc(impl); + Marshal.WriteIntPtr(type, TypeOffset.magic(), (IntPtr)gc); + + // Set the handle attributes on the implementing instance. + impl.tpHandle = Runtime.PyCLRMetaType; + impl.gcHandle = gc; + impl.pyHandle = type; + + //DebugUtil.DumpType(type); + + return type; + } + + internal static IntPtr CreateSubType(IntPtr args) { + + IntPtr py_name = Runtime.PyTuple_GetItem(args, 0); + IntPtr bases = Runtime.PyTuple_GetItem(args, 1); + IntPtr dict = Runtime.PyTuple_GetItem(args, 2); + IntPtr base_ = Runtime.PyTuple_GetItem(bases, 0); + + string name = Runtime.GetManagedString(py_name); + IntPtr type = AllocateTypeObject(name); + + Marshal.WriteIntPtr(type,TypeOffset.ob_type,Runtime.PyCLRMetaType); + Runtime.Incref(Runtime.PyCLRMetaType); + + Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)obSize); + Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); + + IntPtr offset = (IntPtr)ObjectOffset.ob_dict; + Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); + + IntPtr dc = Runtime.PyDict_Copy(dict); + Marshal.WriteIntPtr(type, TypeOffset.tp_dict, dc); + + Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_); + Runtime.Incref(base_); + + int flags = TypeFlags.Default; + flags |= TypeFlags.Managed; + flags |= TypeFlags.HeapType; + flags |= TypeFlags.BaseType; + flags |= TypeFlags.Subclass; + flags |= TypeFlags.HaveGC; + Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + + CopySlot(base_, type, TypeOffset.tp_traverse); + CopySlot(base_, type, TypeOffset.tp_clear); + CopySlot(base_, type, TypeOffset.tp_is_gc); + + Runtime.PyType_Ready(type); + + + IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); + IntPtr mod = Runtime.PyString_FromString("CLR"); + Runtime.PyDict_SetItemString(tp_dict, "__module__", mod); + + // for now, move up hidden handle... + IntPtr gc = Marshal.ReadIntPtr(base_, TypeOffset.magic()); + Marshal.WriteIntPtr(type, TypeOffset.magic(), gc); + + return type; + } + + + internal static IntPtr CreateMetaType(Type impl) { + + // The managed metatype is functionally little different than the + // standard Python metatype (PyType_Type). It overrides certain of + // the standard type slots, and has to subclass PyType_Type for + // certain functions in the C runtime to work correctly with it. + + IntPtr type = AllocateTypeObject("CLR Metatype"); + IntPtr py_type = Runtime.PyTypeType; + + Marshal.WriteIntPtr(type, TypeOffset.tp_base, py_type); + Runtime.Incref(py_type); + + // Copy gc and other type slots from the base Python metatype. + + CopySlot(py_type, type, TypeOffset.tp_basicsize); + CopySlot(py_type, type, TypeOffset.tp_itemsize); + + CopySlot(py_type, type, TypeOffset.tp_dictoffset); + CopySlot(py_type, type, TypeOffset.tp_weaklistoffset); + + CopySlot(py_type, type, TypeOffset.tp_traverse); + CopySlot(py_type, type, TypeOffset.tp_clear); + CopySlot(py_type, type, TypeOffset.tp_is_gc); + + // Override type slots with those of the managed implementation. + + InitializeSlots(type, impl); + + int flags = TypeFlags.Default; + flags |= TypeFlags.Managed; + flags |= TypeFlags.HeapType; + flags |= TypeFlags.HaveGC; + Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + + Runtime.PyType_Ready(type); + + IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); + IntPtr mod = Runtime.PyString_FromString("CLR"); + Runtime.PyDict_SetItemString(dict, "__module__", mod); + + //DebugUtil.DumpType(type); + + return type; + } + + + internal static IntPtr BasicSubType(string name, IntPtr base_, + Type impl) { + + // Utility to create a subtype of a std Python type, but with + // a managed type able to override implementation + + IntPtr type = AllocateTypeObject(name); + //Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)obSize); + //Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); + + //IntPtr offset = (IntPtr)ObjectOffset.ob_dict; + //Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); + + //IntPtr dc = Runtime.PyDict_Copy(dict); + //Marshal.WriteIntPtr(type, TypeOffset.tp_dict, dc); + + Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_); + Runtime.Incref(base_); + + int flags = TypeFlags.Default; + flags |= TypeFlags.Managed; + flags |= TypeFlags.HeapType; + flags |= TypeFlags.HaveGC; + Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + + CopySlot(base_, type, TypeOffset.tp_traverse); + CopySlot(base_, type, TypeOffset.tp_clear); + CopySlot(base_, type, TypeOffset.tp_is_gc); + + InitializeSlots(type, impl); + + Runtime.PyType_Ready(type); + + IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); + IntPtr mod = Runtime.PyString_FromString("CLR"); + Runtime.PyDict_SetItemString(tp_dict, "__module__", mod); + + return type; + } + + + //==================================================================== + // Utility method to allocate a type object & do basic initialization. + //==================================================================== + + internal static IntPtr AllocateTypeObject(string name) { + IntPtr type = Runtime.PyType_GenericAlloc(Runtime.PyTypeType, 0); + + // Cheat a little: we'll set tp_name to the internal char * of + // the Python version of the type name - otherwise we'd have to + // allocate the tp_name and would have no way to free it. + + IntPtr temp = Runtime.PyString_FromString(name); + IntPtr raw = Runtime.PyString_AS_STRING(temp); + Marshal.WriteIntPtr(type, TypeOffset.tp_name, raw); + Marshal.WriteIntPtr(type, TypeOffset.name, temp); + + long ptr = type.ToInt64(); // 64-bit safe + + temp = new IntPtr(ptr + TypeOffset.nb_add); + Marshal.WriteIntPtr(type, TypeOffset.tp_as_number, temp); + + temp = new IntPtr(ptr + TypeOffset.sq_length); + Marshal.WriteIntPtr(type, TypeOffset.tp_as_sequence, temp); + + temp = new IntPtr(ptr + TypeOffset.mp_length); + Marshal.WriteIntPtr(type, TypeOffset.tp_as_mapping, temp); + + temp = new IntPtr(ptr + TypeOffset.bf_getreadbuffer); + Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, temp); + + return type; + } + + + //==================================================================== + // Given a newly allocated Python type object and a managed Type that + // provides the implementation for the type, connect the type slots of + // the Python object to the managed methods of the implementing Type. + //==================================================================== + + internal static void InitializeSlots(IntPtr type, Type impl) { + Hashtable seen = new Hashtable(8); + Type offsetType = typeof(TypeOffset); + + while (impl != null) { + MethodInfo[] methods = impl.GetMethods(tbFlags); + for (int i = 0; i < methods.Length; i++) { + MethodInfo method = methods[i]; + string name = method.Name; + if (! (name.StartsWith("tp_") || + name.StartsWith("nb_") || + name.StartsWith("sq_") || + name.StartsWith("mp_") || + name.StartsWith("bf_") + ) ) { + continue; + } + + if (seen[name] != null) { + continue; + } + + FieldInfo fi = offsetType.GetField(name); + int offset = (int)fi.GetValue(offsetType); + + IntPtr slot = Interop.GetThunk(method); + Marshal.WriteIntPtr(type, offset, slot); + + seen[name] = 1; + } + + impl = impl.BaseType; + } + + } + + + //==================================================================== + // Given a newly allocated Python type object and a managed Type that + // implements it, initialize any methods defined by the Type that need + // to appear in the Python type __dict__ (based on custom attribute). + //==================================================================== + + private static void InitMethods(IntPtr pytype, Type type) { + IntPtr dict = Marshal.ReadIntPtr(pytype, TypeOffset.tp_dict); + Type marker = typeof(PythonMethodAttribute); + + BindingFlags flags = BindingFlags.Public | BindingFlags.Static; + + while (type != null) { + MethodInfo[] methods = type.GetMethods(flags); + for (int i = 0; i < methods.Length; i++) { + MethodInfo method = methods[i]; + object[] attrs = method.GetCustomAttributes(marker, false); + if (attrs.Length > 0) { + string method_name = method.Name; + MethodInfo[] mi = new MethodInfo[1]; + mi[0] = method; + MethodObject m = new TypeMethod(method_name, mi); + Runtime.PyDict_SetItemString(dict, method_name, + m.pyHandle); + } + } + type = type.BaseType; + } + } + + + //==================================================================== + // Utility method to copy slots from a given type to another type. + //==================================================================== + + internal static void CopySlot(IntPtr from, IntPtr to, int offset) { + IntPtr fp = Marshal.ReadIntPtr(from, offset); + Marshal.WriteIntPtr(to, offset, fp); + } + + + } + + +} diff --git a/Pythonnet.Runtime/typemethod.cs b/Pythonnet.Runtime/typemethod.cs new file mode 100644 index 0000000000..ab95f28ed0 --- /dev/null +++ b/Pythonnet.Runtime/typemethod.cs @@ -0,0 +1,54 @@ +// ========================================================================== +// 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.Collections; +using System.Reflection; + +namespace Python.Runtime { + + //======================================================================== + // Implements a Python type that provides access to CLR object methods. + //======================================================================== + + internal class TypeMethod : MethodObject { + + public TypeMethod(string name, MethodInfo[] info) : + base(name, info) {} + + public TypeMethod(string name, MethodInfo[] info, bool allow_threads) : + base(name, info, allow_threads) { } + + public override IntPtr Invoke(IntPtr ob, IntPtr args, IntPtr kw) { + MethodInfo mi = this.info[0]; + Object[] arglist = new Object[3]; + arglist[0] = ob; + arglist[1] = args; + arglist[2] = kw; + + try { + Object inst = null; + if (ob != IntPtr.Zero) { + inst = GetManagedObject(ob); + } + return (IntPtr)mi.Invoke(inst, BindingFlags.Default, null, arglist, + null); + } + catch (Exception e) { + Exceptions.SetError(e); + return IntPtr.Zero; + } + } + + + + } + + +} diff --git a/Pythonnet.Runtime/x86/clrmodule-platform.il b/Pythonnet.Runtime/x86/clrmodule-platform.il new file mode 100644 index 0000000000..aeac29658c --- /dev/null +++ b/Pythonnet.Runtime/x86/clrmodule-platform.il @@ -0,0 +1,11 @@ +// ========================================================================== +// 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. +// ========================================================================== + +.vtfixup [1] int32 fromunmanaged at VT_01 +.data VT_01 = int32(0) diff --git a/WinAlfred.Test/PyImportTest.cs b/WinAlfred.Test/PyImportTest.cs new file mode 100644 index 0000000000..25ffa34b01 --- /dev/null +++ b/WinAlfred.Test/PyImportTest.cs @@ -0,0 +1,63 @@ +using System; +using System.Reflection; +using NUnit.Framework; +using Python.Runtime; + +namespace WinAlfred.Test +{ + [TestFixture] + public class PyImportTest + { + private IntPtr gs; + + [SetUp] + public void SetUp() + { + PythonEngine.Initialize(); + gs = PythonEngine.AcquireLock(); + + //string here = Environment.CurrentDirectory; + // trunk\pythonnet\src\embed_tests\bin\Debug + + /* + * Append the tests directory to sys.path + * using reflection to circumvent the private modifires placed on most Runtime methods. + */ + string s = @"d:\github\pythonnet\pythonnet\src\tests"; + + Type RTClass = typeof(Runtime); + + /* pyStrPtr = PyString_FromString(s); */ + MethodInfo PyString_FromString = RTClass.GetMethod("PyString_FromString", BindingFlags.Public | BindingFlags.Static); + object[] funcArgs = new object[1]; + funcArgs[0] = s; + IntPtr pyStrPtr = (IntPtr)PyString_FromString.Invoke(null, funcArgs); + + /* SysDotPath = sys.path */ + MethodInfo PySys_GetObject = RTClass.GetMethod("PySys_GetObject", BindingFlags.Public | BindingFlags.Static); + funcArgs[0] = "path"; + IntPtr SysDotPath = (IntPtr)PySys_GetObject.Invoke(null, funcArgs); + + /* SysDotPath.append(*pyStrPtr) */ + MethodInfo PyList_Append = RTClass.GetMethod("PyList_Append", BindingFlags.Public | BindingFlags.Static); + funcArgs = new object[2]; + funcArgs[0] = SysDotPath; + funcArgs[1] = pyStrPtr; + int r = (int)PyList_Append.Invoke(null, funcArgs); + } + + [TearDown] + public void TearDown() + { + PythonEngine.ReleaseLock(gs); + PythonEngine.Shutdown(); + } + + [Test] + public void TestDottedName() + { + PyObject module = PythonEngine.ImportModule("PyImportTest.test.one"); + Assert.IsNotNull(module, ">>> import PyImportTest.test.one # FAILED"); + } + } +} diff --git a/WinAlfred.Test/WinAlfred.Test.csproj b/WinAlfred.Test/WinAlfred.Test.csproj index ab2e15c965..e18a3a8faa 100644 --- a/WinAlfred.Test/WinAlfred.Test.csproj +++ b/WinAlfred.Test/WinAlfred.Test.csproj @@ -43,6 +43,7 @@ + @@ -50,6 +51,10 @@ + + {097B4AC0-74E9-4C58-BCF8-C69746EC8271} + Python.Runtime + {8451ECDD-2EA4-4966-BB0A-7BBC40138E80} WinAlfred.Plugin diff --git a/WinAlfred/Commands/PluginCommand.cs b/WinAlfred/Commands/PluginCommand.cs index f30b667ac3..81f5e739d1 100644 --- a/WinAlfred/Commands/PluginCommand.cs +++ b/WinAlfred/Commands/PluginCommand.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; +using Python.Runtime; using WinAlfred.Helper; using WinAlfred.Plugin; using WinAlfred.PluginLoader; @@ -9,6 +11,9 @@ namespace WinAlfred.Commands { public class PluginCommand : BaseCommand { + private string currentPythonModulePath = string.Empty; + private IntPtr GIL; + public PluginCommand(MainWindow mainWindow) : base(mainWindow) { @@ -17,36 +22,56 @@ namespace WinAlfred.Commands public override void Dispatch(Query q) { - - foreach (PluginPair pair in Plugins.AllPlugins) + PluginPair thirdPlugin = Plugins.AllPlugins.FirstOrDefault(o => o.Metadata.ActionKeyword == q.ActionName); + if (thirdPlugin != null && !string.IsNullOrEmpty(thirdPlugin.Metadata.ActionKeyword)) { - if (pair.Metadata.ActionKeyword == q.ActionName) + if (thirdPlugin.Metadata.Language == AllowedLanguage.Python) { - PluginPair pair1 = pair; - ThreadPool.QueueUserWorkItem(state => - { - try - { - List r = pair1.Plugin.Query(q); - r.ForEach(o => - { - o.PluginDirectory = pair1.Metadata.PluginDirecotry; - o.OriginQuery = q; - }); - UpdateResultView(r); - } - catch (Exception queryException) - { - Log.Error(string.Format("Plugin {0} query failed: {1}", pair1.Metadata.Name, - queryException.Message)); -#if (DEBUG) - { - throw; - } -#endif - } - }); + SwitchPythonEnv(thirdPlugin); } + ThreadPool.QueueUserWorkItem(t => + { + try + { + List r = thirdPlugin.Plugin.Query(q); + r.ForEach(o => + { + o.PluginDirectory = thirdPlugin.Metadata.PluginDirecotry; + o.OriginQuery = q; + }); + UpdateResultView(r); + } + catch (Exception queryException) + { + Log.Error(string.Format("Plugin {0} query failed: {1}", thirdPlugin.Metadata.Name, + queryException.Message)); +#if (DEBUG) + { + throw; + } +#endif + } + }); + } + } + + private void SwitchPythonEnv(PluginPair thirdPlugin) + { + if (currentPythonModulePath != thirdPlugin.Metadata.PluginDirecotry) + { + //this must initial in main thread + currentPythonModulePath = thirdPlugin.Metadata.PluginDirecotry; + + if (GIL != IntPtr.Zero) + { + Runtime.PyEval_RestoreThread(GIL); + PythonEngine.Shutdown(); + } + PythonEngine.Initialize(); + IntPtr pyStrPtr = Runtime.PyString_FromString(thirdPlugin.Metadata.PluginDirecotry); + IntPtr SysDotPath = Runtime.PySys_GetObject("path"); + Runtime.PyList_Append(SysDotPath, pyStrPtr); + GIL = PythonEngine.BeginAllowThreads(); } } } diff --git a/WinAlfred/PluginLoader/BasePluginLoader.cs b/WinAlfred/PluginLoader/BasePluginLoader.cs index 6a48e7aa0e..3acf56d129 100644 --- a/WinAlfred/PluginLoader/BasePluginLoader.cs +++ b/WinAlfred/PluginLoader/BasePluginLoader.cs @@ -117,5 +117,16 @@ namespace WinAlfred.PluginLoader return null; } } + + ///// + ///// Change python execute file name to unique file name using GUID + ///// this is because if two pythong plugin use the same + ///// + ///// + ///// + //private static PluginMetadata filterPythonMetadata(PluginMetadata metadata) + //{ + + //} } } diff --git a/WinAlfred/PluginLoader/Plugins.cs b/WinAlfred/PluginLoader/Plugins.cs index 06bbceb65b..9e53b3507a 100644 --- a/WinAlfred/PluginLoader/Plugins.cs +++ b/WinAlfred/PluginLoader/Plugins.cs @@ -19,15 +19,18 @@ namespace WinAlfred.PluginLoader foreach (IPlugin plugin in plugins.Select(pluginPair => pluginPair.Plugin)) { IPlugin plugin1 = plugin; - ThreadPool.QueueUserWorkItem(o => plugin1.Init(new PluginInitContext() + ThreadPool.QueueUserWorkItem(o => { - Plugins = plugins, - ChangeQuery = s => window.ChangeQuery(s), - CloseApp = window.CloseApp, - HideApp = window.HideApp, - ShowApp = window.ShowApp, - ShowMsg = (title, subTitle, iconPath) => window.ShowMsg(title, subTitle, iconPath) - })); + plugin1.Init(new PluginInitContext() + { + Plugins = plugins, + ChangeQuery = s => window.ChangeQuery(s), + CloseApp = window.CloseApp, + HideApp = window.HideApp, + ShowApp = window.ShowApp, + ShowMsg = (title, subTitle, iconPath) => window.ShowMsg(title, subTitle, iconPath) + }); + }); } } diff --git a/WinAlfred/PluginLoader/PythonPluginLoader.cs b/WinAlfred/PluginLoader/PythonPluginLoader.cs index 955ec55eff..a31fea21fd 100644 --- a/WinAlfred/PluginLoader/PythonPluginLoader.cs +++ b/WinAlfred/PluginLoader/PythonPluginLoader.cs @@ -1,12 +1,20 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading; +using Python.Runtime; using WinAlfred.Plugin; namespace WinAlfred.PluginLoader { public class PythonPluginLoader : BasePluginLoader { + + static PythonPluginLoader() + { + + } + public override List LoadPlugin() { List plugins = new List(); diff --git a/WinAlfred/PluginLoader/PythonPluginWrapper.cs b/WinAlfred/PluginLoader/PythonPluginWrapper.cs index 2dad738166..1ecba68c8c 100644 --- a/WinAlfred/PluginLoader/PythonPluginWrapper.cs +++ b/WinAlfred/PluginLoader/PythonPluginWrapper.cs @@ -1,19 +1,17 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Windows.Documents; using Newtonsoft.Json; +using Python.Runtime; using WinAlfred.Plugin; namespace WinAlfred.PluginLoader { public class PythonPluginWrapper : IPlugin { - private PluginMetadata metadata; - [DllImport("PyWinAlfred.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - private extern static IntPtr ExecPython(string directory, string file, string method, string para); - [DllImport("PyWinAlfred.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - private extern static void InitPythonEnv(); + private PluginMetadata metadata; public PythonPluginWrapper(PluginMetadata metadata) { @@ -24,24 +22,20 @@ namespace WinAlfred.PluginLoader { try { - string s = Marshal.PtrToStringAnsi(ExecPython(metadata.PluginDirecotry, metadata.ExecuteFileName.Replace(".py", ""), "query", query.RawQuery)); - if (string.IsNullOrEmpty(s)) + string jsonResult = InvokeFunc(metadata.PluginDirecotry, metadata.ExecuteFileName.Replace(".py", ""), "query", query.RawQuery); + if (string.IsNullOrEmpty(jsonResult)) { return new List(); } - if (s.StartsWith("PYTHONERROR")) - { - throw new ArgumentException(s); - } - List o = JsonConvert.DeserializeObject>(s); + List o = JsonConvert.DeserializeObject>(jsonResult); List r = new List(); foreach (PythonResult pythonResult in o) { PythonResult ps = pythonResult; if (!string.IsNullOrEmpty(ps.ActionName)) { - ps.Action = () => ExecPython(metadata.PluginDirecotry, metadata.ExecuteFileName.Replace(".py", ""), ps.ActionName, ps.ActionPara); + ps.Action = () => InvokeFunc(metadata.PluginDirecotry, metadata.ExecuteFileName.Replace(".py", ""), ps.ActionName, ps.ActionPara); } r.Add(ps); } @@ -49,15 +43,31 @@ namespace WinAlfred.PluginLoader } catch (Exception) { - - throw; +#if (DEBUG) + { + throw; + } +#endif } } + private string InvokeFunc(string path, string moduleName, string func, string para) + { + IntPtr gs = PythonEngine.AcquireLock(); + + PyObject module = PythonEngine.ImportModule(moduleName); + PyObject res = module.InvokeMethod(func, new PyString(para)); + string json = Runtime.GetManagedString(res.Handle); + + PythonEngine.ReleaseLock(gs); + + return json; + } + public void Init(PluginInitContext context) { - InitPythonEnv(); + } } } diff --git a/clr.pyd b/clr.pyd new file mode 100644 index 0000000000..3d2b72e3e6 Binary files /dev/null and b/clr.pyd differ