diff --git a/.travis.yml b/.travis.yml index 6355ce73d..551befdf9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,24 @@ +sudo: required language: python python: - 2.6 - 2.7 + - 3.2 + - 3.4 + - 3.5 before_install: - - sudo apt-get install software-properties-common - sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu/ trusty main universe" - - sudo apt-get -qq update - - sudo apt-get -qq install mono-devel mono-gmcs mono-xbuild nunit-console - - sudo mozroots --import --machine --sync - - yes | sudo certmgr -ssl -m https://go.microsoft.com - - yes | sudo certmgr -ssl -m https://nugetgallery.blob.core.windows.net - - yes | sudo certmgr -ssl -m https://nuget.org + - sudo apt-get install software-properties-common + - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF + - echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list + - echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list + - sudo apt-get update + - sudo DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" install mono-devel mono-complete referenceassemblies-pcl ca-certificates-mono nunit-console + install: + - pip install six + - pip install pycparser - python setup.py build_ext --inplace script: - - export PYTHONPATH=`pwd` - - ./npython src/tests/runtests.py + - export PYTHONPATH=`pwd`:$PYTHONPATH + - python src/tests/runtests.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..31ba95121 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,40 @@ +# How to contribute + +PythonNet is developed and maintained by unpaid community members so well +written, documented and tested pull requests are encouraged. + +## Getting Started + +* Make sure you have a [GitHub account](https://github.com/signup/free) +* Submit a ticket for your issue, assuming one does not already exist. + * Clearly describe the issue including steps to reproduce when it is a bug. + * Make sure you include what Python version and operating system you are using. +* Fork the repository on GitHub + +## Making Changes + +* Create a topic branch from where you want to base your work. + * This is usually the develop branch. + * Only target release branches if you are certain your fix must be on that + branch. + * To quickly create a topic branch based on develop; `git checkout -b + fix/develop/my_contribution develop`. Please avoid working directly on the + `master` branch. +* Make commits of logical units. +* Check for unnecessary whitespace with `git diff --check` before committing. +* Make sure your commit messages are in the proper format. +* Make sure you have added the necessary tests for your changes. +* Run _all_ the tests to assure nothing else was accidentally broken. + + +## Submitting Changes + +* Push your changes to a topic branch in your fork of the repository. +* Submit a pull request to the repository in the pythonnet organization. +* After feedback has been given we expect responses within two weeks. After two + weeks we may close the pull request if it isn't showing any activity. + +# Additional Resources + +* [General GitHub documentation](https://help.github.com/) +* [GitHub pull request documentation](https://help.github.com/send-pull-requests/) diff --git a/Python.Runtime.dll.config b/Python.Runtime.dll.config index 11b4fb0fe..e9821a8a9 100644 --- a/Python.Runtime.dll.config +++ b/Python.Runtime.dll.config @@ -14,10 +14,16 @@ For more information read: + + + + + + diff --git a/Python.Test.csproj b/Python.Test.csproj new file mode 100644 index 000000000..11591e091 --- /dev/null +++ b/Python.Test.csproj @@ -0,0 +1,190 @@ + + + + Debug + AnyCPU + {6F401A34-273B-450F-9A4C-13550BE0767B} + Library + false + Python.Test + Python.Test + OnBuildSuccess + + + + + 3.5 + v4.0 + + + + true + full + false + .\bin\Debug\ + DEBUG;TRACE + + + pdbonly + true + .\bin\Release\ + TRACE + + + true + bin\EmbeddingTest\ + DEBUG;TRACE + full + AnyCPU + + + true + bin\UnitTests\ + DEBUG;TRACE + full + AnyCPU + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + true + false + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + false + false + false + + + true + bin\x86\EmbeddingTest\ + DEBUG;TRACE + full + x86 + false + true + true + + + true + bin\x86\UnitTests\ + DEBUG;TRACE + full + x86 + true + true + + + true + bin\DebugMono_x86\ + DEBUG;TRACE + full + AnyCPU + true + true + + + true + bin\x86\DebugMono_x86\ + DEBUG;TRACE + full + x86 + true + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + true + false + false + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + false + + + true + bin\x64\EmbeddingTest\ + DEBUG;TRACE + full + x64 + false + true + true + + + true + bin\x64\UnitTests\ + DEBUG;TRACE + full + x64 + true + true + + + true + bin\x64\DebugMono_x86\ + DEBUG;TRACE + full + x64 + true + false + false + + + + + + + + + + + + + + + + + + + + + + + + 3.5 + + + + + + {097B4AC0-74E9-4C58-BCF8-C69746EC8271} + Python.Runtime + + + + + + + + copy "$(TargetPath)" "$(SolutionDir)" +copy "$(TargetDir)*.pdb" "$(SolutionDir)" + + + \ No newline at end of file diff --git a/README.md b/README.md index 8b88ac0e9..f6196e5d9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,63 @@ pythonnet ========= -Python for .NET is a package that gives Python programmers nearly seamless integration with the .NET Common Language Runtime (CLR) and provides a powerful application scripting tool for .NET developers. +Python for .NET is a package that gives Python programmers nearly seamless integration with the .NET Common Language Runtime (CLR) and provides a powerful application scripting tool for .NET developers. It allows Python code to interact with the CLR, and may also be used to embed Python into a .NET application. -[![Build Status](https://travis-ci.org/pythonnet/pythonnet.png?branch=develop)](https://travis-ci.org/pythonnet/pythonnet) +[![Build Status](https://travis-ci.org/pythonnet/pythonnet.png?branch=master)](https://travis-ci.org/pythonnet/pythonnet) -[![Build status](https://ci.appveyor.com/api/projects/status/65riiu1hvgaxsbwb)](https://ci.appveyor.com/project/davidanthoff/pythonnet) +[![Build status](https://ci.appveyor.com/api/projects/status/c8k0miljb3n1c7be/branch/master)](https://ci.appveyor.com/project/TonyRoberts/pythonnet-480xs) + +**Calling .NET code from Python** + +Python for .NET allows CLR namespaces to be treated essentially as Python packages. + +```python + import clr + from System import String + from System.Collections import * +``` +To load an assembly, use the "AddReference" function in the "clr" module: + +```python + import clr + clr.AddReference("System.Windows.Forms") + from System.Windows.Forms import Form +``` + +**Embedding Python in .NET** + ++ All calls to python should be inside a "using (Py.GIL()) {/* Your code here */}" block. ++ Import python modules using dynamic mod = Py.Import("mod"), then you can call functions as normal, eg mod.func(args). ++ Use mod.func(args, Py.kw("keywordargname", keywordargvalue)) to apply keyword arguments. ++ All python objects should be declared as 'dynamic' type. ++ Mathematical operations involving python and literal/managed types must have the python object first, eg np.pi*2 works, 2*np.pi doesn't + +EG: +```csharp +static void Main(string[] args) +{ + using (Py.GIL()) { + dynamic np = Py.Import("numpy"); + dynamic sin = np.sin; + Console.WriteLine(np.cos(np.pi*2)); + Console.WriteLine(sin(5)); + double c = np.cos(5) + sin(5); + Console.WriteLine(c); + dynamic a = np.array(new List { 1, 2, 3 }); + dynamic b = np.array(new List { 6, 5, 4 }, Py.kw("dtype", np.int32)); + Console.WriteLine(a.dtype); + Console.WriteLine(b.dtype); + Console.WriteLine(a * b); + Console.ReadKey(); + } +} +``` +outputs: +``` +1.0 +-0.958924274663 +-0.6752620892 +float64 +int32 +[ 6. 10. 12.] +``` diff --git a/appveyor.yml b/appveyor.yml index 18f9761c0..02807b816 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,29 +3,33 @@ os: Windows Server 2012 environment: global: PYTHONPATH: c:\testdir + PYTHONWARNINGS: 'ignore:::pip.pep425tags:' matrix: - - pythonurl: http://www.python.org/ftp/python/2.7.6/python-2.7.6.amd64.msi - - pythonurl: http://www.python.org/ftp/python/2.7.6/python-2.7.6.msi - - pythonurl: http://www.python.org/ftp/python/2.6.6/python-2.6.6.msi - - pythonurl: http://www.python.org/ftp/python/2.6.6/python-2.6.6.amd64.msi + # http://www.appveyor.com/docs/installed-software#python + - PYTHON: "C:\\Python27" + - PYTHON: "C:\\Python27-x64" + - PYTHON: "C:\\Python33" + - PYTHON: "C:\\Python33-x64" + - PYTHON: "C:\\Python34" + - PYTHON: "C:\\Python34-x64" + - PYTHON: "C:\\Python35" + - PYTHON: "C:\\Python35-x64" install: - - ps: (new-object net.webclient).DownloadFile($env:pythonurl, 'C:\python.msi') - - ps: start-process -wait -FilePath msiexec.exe -ArgumentList "/qn /i C:\python.msi TARGETDIR=C:\Python" - - ps: (new-object net.webclient).DownloadFile('https://raw.github.com/pypa/pip/master/contrib/get-pip.py', 'C:\get-pip.py') - # appveyor has python 2.7.6 x86 preinstalled, but in the wrong directory, this works around this - - ps: if ($env:pythonurl -eq "http://www.python.org/ftp/python/2.7.6/python-2.7.6.msi") {mi c:\python27 c:\python} - - set PATH=C:\Python;%PATH% - - C:\Python\python.exe c:\get-pip.py - - C:\Python\Scripts\pip.exe install wheel +- "%PYTHON%\\python.exe -m pip install --upgrade pip" +- "%PYTHON%\\python.exe -m pip install wheel" +- "%PYTHON%\\python.exe -m pip install six" build_script: - - C:\python\python.exe setup.py bdist_wheel +- "%PYTHON%\\python.exe setup.py bdist_wheel" test_script: - - ps: C:\python\scripts\pip.exe install ("dist\" + (gci dist)[0].Name) + - ps: '& "$env:PYTHON\\Scripts\\pip.exe" install --no-cache-dir --force-reinstall --ignore-installed ("dist\\" + (gci dist)[0].Name)' - mkdir c:\testdir - ps: copy-item (gci -path build -re -include Python.Test.dll)[0].FullName c:\testdir - - c:\python\python.exe src\tests\runtests.py - - c:\python\scripts\npython.exe src\tests\runtests.py + - "%PYTHON%\\python.exe src\\tests\\runtests.py" + +artifacts: + # bdist_wheel puts your built wheel in the dist directory + - path: dist\* diff --git a/demo/helloform.py b/demo/helloform.py index 5d7a026f7..bdc5c54ed 100644 --- a/demo/helloform.py +++ b/demo/helloform.py @@ -9,7 +9,7 @@ import clr SWF = clr.AddReference("System.Windows.Forms") -print SWF.Location +print (SWF.Location) import System.Windows.Forms as WinForms from System.Drawing import Size, Point @@ -49,7 +49,7 @@ def __init__(self): def button_Click(self, sender, args): """Button click event handler""" - print "Click" + print ("Click") WinForms.MessageBox.Show("Please do not press this button again.") def run(self): @@ -58,9 +58,9 @@ def run(self): def main(): form = HelloApp() - print "form created" + print ("form created") app = WinForms.Application - print "app referenced" + print ("app referenced") app.Run(form) diff --git a/demo/splitter.py b/demo/splitter.py index f40b67137..5c5da67a8 100644 --- a/demo/splitter.py +++ b/demo/splitter.py @@ -7,6 +7,7 @@ # FOR A PARTICULAR PURPOSE. # =========================================================================== +import clr import System.Windows.Forms as WinForms from System.Drawing import Color, Size, Point import System diff --git a/demo/wordpad.py b/demo/wordpad.py index 36286b811..300c4a1dd 100644 --- a/demo/wordpad.py +++ b/demo/wordpad.py @@ -7,7 +7,9 @@ # FOR A PARTICULAR PURPOSE. # =========================================================================== +import clr import System.Windows.Forms as WinForms +from System.Threading import Thread, ThreadStart, ApartmentState from System.Drawing import Color, Size, Point from System.Text import Encoding from System.IO import File @@ -204,7 +206,7 @@ def InitializeComponent(self): self.richTextBox.TabIndex = 0 self.richTextBox.AutoSize = 1 self.richTextBox.ScrollBars = WinForms.RichTextBoxScrollBars.ForcedBoth - self.richTextBox.Font = System.Drawing.Font("Tahoma", 10) + self.richTextBox.Font = System.Drawing.Font("Tahoma", 10.0) self.richTextBox.AcceptsTab = 1 self.richTextBox.Location = System.Drawing.Point(0, 0) @@ -314,11 +316,10 @@ def OpenDocument(self): buff = System.Array.CreateInstance(System.Byte, 1024) data = [] - read = -1 - while (read != 0): + while stream.Position < stream.Length: buff.Initialize() - read = stream.Read(buff, 0, 1024) + stream.Read(buff, 0, 1024) temp = Encoding.ASCII.GetString(buff, 0, 1024) data.append(temp) @@ -413,18 +414,25 @@ def InitializeComponent(self): self.ShowInTaskbar = False self.StartPosition = WinForms.FormStartPosition.CenterScreen self.Text = "About" - self.ResumeLayout(0) + self.ResumeLayout(False) def OnClickClose(self, sender, args): self.Close() - -def main(): +def app_thread(): app = Wordpad() WinForms.Application.Run(app) app.Dispose() + +def main(): + thread = Thread(ThreadStart(app_thread)) + thread.SetApartmentState(ApartmentState.STA) + thread.Start() + thread.Join() + + if __name__ == '__main__': main() diff --git a/setup.py b/setup.py index 7926c663d..f0c50838f 100644 --- a/setup.py +++ b/setup.py @@ -4,88 +4,106 @@ """ from setuptools import setup, Extension from distutils.command.build_ext import build_ext -from distutils.command.build_scripts import build_scripts from distutils.command.install_lib import install_lib from distutils.command.install_data import install_data from distutils.sysconfig import get_config_var +from distutils import log from platform import architecture from subprocess import Popen, CalledProcessError, PIPE, check_call from glob import glob import fnmatch -import shutil import sys import os -CONFIG = "Release" # Release or Debug +CONFIG = "Release" # Release or Debug DEVTOOLS = "MsDev" if sys.platform == "win32" else "Mono" -VERBOSITY = "minimal" # quiet, minimal, normal, detailed, diagnostic +VERBOSITY = "minimal" # quiet, minimal, normal, detailed, diagnostic PLATFORM = "x64" if architecture()[0] == "64bit" else "x86" def _find_msbuild_tool(tool="msbuild.exe", use_windows_sdk=False): """Return full path to one of the microsoft build tools""" - import _winreg + try: + import _winreg + except ImportError: + import winreg as _winreg + keys_to_check = [] if use_windows_sdk: - value_name = "InstallationFolder" - sdk_name = "Windows SDK" - keys_to_check = [ - r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A\WinSDK-Win32Tools", - r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1\WinSDKWin32Tools", - r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A\WinSDK-Win32Tools", - r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0\WinSDKWin32Tools", - r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0A\WinSDKWin32Tools", - ] + sdks_root = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows" + kits_root = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots" + kits_suffix = "bin" + if PLATFORM == "x64": + kits_suffix += r"\x64" + keys_to_check.extend([ + ("Windows Kit 10.0", kits_root, "KitsRoot10", kits_suffix), + ("Windows Kit 8.1", kits_root, "KitsRoot81", kits_suffix), + ("Windows Kit 8.0", kits_root, "KitsRoot", kits_suffix), + ("Windows SDK 7.1A", sdks_root + r"\v7.1A\WinSDK-Win32Tools", "InstallationFolder"), + ("Windows SDK 7.1", sdks_root + r"\v7.1\WinSDKWin32Tools", "InstallationFolder"), + ("Windows SDK 7.0A", sdks_root + r"\v7.0A\WinSDK-Win32Tools", "InstallationFolder"), + ("Windows SDK 7.0", sdks_root + r"\v7.0\WinSDKWin32Tools", "InstallationFolder"), + ("Windows SDK 6.0A", sdks_root + r"\v6.0A\WinSDKWin32Tools", "InstallationFolder") + ]) else: - value_name = "MSBuildToolsPath" - sdk_name = "MSBuild" - keys_to_check = [ - r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\12.0", - r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0", - r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\3.5", - r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\2.0" - ] - + vs_root = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions" + keys_to_check.extend([ + ("MSBuild 14", vs_root + r"\14.0", "MSBuildToolsPath"), + ("MSBuild 12", vs_root + r"\12.0", "MSBuildToolsPath"), + ("MSBuild 4", vs_root + r"\4.0", "MSBuildToolsPath"), + ("MSBuild 3.5", vs_root + r"\3.5", "MSBuildToolsPath"), + ("MSBuild 2.0", vs_root + r"\2.0", "MSBuildToolsPath") + ]) + + # read the possible tools paths from the various registry locations + paths_to_check = [] hreg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) try: hkey = None - for key in keys_to_check: + for key_to_check in keys_to_check: + sdk_name, key, value_name = key_to_check[:3] + suffix = key_to_check[3] if len(key_to_check) > 3 else None try: hkey = _winreg.OpenKey(hreg, key) - break + val, type_ = _winreg.QueryValueEx(hkey, value_name) + if type_ != _winreg.REG_SZ: + continue + if suffix: + val = os.path.join(val, suffix) + paths_to_check.append((sdk_name, val)) except WindowsError: pass - - if hkey is None: - raise RuntimeError("%s could not be found" % sdk_name) - - try: - val, type_ = _winreg.QueryValueEx(hkey, value_name) - if type_ != _winreg.REG_SZ: - raise RuntimeError("%s could not be found" % sdk_name) - - path = os.path.join(val, tool) - if os.path.exists(path): - return path - finally: - hkey.Close() + finally: + hkey.Close() finally: hreg.Close() + # Add Visual C++ for Python as a fallback in case one of the other Windows SDKs isn't installed + if use_windows_sdk: + localappdata = os.environ["LOCALAPPDATA"] + pywinsdk = localappdata + r"\Programs\Common\Microsoft\Visual C++ for Python\9.0\WinSDK\Bin" + if PLATFORM == "x64": + pywinsdk += r"\x64" + paths_to_check.append(("Visual C++ for Python", pywinsdk)) + + for sdk_name, path in paths_to_check: + path = os.path.join(path, tool) + if os.path.exists(path): + log.info("Using %s from %s" % (tool, sdk_name)) + return path + raise RuntimeError("%s could not be found" % tool) - + if DEVTOOLS == "MsDev": _xbuild = "\"%s\"" % _find_msbuild_tool("msbuild.exe") _defines_sep = ";" _config = "%sWin" % CONFIG - _npython_exe = "nPython.exe" elif DEVTOOLS == "Mono": _xbuild = "xbuild" _defines_sep = "," _config = "%sMono" % CONFIG - _npython_exe = "npython" else: raise NotImplementedError("DevTools %s not supported (use MsDev or Mono)" % DEVTOOLS) @@ -108,21 +126,55 @@ def build_extension(self, ext): if not os.path.exists(dest_dir): os.makedirs(dest_dir) + # Up to Python 3.2 sys.maxunicode is used to determine the size of Py_UNICODE + # but from 3.3 onwards Py_UNICODE is a typedef of wchar_t. + if sys.version_info[:2] <= (3, 2): + unicode_width = 2 if sys.maxunicode < 0x10FFFF else 4 + else: + import ctypes + unicode_width = ctypes.sizeof(ctypes.c_wchar) + defines = [ "PYTHON%d%s" % (sys.version_info[:2]), - "UCS2" if sys.maxunicode < 0x10FFFF else "UCS4", + "UCS%d" % unicode_width, ] if CONFIG == "Debug": defines.extend(["DEBUG", "TRACE"]) + if sys.platform != "win32" and DEVTOOLS == "Mono": + if sys.platform == "darwin": + defines.append("MONO_OSX") + else: + defines.append("MONO_LINUX") + + # Check if --enable-shared was set when Python was built + enable_shared = get_config_var("Py_ENABLE_SHARED") + if enable_shared == 0: + defines.append("PYTHON_WITHOUT_ENABLE_SHARED") + + if hasattr(sys, "abiflags"): + if "d" in sys.abiflags: + defines.append("PYTHON_WITH_PYDEBUG") + if "m" in sys.abiflags: + defines.append("PYTHON_WITH_PYMALLOC") + if "u" in sys.abiflags: + defines.append("PYTHON_WITH_WIDE_UNICODE") + + # check the interop file exists, and create it if it doesn't + interop_file = _get_interop_filename() + if not os.path.exists(interop_file): + geninterop = os.path.join("tools", "geninterop", "geninterop.py") + _check_output([sys.executable, geninterop, interop_file]) + cmd = [ _xbuild, "pythonnet.sln", "/p:Configuration=%s" % _config, "/p:Platform=%s" % PLATFORM, "/p:DefineConstants=\"%s\"" % _defines_sep.join(defines), - "/p:PythonBuildDir=%s" % os.path.abspath(dest_dir), + "/p:PythonBuildDir=\"%s\"" % os.path.abspath(dest_dir), + "/p:PythonInteropFile=\"%s\"" % os.path.basename(interop_file), "/verbosity:%s" % VERBOSITY, ] @@ -138,7 +190,6 @@ def build_extension(self, ext): if DEVTOOLS == "Mono": self._build_monoclr(ext) - def _get_manifest(self, build_dir): if DEVTOOLS == "MsDev" and sys.version_info[:2] > (2,5): mt = _find_msbuild_tool("mt.exe", use_windows_sdk=True) @@ -148,7 +199,6 @@ def _get_manifest(self, build_dir): check_call(" ".join(cmd), shell=False) return manifest - def _build_monoclr(self, ext): mono_libs = _check_output("pkg-config --libs mono-2", shell=True) mono_cflags = _check_output("pkg-config --cflags mono-2", shell=True) @@ -168,45 +218,6 @@ def _build_monoclr(self, ext): build_ext.build_extension(self, clr_ext) - # build the clr python executable - sources = [ - "src/monoclr/pynetinit.c", - "src/monoclr/python.c", - ] - - macros = ext.define_macros[:] - for undef in ext.undef_macros: - macros.append((undef,)) - - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=cflags.split(" "), - depends=ext.depends) - - output_dir = os.path.dirname(self.get_ext_fullpath(ext.name)) - py_libs = get_config_var("BLDLIBRARY") - libs += " " + py_libs - - # Include the directories python's shared libs were installed to. This - # is case python was built with --enable-shared as then npython will need - # to be able to find libpythonX.X.so. - runtime_library_dirs = (get_config_var("DESTDIRS") or "").split(" ") - if ext.runtime_library_dirs: - runtime_library_dirs.extend(ext.runtime_library_dirs) - - self.compiler.link_executable(objects, - _npython_exe, - output_dir=output_dir, - libraries=self.get_libraries(ext), - library_dirs=ext.library_dirs, - runtime_library_dirs=runtime_library_dirs, - extra_postargs=libs.split(" "), - debug=self.debug) - - def _install_packages(self): """install packages using nuget""" nuget = os.path.join("tools", "nuget", "nuget.exe") @@ -255,25 +266,6 @@ def run(self): self.data_files[i] = dest, data_files[1] return install_data.run(self) - - -class PythonNET_BuildScripts(build_scripts): - - def run(self): - build_scripts.finalize_options(self) - - # fixup scripts to look in the build_ext output folder - if self.scripts: - build_ext = self.get_finalized_command("build_ext") - output_dir = os.path.dirname(build_ext.get_ext_fullpath("clr")) - scripts = [] - for script in self.scripts: - if os.path.exists(os.path.join(output_dir, script)): - script = os.path.join(output_dir, script) - scripts.append(script) - self.scripts = scripts - - return build_scripts.run(self) def _check_output(*popenargs, **kwargs): @@ -289,9 +281,21 @@ def _check_output(*popenargs, **kwargs): if cmd is None: cmd = popenargs[0] raise CalledProcessError(retcode, cmd, output=output) + if sys.version_info[0] > 2: + return output.decode("ascii") return output +def _get_interop_filename(): + """interopXX.cs is auto-generated as part of the build. + For common windows platforms pre-generated files are included + as most windows users won't have Clang installed, which is + required to generate the file. + """ + interop_file = "interop%d%s%s.cs" % (sys.version_info[0], sys.version_info[1], getattr(sys, "abiflags", "")) + return os.path.join("src", "runtime", interop_file) + + if __name__ == "__main__": setupdir = os.path.dirname(__file__) if setupdir: @@ -311,15 +315,23 @@ def _check_output(*popenargs, **kwargs): for filename in fnmatch.filter(filenames, "*" + ext): sources.append(os.path.join(root, filename)) + setup_requires = [] + interop_file = _get_interop_filename() + if not os.path.exists(interop_file): + setup_requires.append("pycparser") + setup( name="pythonnet", - version="2.0.0", + version="2.1.0", description=".Net and Mono integration for Python", url='http://pythonnet.github.io/', author="Python for .Net developers", classifiers=[ - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Programming Language :: C#', 'License :: OSI Approved :: Zope Public License', 'Development Status :: 5 - Production/Stable', @@ -336,13 +348,12 @@ def _check_output(*popenargs, **kwargs): "{build_lib}/Python.Runtime.dll", "Python.Runtime.dll.config"]), ], - scripts=[_npython_exe], zip_safe=False, cmdclass={ "build_ext" : PythonNET_BuildExt, - "build_scripts" : PythonNET_BuildScripts, "install_lib" : PythonNET_InstallLib, "install_data": PythonNET_InstallData, - } + }, + setup_requires=setup_requires ) diff --git a/src/clrmodule/ClrModule.cs b/src/clrmodule/ClrModule.cs index 3347d55a9..801acf7db 100644 --- a/src/clrmodule/ClrModule.cs +++ b/src/clrmodule/ClrModule.cs @@ -30,7 +30,7 @@ // If DEBUG_PRINT is defined in the Build Properties, a few System.Console.WriteLine // calls are made to indicate what's going on during the load... //============================================================================ - +using System; // ReSharper disable CheckNamespace // ReSharper disable InconsistentNaming @@ -38,10 +38,14 @@ public class clrModule // ReSharper restore InconsistentNaming // ReSharper restore CheckNamespace { - - [RGiesecke.DllExport.DllExport("initclr", System.Runtime.InteropServices.CallingConvention.StdCall)] // ReSharper disable InconsistentNaming +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + [RGiesecke.DllExport.DllExport("PyInit_clr", System.Runtime.InteropServices.CallingConvention.StdCall)] + public static IntPtr PyInit_clr() +#else + [RGiesecke.DllExport.DllExport("initclr", System.Runtime.InteropServices.CallingConvention.StdCall)] public static void initclr() +#endif // ReSharper restore InconsistentNaming { #if DEBUG_PRINT @@ -76,7 +80,7 @@ public static void initclr() System.Console.WriteLine("Success!"); #endif } - catch (System.IO.FileNotFoundException) + catch (System.IO.IOException) { try { @@ -103,13 +107,22 @@ public static void initclr() #if DEBUG_PRINT System.Console.WriteLine("Could not load Python.Runtime, so sad."); #endif +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + return IntPtr.Zero; +#else return; +#endif } } // Once here, we've successfully loaded SOME version of Python.Runtime // So now we get the PythonEngine and execute the InitExt method on it. var pythonEngineType = pythonRuntime.GetType("Python.Runtime.PythonEngine"); + +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + return (IntPtr)pythonEngineType.InvokeMember("InitExt", System.Reflection.BindingFlags.InvokeMethod, null, null, null); +#else pythonEngineType.InvokeMember("InitExt", System.Reflection.BindingFlags.InvokeMethod, null, null, null); +#endif } } diff --git a/src/clrmodule/clrmodule.csproj b/src/clrmodule/clrmodule.csproj index 92dc2a945..f6d1a41b5 100644 --- a/src/clrmodule/clrmodule.csproj +++ b/src/clrmodule/clrmodule.csproj @@ -1,126 +1,131 @@ - - - - Debug - x86 - 8.0.30703 - 2.0 - {86E834DE-1139-4511-96CC-69636A56E7AC} - Library - Properties - clrmodule - clrmodule - v4.0 - 512 - ..\..\ - $(SolutionDir) - true - - - true - bin\x86\DebugMono\ - DEBUG;TRACE - full - x86 - prompt - true - true - false - - - true - bin\x64\DebugMono\ - DEBUG;TRACE - full - x64 - prompt - true - true - false - - - bin\x86\ReleaseMono\ - - true - pdbonly - x86 - prompt - true - true - false - - - bin\x64\ReleaseMono\ - - true - pdbonly - x64 - prompt - true - true - false - - - true - bin\x86\DebugWin\ - TRACE;DEBUG;DEBUG_PRINT - full - x86 - prompt - true - false - false - - - true - bin\x64\DebugWin\ - DEBUG;TRACE - full - x64 - prompt - true - true - false - - - bin\x86\ReleaseWin\ - - true - pdbonly - x86 - prompt - true - true - false - - - bin\x64\ReleaseWin\ - - true - pdbonly - x64 - prompt - true - true - false - - - - ..\..\packages\UnmanagedExports.1.2.6\lib\net\RGiesecke.DllExport.Metadata.dll - False - - - - - - - - - - - - - - - - \ No newline at end of file + + + + Debug + x86 + 8.0.30703 + 2.0 + {86E834DE-1139-4511-96CC-69636A56E7AC} + Library + Properties + clrmodule + clrmodule + v4.0 + 512 + ..\..\ + $(SolutionDir) + true + + + true + bin\x86\DebugMono\ + DEBUG;TRACE + full + x86 + prompt + true + true + false + + + true + bin\x64\DebugMono\ + DEBUG;TRACE + full + x64 + prompt + true + true + false + + + bin\x86\ReleaseMono\ + + true + pdbonly + x86 + prompt + true + true + false + + + bin\x64\ReleaseMono\ + + true + pdbonly + x64 + prompt + true + true + false + + + true + bin\x86\DebugWin\ + TRACE;DEBUG;DEBUG_PRINT + full + x86 + prompt + true + false + false + + + true + bin\x64\DebugWin\ + DEBUG;TRACE + full + x64 + prompt + true + true + false + + + bin\x86\ReleaseWin\ + + true + pdbonly + x86 + prompt + true + true + false + + + bin\x64\ReleaseWin\ + + true + pdbonly + x64 + prompt + true + true + false + + + + ..\..\packages\UnmanagedExports.1.2.6\lib\net\RGiesecke.DllExport.Metadata.dll + False + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj index 14f97f5fb..e4a9750b8 100644 --- a/src/embed_tests/Python.EmbeddingTest.csproj +++ b/src/embed_tests/Python.EmbeddingTest.csproj @@ -172,7 +172,12 @@ + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + - + + diff --git a/src/monoclr/clrmod.c b/src/monoclr/clrmod.c index 8b809b28f..c6de71eeb 100644 --- a/src/monoclr/clrmod.c +++ b/src/monoclr/clrmod.c @@ -25,21 +25,54 @@ PyDoc_STRVAR(clr_module_doc, static PyNet_Args *pn_args; char** environ = NULL; +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef clrdef = { + PyModuleDef_HEAD_INIT, + "clr", /* m_name */ + clr_module_doc, /* m_doc */ + -1, /* m_size */ + clr_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; +#endif + +static PyObject *_initclr() { + PyObject *m; + + /* Create the module and add the functions */ +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&clrdef); +#else + m = Py_InitModule3("clr", clr_methods, clr_module_doc); +#endif + if (m == NULL) + return NULL; + PyModule_AddObject(m, "facade", Py_True); + Py_INCREF(Py_True); + + pn_args = PyNet_Init(1); + if (pn_args->error) { + return NULL; + } + + if (NULL != pn_args->module) + return pn_args->module; + + return m; +} + +#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC +PyInit_clr(void) { + return _initclr(); +} +#else PyMODINIT_FUNC -initclr(void) -{ - PyObject *m; - - /* Create the module and add the functions */ - m = Py_InitModule3("clr", clr_methods, clr_module_doc); - if (m == NULL) - return; - PyModule_AddObject(m, "facade", Py_True); - Py_INCREF(Py_True); - - pn_args = PyNet_Init(1); - if (pn_args->error) { - return; - } +initclr(void) { + _initclr(); } +#endif diff --git a/src/monoclr/clrpython.c b/src/monoclr/clrpython.c deleted file mode 100644 index 5fddabf22..000000000 --- a/src/monoclr/clrpython.c +++ /dev/null @@ -1,29 +0,0 @@ -// ========================================================================== -// This software is subject to the provisions of the Zope Public License, -// Version 2.1 (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. -// ========================================================================== -// -// Example how to integrate Python, PythonNet and Mono into a C application -// It provides a command prompt equal to PythonNet's console but using a -// different path. -// -// Author: Christian Heimes -// - -#include "pynetclr.h" - -int main(int argc, char **argv) { - PyNet_Args *pn_args; - pn_args = PyNet_Init(0); - if (pn_args->error) { - exit(1); - } - int rc = Py_Main(argc, argv); - PyNet_Finalize(pn_args); - exit(rc); -} - diff --git a/src/monoclr/pynetclr.h b/src/monoclr/pynetclr.h index c97db10cb..3a6a60c9c 100644 --- a/src/monoclr/pynetclr.h +++ b/src/monoclr/pynetclr.h @@ -32,6 +32,7 @@ typedef struct { char *error; char *init_name; char *shutdown_name; + PyObject* module; } PyNet_Args; PyNet_Args* PyNet_Init(int); diff --git a/src/monoclr/pynetinit.c b/src/monoclr/pynetinit.c index eaa1d9c8b..df9d67c9a 100644 --- a/src/monoclr/pynetinit.c +++ b/src/monoclr/pynetinit.c @@ -10,11 +10,16 @@ // Author: Christian Heimes #include "pynetclr.h" +#include "stdlib.h" #ifndef _WIN32 #include "dirent.h" +#include "dlfcn.h" +#include "libgen.h" +#include "alloca.h" #endif + // initialize Mono and PythonNet PyNet_Args* PyNet_Init(int ext) { PyNet_Args *pn_args; @@ -22,6 +27,7 @@ PyNet_Args* PyNet_Init(int ext) { pn_args->pr_file = PR_ASSEMBLY; pn_args->error = NULL; pn_args->shutdown = NULL; + pn_args->module = NULL; if (ext == 0) { pn_args->init_name = "Python.Runtime:Initialize()"; @@ -31,6 +37,7 @@ PyNet_Args* PyNet_Init(int ext) { pn_args->shutdown_name = "Python.Runtime:Shutdown()"; pn_args->domain = mono_jit_init_version(MONO_DOMAIN, MONO_VERSION); + mono_domain_set_config(pn_args->domain, ".", "Python.Runtime.dll.config"); /* * Load the default Mono configuration file, this is needed @@ -91,8 +98,29 @@ void main_thread_handler (gpointer user_data) { MonoImage *pr_image; MonoClass *pythonengine; MonoObject *exception = NULL; + MonoObject *init_result; #ifndef _WIN32 + // Get the filename of the python shared object and set + // LD_LIBRARY_PATH so Mono can find it. + Dl_info dlinfo = {0}; + if (0 != dladdr(&Py_Initialize, &dlinfo)) { + char* fname = alloca(strlen(dlinfo.dli_fname) + 1); + strcpy(fname, dlinfo.dli_fname); + char* py_libdir = dirname(fname); + char* ld_library_path = getenv("LD_LIBRARY_PATH"); + if (NULL == ld_library_path) { + setenv("LD_LIBRARY_PATH", py_libdir, 1); + } else { + char* new_ld_library_path = alloca(strlen(py_libdir) + + strlen(ld_library_path) + + 2); + strcpy(new_ld_library_path, py_libdir); + strcat(new_ld_library_path, ":"); + strcat(new_ld_library_path, ld_library_path); + setenv("LD_LIBRARY_PATH", py_libdir, 1); + } + } //get python path system variable PyObject* syspath = PySys_GetObject("path"); @@ -102,11 +130,25 @@ void main_thread_handler (gpointer user_data) { int ii = 0; for (ii = 0; ii < PyList_Size(syspath); ++ii) { +#if PY_MAJOR_VERSION > 2 + Py_ssize_t wlen; + wchar_t *wstr = PyUnicode_AsWideCharString(PyList_GetItem(syspath, ii), &wlen); + char* pydir = (char*)malloc(wlen + 1); + size_t mblen = wcstombs(pydir, wstr, wlen + 1); + if (mblen > wlen) + pydir[wlen] = '\0'; + PyMem_Free(wstr); +#else const char* pydir = PyString_AsString(PyList_GetItem(syspath, ii)); +#endif char* curdir = (char*) malloc(1024); strncpy(curdir, strlen(pydir) > 0 ? pydir : ".", 1024); strncat(curdir, slash, 1024); +#if PY_MAJOR_VERSION > 2 + free(pydir); +#endif + //look in this directory for the pn_args->pr_file DIR* dirp = opendir(curdir); if (dirp != NULL) { @@ -170,11 +212,17 @@ void main_thread_handler (gpointer user_data) { return; } - mono_runtime_invoke(init, NULL, NULL, &exception); + init_result = mono_runtime_invoke(init, NULL, NULL, &exception); if (exception) { pn_args->error = PyNet_ExceptionToString(exception); return; } + +#if PY_MAJOR_VERSION >= 3 + if (NULL != init_result) + pn_args->module = *(PyObject**)mono_object_unbox(init_result); +#endif + } // Get string from a Mono exception diff --git a/src/monoclr/python.c b/src/monoclr/python.c deleted file mode 100644 index aa340491f..000000000 --- a/src/monoclr/python.c +++ /dev/null @@ -1,22 +0,0 @@ -// ========================================================================== -// This software is subject to the provisions of the Zope Public License, -// Version 2.1 (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. -// ========================================================================== -// -// python.c provides a python executable with is dynamically linked agaist -// libpython2.x.so. For example Ubuntu's python executables aren't linked -// against libpython :( -// -// Author: Christian Heimes -// - -#include - -int main(int argc, char **argv) { - return Py_Main(argc, argv); -} - diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index f5bf3378a..7bfaff313 100644 --- a/src/runtime/Python.Runtime.csproj +++ b/src/runtime/Python.Runtime.csproj @@ -1,190 +1,212 @@ - - - - Debug - x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271} - Library - false - Python.Runtime - Python.Runtime - ..\..\ - $(SolutionDir) - - - bin\x86\ReleaseMono\ - PYTHON27, UCS4 - true - true - pdbonly - x86 - false - true - - - bin\x64\ReleaseMono\ - PYTHON27, UCS4 - true - true - pdbonly - x64 - false - true - - - bin\x86\ReleaseWin\ - PYTHON27, UCS2 - true - true - pdbonly - x86 - false - true - - - bin\x64\ReleaseWin\ - PYTHON27, UCS2 - true - true - pdbonly - x64 - false - true - - - true - bin\x86\DebugMono\ - TRACE;DEBUG;PYTHON27,UCS4 - true - false - full - x86 - false - false - false - - - true - bin\x64\DebugMono\ - TRACE;DEBUG;PYTHON27,UCS4 - true - false - full - x64 - - - true - bin\x86\DebugWin\ - TRACE;DEBUG;PYTHON27,UCS2 - true - false - full - x86 - false - false - false - - - true - bin\x64\DebugWin\ - TRACE;DEBUG;PYTHON27,UCS2 - true - false - full - x64 - - - - - - False - ..\..\packages\MonoGAC\Mono.Posix\4.0.0.0__0738eb9f132ed756\Mono.Posix.dll - - - - - - - False - ..\..\packages\MonoGAC\Mono.Posix\4.0.0.0__0738eb9f132ed756\Mono.Posix.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + Debug + x86 + {097B4AC0-74E9-4C58-BCF8-C69746EC8271} + Library + false + Python.Runtime + Python.Runtime + ..\..\ + $(SolutionDir) + + + bin\x86\ReleaseMono\ + PYTHON27, UCS4 + true + true + pdbonly + x86 + false + true + PYTHON27,UCS2 + + + bin\x64\ReleaseMono\ + PYTHON27, UCS4 + true + true + pdbonly + x64 + false + true + + + bin\x86\ReleaseWin\ + PYTHON27, UCS2 + true + true + pdbonly + x86 + false + true + + + bin\x64\ReleaseWin\ + PYTHON27, UCS2 + true + true + pdbonly + x64 + false + true + + + true + bin\x86\DebugMono\ + TRACE;DEBUG;PYTHON27,UCS4 + true + false + full + x86 + false + false + false + + + true + bin\x64\DebugMono\ + TRACE;DEBUG;PYTHON27,UCS4 + true + false + full + x64 + + + true + bin\x86\DebugWin\ + TRACE;DEBUG;PYTHON27,UCS2 + true + false + full + x86 + false + false + false + + + true + bin\x64\DebugWin\ + TRACE;DEBUG;PYTHON27,UCS2 + true + false + full + x64 + + + + + + False + ..\..\packages\MonoGAC\Mono.Posix\4.0.0.0__0738eb9f132ed756\Mono.Posix.dll + + + + + + + False + ..\..\packages\MonoGAC\Mono.Posix\4.0.0.0__0738eb9f132ed756\Mono.Posix.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + clr.py + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + diff --git a/src/runtime/assemblymanager.cs b/src/runtime/assemblymanager.cs index 583b5c945..a217e84e2 100644 --- a/src/runtime/assemblymanager.cs +++ b/src/runtime/assemblymanager.cs @@ -12,6 +12,7 @@ using System.Collections; using System.Collections.Specialized; using System.Collections.Generic; +using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; @@ -57,10 +58,17 @@ internal static void Initialize() { domain.AssemblyResolve += rhandler; Assembly[] items = domain.GetAssemblies(); - for (int i = 0; i < items.Length; i++) { - Assembly a = items[i]; - assemblies.Add(a); - ScanAssembly(a); + foreach (var a in items) + { + try + { + ScanAssembly(a); + assemblies.Add(a); + } + catch (Exception ex) + { + Debug.WriteLine(string.Format("Error scanning assembly {0}. {1}", a, ex)); + } } } @@ -208,6 +216,24 @@ public static Assembly LoadAssemblyPath(string name) { return assembly; } + /// + /// Loads an assembly using full path. + /// + /// + /// + public static Assembly LoadAssemblyFullPath(string name) { + Assembly assembly = null; + if (Path.IsPathRooted(name)) { + if (!Path.HasExtension(name)) + name = name + ".dll"; + if (File.Exists(name)) { + try { assembly = Assembly.LoadFrom(name); } + catch { } + } + } + return assembly; + } + //=================================================================== // Returns an assembly that's already been loaded //=================================================================== @@ -282,7 +308,7 @@ public static bool LoadImplicit(string name, bool warn=true) { // be valid namespaces (to better match Python import semantics). //=================================================================== - static void ScanAssembly(Assembly assembly) { + internal 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 @@ -291,16 +317,14 @@ static void ScanAssembly(Assembly 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 ns = t.Namespace ?? ""; + if (!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() - ); + namespaces.Add(s, new Dictionary()); } } } @@ -336,6 +360,16 @@ public static bool IsValidNamespace(string name) { return namespaces.ContainsKey(name); } + //=================================================================== + // Returns list of assemblies that declare types in a given namespace + //=================================================================== + + public static IEnumerable GetAssemblies(string nsname) { + if (!namespaces.ContainsKey(nsname)) + return new List(); + + return namespaces[nsname].Keys; + } //=================================================================== // Returns the current list of valid names for the input namespace. @@ -357,7 +391,7 @@ public static List GetNames(string nsname) { Type[] types = a.GetTypes(); for (int i = 0; i < types.Length; i++) { Type t = types[i]; - if (t.Namespace == nsname) { + if ((t.Namespace ?? "") == nsname) { names.Add(t.Name); } } diff --git a/src/runtime/buildclrmodule.bat b/src/runtime/buildclrmodule.bat index 125ff9090..549902d7f 100644 --- a/src/runtime/buildclrmodule.bat +++ b/src/runtime/buildclrmodule.bat @@ -1,36 +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%==x86 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 or 3.5 -:::%windir%\Microsoft.NET\Framework\v2.0.50727\ilasm /nologo /quiet /dll %ILASM_EXTRA_ARGS% /include=%INCLUDE_PATH% /output=%OUTPUT_PATH% %INPUT_PATH% +:: 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%==x86 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 or 3.5 +:::%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/src/runtime/classbase.cs b/src/runtime/classbase.cs index 1541b12cd..4aba01df0 100644 --- a/src/runtime/classbase.cs +++ b/src/runtime/classbase.cs @@ -51,13 +51,68 @@ public static int tp_init(IntPtr ob, IntPtr args, IntPtr kw) { //==================================================================== public virtual IntPtr type_subscript(IntPtr idx) { - return Exceptions.RaiseTypeError("unsubscriptable object"); + Type[] types = Runtime.PythonArgsToTypeArray(idx); + if (types == null) { + return Exceptions.RaiseTypeError("type(s) expected"); + } + + Type target = GenericUtil.GenericForType(this.type, types.Length); + + 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"); } //==================================================================== // Standard comparison implementation for instances of reflected types. //==================================================================== +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + public static IntPtr tp_richcompare(IntPtr ob, IntPtr other, int op) { + if (op != Runtime.Py_EQ && op != Runtime.Py_NE) + { + Runtime.Incref(Runtime.PyNotImplemented); + return Runtime.PyNotImplemented; + } + + IntPtr pytrue = Runtime.PyTrue; + IntPtr pyfalse = Runtime.PyFalse; + + // swap true and false for NE + if (op != Runtime.Py_EQ) + { + pytrue = Runtime.PyFalse; + pyfalse = Runtime.PyTrue; + } + + if (ob == other) { + Runtime.Incref(pytrue); + return pytrue; + } + + CLRObject co1 = GetManagedObject(ob) as CLRObject; + CLRObject co2 = GetManagedObject(other) as CLRObject; + if (null == co2) { + Runtime.Incref(pyfalse); + return pyfalse; + } + + Object o1 = co1.inst; + Object o2 = co2.inst; + if (Object.Equals(o1, o2)) { + Runtime.Incref(pytrue); + return pytrue; + } + + Runtime.Incref(pyfalse); + return pyfalse; + } +#else public static int tp_compare(IntPtr ob, IntPtr other) { if (ob == other) { return 0; @@ -73,6 +128,7 @@ public static int tp_compare(IntPtr ob, IntPtr other) { } return -1; } +#endif //==================================================================== @@ -128,7 +184,17 @@ public static IntPtr tp_str(IntPtr ob) { if (co == null) { return Exceptions.RaiseTypeError("invalid object"); } - return Runtime.PyString_FromString(co.inst.ToString()); + try { + return Runtime.PyString_FromString(co.inst.ToString()); + } + catch (Exception e) + { + if (e.InnerException != null) { + e = e.InnerException; + } + Exceptions.SetError(e); + return IntPtr.Zero; + } } @@ -154,7 +220,7 @@ public static int tp_is_gc(IntPtr type) { public static void tp_dealloc(IntPtr ob) { ManagedType self = GetManagedObject(ob); - IntPtr dict = Marshal.ReadIntPtr(ob, ObjectOffset.ob_dict); + IntPtr dict = Marshal.ReadIntPtr(ob, ObjectOffset.DictOffset(ob)); if (dict != IntPtr.Zero) { Runtime.Decref(dict); } diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs new file mode 100644 index 000000000..baffbd4e3 --- /dev/null +++ b/src/runtime/classderived.cs @@ -0,0 +1,865 @@ +// ========================================================================== +// 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.Reflection; +using System.Reflection.Emit; +using System.Collections.Generic; +using System.Threading; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +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. + /// + + // interface used to idenfity which C# types were dynamically created as python subclasses + public interface IPythonDerivedType + { + } + + internal class ClassDerivedObject : ClassObject + { + static private Dictionary assemblyBuilders; + static private Dictionary, ModuleBuilder> moduleBuilders; + + static ClassDerivedObject() + { + assemblyBuilders = new Dictionary(); + moduleBuilders = new Dictionary, ModuleBuilder>(); + } + + internal ClassDerivedObject(Type tp) + : base(tp) + { + } + + /// + /// Implements __new__ for derived classes of reflected classes. + /// + new public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) + { + ClassDerivedObject cls = GetManagedObject(tp) as ClassDerivedObject; + + // call the managed constructor + Object obj = cls.binder.InvokeRaw(IntPtr.Zero, args, kw); + if (obj == null) + return IntPtr.Zero; + + // return the pointer to the python object + // (this indirectly calls ClassDerivedObject.ToPython) + return Converter.ToPython(obj, cls.GetType()); + } + + new public static void tp_dealloc(IntPtr ob) + { + CLRObject self = (CLRObject)GetManagedObject(ob); + + // don't let the python GC destroy this object + Runtime.PyObject_GC_UnTrack(self.pyHandle); + + // The python should now have a ref count of 0, but we don't actually want to + // deallocate the object until the C# object that references it is destroyed. + // So we don't call PyObject_GC_Del here and instead we set the python + // reference to a weak reference so that the C# object can be collected. + GCHandle gc = GCHandle.Alloc(self, GCHandleType.Weak); + Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc); + self.gcHandle.Free(); + self.gcHandle = gc; + } + + // Called from Converter.ToPython for types that are python subclasses of managed types. + // The referenced python object is returned instead of a new wrapper. + internal static IntPtr ToPython(IPythonDerivedType obj) + { + // derived types have a __pyobj__ field that gets set to the python + // object in the overriden constructor + FieldInfo fi = obj.GetType().GetField("__pyobj__"); + CLRObject self = (CLRObject)fi.GetValue(obj); + + Runtime.Incref(self.pyHandle); + + // when the C# constructor creates the python object it starts as a weak + // reference with a reference count of 0. Now we're passing this object + // to Python the reference count needs to be incremented and the reference + // needs to be replaced with a strong reference to stop the C# object being + // collected while Python still has a reference to it. + if (Runtime.Refcount(self.pyHandle) == 1) + { + GCHandle gc = GCHandle.Alloc(self, GCHandleType.Normal); + Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc); + self.gcHandle.Free(); + self.gcHandle = gc; + + // now the object has a python reference it's safe for the python GC to track it + Runtime.PyObject_GC_Track(self.pyHandle); + } + + return self.pyHandle; + } + + /// + /// Creates a new managed type derived from a base type with any virtual + /// methods overriden to call out to python if the associated python + /// object has overriden the method. + /// + internal static Type CreateDerivedType(string name, + Type baseType, + IntPtr py_dict, + string namespaceStr, + string assemblyName, + string moduleName="Python.Runtime.Dynamic.dll") + { + if (null != namespaceStr) + name = namespaceStr + "." + name; + + if (null == assemblyName) + assemblyName = Assembly.GetExecutingAssembly().FullName; + + ModuleBuilder moduleBuilder = GetModuleBuilder(assemblyName, moduleName); + TypeBuilder typeBuilder; + + Type baseClass = baseType; + List interfaces = new List { typeof(IPythonDerivedType) }; + + // if the base type is an interface then use System.Object as the base class + // and add the base type to the list of interfaces this new class will implement. + if (baseType.IsInterface) + { + interfaces.Add(baseType); + baseClass = typeof(System.Object); + } + + typeBuilder = moduleBuilder.DefineType(name, + TypeAttributes.Public | TypeAttributes.Class, + baseClass, + interfaces.ToArray()); + + // add a field for storing the python object pointer + FieldBuilder fb = typeBuilder.DefineField("__pyobj__", typeof(CLRObject), FieldAttributes.Public); + + // override any constructors + ConstructorInfo[] constructors = baseClass.GetConstructors(); + foreach (ConstructorInfo ctor in constructors) + { + AddConstructor(ctor, baseType, typeBuilder); + } + + // Override any properties explicitly overriden in python + HashSet pyProperties = new HashSet(); + if (py_dict != IntPtr.Zero && Runtime.PyDict_Check(py_dict)) + { + Runtime.Incref(py_dict); + using (PyDict dict = new PyDict(py_dict)) + using (PyObject keys = dict.Keys()) + { + foreach (PyObject pyKey in keys) + { + using (PyObject value = dict[pyKey]) + if (value.HasAttr("_clr_property_type_")) + { + string propertyName = pyKey.ToString(); + pyProperties.Add(propertyName); + + // Add the property to the type + AddPythonProperty(propertyName, value, typeBuilder); + } + } + } + } + + // override any virtual methods not already overriden by the properties above + MethodInfo[] methods = baseType.GetMethods(); + HashSet virtualMethods = new HashSet(); + foreach (MethodInfo method in methods) + { + if (!method.Attributes.HasFlag(MethodAttributes.Virtual) | method.Attributes.HasFlag(MethodAttributes.Final)) + continue; + + // skip if this property has already been overriden + if ((method.Name.StartsWith("get_") || method.Name.StartsWith("set_")) + && pyProperties.Contains(method.Name.Substring(4))) + continue; + + // keep track of the virtual methods redirected to the python instance + virtualMethods.Add(method.Name); + + // override the virtual method to call out to the python method, if there is one. + AddVirtualMethod(method, baseType, typeBuilder); + } + + // Add any additional methods and properties explicitly exposed from Python. + if (py_dict != IntPtr.Zero && Runtime.PyDict_Check(py_dict)) + { + Runtime.Incref(py_dict); + using (PyDict dict = new PyDict(py_dict)) + using (PyObject keys = dict.Keys()) + { + foreach (PyObject pyKey in keys) + { + using (PyObject value = dict[pyKey]) + if (value.HasAttr("_clr_return_type_") && value.HasAttr("_clr_arg_types_")) + { + string methodName = pyKey.ToString(); + + // if this method has already been redirected to the python method skip it + if (virtualMethods.Contains(methodName)) + continue; + + // Add the method to the type + AddPythonMethod(methodName, value, typeBuilder); + } + } + } + } + + // add the destructor so the python object created in the constructor gets destroyed + MethodBuilder methodBuilder = typeBuilder.DefineMethod("Finalize", + MethodAttributes.Family | + MethodAttributes.Virtual | + MethodAttributes.HideBySig, + CallingConventions.Standard, + typeof(void), + Type.EmptyTypes); + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("Finalize")); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Call, baseClass.GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance)); + il.Emit(OpCodes.Ret); + + Type type = typeBuilder.CreateType(); + + // scan the assembly so the newly added class can be imported + Assembly assembly = Assembly.GetAssembly(type); + AssemblyManager.ScanAssembly(assembly); + + AssemblyBuilder assemblyBuilder = assemblyBuilders[assemblyName]; + + return type; + } + + /// + /// Add a constructor override that calls the python ctor after calling the base type constructor. + /// + /// constructor to be called before calling the python ctor + /// Python callable object + /// TypeBuilder for the new type the ctor is to be added to + private static void AddConstructor(ConstructorInfo ctor, Type baseType, TypeBuilder typeBuilder) + { + ParameterInfo[] parameters = ctor.GetParameters(); + Type[] parameterTypes = (from param in parameters select param.ParameterType).ToArray(); + + // create a method for calling the original constructor + string baseCtorName = "_" + baseType.Name + "__cinit__"; + MethodBuilder methodBuilder = typeBuilder.DefineMethod(baseCtorName, + MethodAttributes.Public | + MethodAttributes.Final | + MethodAttributes.HideBySig, + typeof(void), + parameterTypes); + + // emit the assembly for calling the original method using call instead of callvirt + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + for (int i = 0; i < parameters.Length; ++i) + il.Emit(OpCodes.Ldarg, i + 1); + il.Emit(OpCodes.Call, ctor); + il.Emit(OpCodes.Ret); + + // override the original method with a new one that dispatches to python + ConstructorBuilder cb = typeBuilder.DefineConstructor(MethodAttributes.Public | + MethodAttributes.ReuseSlot | + MethodAttributes.HideBySig, + ctor.CallingConvention, + parameterTypes); + il = cb.GetILGenerator(); + il.DeclareLocal(typeof(Object[])); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldstr, baseCtorName); + il.Emit(OpCodes.Ldc_I4, parameters.Length); + il.Emit(OpCodes.Newarr, typeof(System.Object)); + il.Emit(OpCodes.Stloc_0); + for (int i = 0; i < parameters.Length; ++i) + { + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldarg, i + 1); + if (parameterTypes[i].IsValueType) + il.Emit(OpCodes.Box, parameterTypes[i]); + il.Emit(OpCodes.Stelem, typeof(Object)); + } + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeCtor")); + il.Emit(OpCodes.Ret); + } + + /// + /// Add a virtual method override that checks for an override on the python instance + /// and calls it, otherwise fall back to the base class method. + /// + /// virtual method to be overriden + /// Python callable object + /// TypeBuilder for the new type the method is to be added to + private static void AddVirtualMethod(MethodInfo method, Type baseType, TypeBuilder typeBuilder) + { + + ParameterInfo[] parameters = method.GetParameters(); + Type[] parameterTypes = (from param in parameters select param.ParameterType).ToArray(); + + // If the method isn't abstract create a method for calling the original method + string baseMethodName = null; + if (!method.IsAbstract) + { + baseMethodName = "_" + baseType.Name + "__" + method.Name; + MethodBuilder baseMethodBuilder = typeBuilder.DefineMethod(baseMethodName, + MethodAttributes.Public | + MethodAttributes.Final | + MethodAttributes.HideBySig, + method.ReturnType, + parameterTypes); + + // emit the assembly for calling the original method using call instead of callvirt + ILGenerator baseIl = baseMethodBuilder.GetILGenerator(); + baseIl.Emit(OpCodes.Ldarg_0); + for (int i = 0; i < parameters.Length; ++i) + baseIl.Emit(OpCodes.Ldarg, i + 1); + baseIl.Emit(OpCodes.Call, method); + baseIl.Emit(OpCodes.Ret); + } + + // override the original method with a new one that dispatches to python + MethodBuilder methodBuilder = typeBuilder.DefineMethod(method.Name, + MethodAttributes.Public | + MethodAttributes.ReuseSlot | + MethodAttributes.Virtual | + MethodAttributes.HideBySig, + method.CallingConvention, + method.ReturnType, + parameterTypes); + ILGenerator il = methodBuilder.GetILGenerator(); + il.DeclareLocal(typeof(Object[])); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldstr, method.Name); + + // don't fall back to the base type's method if it's abstract + if (null != baseMethodName) + il.Emit(OpCodes.Ldstr, baseMethodName); + else + il.Emit(OpCodes.Ldnull); + + il.Emit(OpCodes.Ldc_I4, parameters.Length); + il.Emit(OpCodes.Newarr, typeof(System.Object)); + il.Emit(OpCodes.Stloc_0); + for (int i = 0; i < parameters.Length; ++i) + { + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldarg, i + 1); + if (parameterTypes[i].IsValueType) + il.Emit(OpCodes.Box, parameterTypes[i]); + il.Emit(OpCodes.Stelem, typeof(Object)); + } + il.Emit(OpCodes.Ldloc_0); + if (method.ReturnType == typeof(void)) + { + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethodVoid")); + } + else + { + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethod").MakeGenericMethod(method.ReturnType)); + } + il.Emit(OpCodes.Ret); + } + + /// + /// Python method may have the following function attributes set to control how they're exposed: + /// - _clr_return_type_ - method return type (required) + /// - _clr_arg_types_ - list of method argument types (required) + /// - _clr_method_name_ - method name, if different from the python method name (optional) + /// + /// Method name to add to the type + /// Python callable object + /// TypeBuilder for the new type the method/property is to be added to + private static void AddPythonMethod(string methodName, PyObject func, TypeBuilder typeBuilder) + { + if (func.HasAttr("_clr_method_name_")) + { + using (PyObject pyMethodName = func.GetAttr("_clr_method_name_")) + methodName = pyMethodName.ToString(); + } + + using (PyObject pyReturnType = func.GetAttr("_clr_return_type_")) + using (PyObject pyArgTypes = func.GetAttr("_clr_arg_types_")) + { + Type returnType = pyReturnType.AsManagedObject(typeof(Type)) as Type; + if (returnType == null) + returnType = typeof(void); + + if (!pyArgTypes.IsIterable()) + throw new ArgumentException("_clr_arg_types_ must be a list or tuple of CLR types"); + + List argTypes = new List(); + foreach (PyObject pyArgType in pyArgTypes) + { + Type argType = pyArgType.AsManagedObject(typeof(Type)) as Type; + if (argType == null) + throw new ArgumentException("_clr_arg_types_ must be a list or tuple of CLR types"); + argTypes.Add(argType); + } + + // add the method to call back into python + MethodAttributes methodAttribs = MethodAttributes.Public | + MethodAttributes.Virtual | + MethodAttributes.ReuseSlot | + MethodAttributes.HideBySig; + + MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName, + methodAttribs, + returnType, + argTypes.ToArray()); + + ILGenerator il = methodBuilder.GetILGenerator(); + il.DeclareLocal(typeof(Object[])); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldstr, methodName); + il.Emit(OpCodes.Ldnull); // don't fall back to the base type's method + il.Emit(OpCodes.Ldc_I4, argTypes.Count); + il.Emit(OpCodes.Newarr, typeof(System.Object)); + il.Emit(OpCodes.Stloc_0); + for (int i = 0; i < argTypes.Count; ++i) + { + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldarg, i + 1); + if (argTypes[i].IsValueType) + il.Emit(OpCodes.Box, argTypes[i]); + il.Emit(OpCodes.Stelem, typeof(Object)); + } + il.Emit(OpCodes.Ldloc_0); + if (returnType == typeof(void)) + { + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethodVoid")); + } + else + { + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethod").MakeGenericMethod(returnType)); + } + il.Emit(OpCodes.Ret); + } + } + + /// + /// Python properties may have the following function attributes set to control how they're exposed: + /// - _clr_property_type_ - property type (required) + /// + /// Property name to add to the type + /// Python property object + /// TypeBuilder for the new type the method/property is to be added to + private static void AddPythonProperty(string propertyName, PyObject func, TypeBuilder typeBuilder) + { + // add the method to call back into python + MethodAttributes methodAttribs = MethodAttributes.Public | + MethodAttributes.Virtual | + MethodAttributes.ReuseSlot | + MethodAttributes.HideBySig | + MethodAttributes.SpecialName; + + using (PyObject pyPropertyType = func.GetAttr("_clr_property_type_")) + { + Type propertyType = pyPropertyType.AsManagedObject(typeof(Type)) as Type; + if (propertyType == null) + throw new ArgumentException("_clr_property_type must be a CLR type"); + + PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, + PropertyAttributes.None, + propertyType, + null); + + if (func.HasAttr("fget")) + { + using (PyObject pyfget = func.GetAttr("fget")) + if (pyfget.IsTrue()) + { + MethodBuilder methodBuilder = typeBuilder.DefineMethod("get_" + propertyName, + methodAttribs, + propertyType, + null); + + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldstr, propertyName); + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeGetProperty").MakeGenericMethod(propertyType)); + il.Emit(OpCodes.Ret); + + propertyBuilder.SetGetMethod(methodBuilder); + } + } + + if (func.HasAttr("fset")) + { + using (PyObject pyset = func.GetAttr("fset")) + if (pyset.IsTrue()) + { + MethodBuilder methodBuilder = typeBuilder.DefineMethod("set_" + propertyName, + methodAttribs, + null, + new Type[]{propertyType}); + + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldstr, propertyName); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeSetProperty").MakeGenericMethod(propertyType)); + il.Emit(OpCodes.Ret); + + propertyBuilder.SetSetMethod(methodBuilder); + } + } + } + } + + private static ModuleBuilder GetModuleBuilder(string assemblyName, string moduleName) + { + // find or create a dynamic assembly and module + AppDomain domain = AppDomain.CurrentDomain; + ModuleBuilder moduleBuilder = null; + + if (moduleBuilders.ContainsKey(Tuple.Create(assemblyName, moduleName))) + { + moduleBuilder = moduleBuilders[Tuple.Create(assemblyName, moduleName)]; + } + else + { + AssemblyBuilder assemblyBuilder = null; + if (assemblyBuilders.ContainsKey(assemblyName)) + { + assemblyBuilder = assemblyBuilders[assemblyName]; + } + else + { + assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), + AssemblyBuilderAccess.Run); + assemblyBuilders[assemblyName] = assemblyBuilder; + } + + moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName); + moduleBuilders[Tuple.Create(assemblyName, moduleName)] = moduleBuilder; + } + + return moduleBuilder; + } + } + + // + // PythonDerivedType contains static methods used by the dynamically created + // derived type that allow it to call back into python from overriden virtual + // methods, and also handle the construction and destruction of the python + // object. + // + // This has to be public as it's called from methods on dynamically built classes + // potentially in other assemblies. + // + public class PythonDerivedType + { + //==================================================================== + // This is the implementaion of the overriden methods in the derived + // type. It looks for a python method with the same name as the method + // on the managed base class and if it exists and isn't the managed + // method binding (ie it has been overriden in the derived python + // class) it calls it, otherwise it calls the base method. + //==================================================================== + public static T InvokeMethod(IPythonDerivedType obj, string methodName, string origMethodName, Object[] args) + { + FieldInfo fi = obj.GetType().GetField("__pyobj__"); + CLRObject self = (CLRObject)fi.GetValue(obj); + + if (null != self) + { + List disposeList = new List(); + IntPtr gs = Runtime.PyGILState_Ensure(); + try + { + Runtime.Incref(self.pyHandle); + PyObject pyself = new PyObject(self.pyHandle); + disposeList.Add(pyself); + + Runtime.Incref(Runtime.PyNone); + PyObject pynone = new PyObject(Runtime.PyNone); + disposeList.Add(pynone); + + PyObject method = pyself.GetAttr(methodName, pynone); + disposeList.Add(method); + if (method.Handle != Runtime.PyNone) + { + // if the method hasn't been overriden then it will be a managed object + ManagedType managedMethod = ManagedType.GetManagedObject(method.Handle); + if (null == managedMethod) + { + PyObject[] pyargs = new PyObject[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + pyargs[i] = new PyObject(Converter.ToPythonImplicit(args[i])); + disposeList.Add(pyargs[i]); + } + + PyObject py_result = method.Invoke(pyargs); + disposeList.Add(py_result); + return (T)py_result.AsManagedObject(typeof(T)); + } + } + } + finally + { + foreach (PyObject x in disposeList) { + if (x != null) + x.Dispose(); + } + Runtime.PyGILState_Release(gs); + } + } + + if (origMethodName == null) + throw new NotImplementedException("Python object does not have a '" + methodName + "' method"); + + return (T)obj.GetType().InvokeMember(origMethodName, + BindingFlags.InvokeMethod, + null, + obj, + args); + } + + public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, string origMethodName, Object[] args) + { + FieldInfo fi = obj.GetType().GetField("__pyobj__"); + CLRObject self = (CLRObject)fi.GetValue(obj); + if (null != self) + { + List disposeList = new List(); + IntPtr gs = Runtime.PyGILState_Ensure(); + try + { + Runtime.Incref(self.pyHandle); + PyObject pyself = new PyObject(self.pyHandle); + disposeList.Add(pyself); + + Runtime.Incref(Runtime.PyNone); + PyObject pynone = new PyObject(Runtime.PyNone); + disposeList.Add(pynone); + + PyObject method = pyself.GetAttr(methodName, pynone); + disposeList.Add(method); + if (method.Handle != Runtime.PyNone) + { + // if the method hasn't been overriden then it will be a managed object + ManagedType managedMethod = ManagedType.GetManagedObject(method.Handle); + if (null == managedMethod) + { + PyObject[] pyargs = new PyObject[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + pyargs[i] = new PyObject(Converter.ToPythonImplicit(args[i])); + disposeList.Add(pyargs[i]); + } + + PyObject py_result = method.Invoke(pyargs); + disposeList.Add(py_result); + return; + } + } + } + finally + { + foreach (PyObject x in disposeList) { + if (x != null) + x.Dispose(); + } + Runtime.PyGILState_Release(gs); + } + } + + if (origMethodName == null) + throw new NotImplementedException("Python object does not have a '" + methodName + "' method"); + + obj.GetType().InvokeMember(origMethodName, + BindingFlags.InvokeMethod, + null, + obj, + args); + } + + public static T InvokeGetProperty(IPythonDerivedType obj, string propertyName) + { + FieldInfo fi = obj.GetType().GetField("__pyobj__"); + CLRObject self = (CLRObject)fi.GetValue(obj); + + if (null == self) + throw new NullReferenceException("Instance must be specified when getting a property"); + + IntPtr gs = Runtime.PyGILState_Ensure(); + try + { + Runtime.Incref(self.pyHandle); + using (PyObject pyself = new PyObject(self.pyHandle)) + using (PyObject pyvalue = pyself.GetAttr(propertyName)) + return (T)pyvalue.AsManagedObject(typeof(T)); + } + finally + { + Runtime.PyGILState_Release(gs); + } + } + + public static void InvokeSetProperty(IPythonDerivedType obj, string propertyName, T value) + { + FieldInfo fi = obj.GetType().GetField("__pyobj__"); + CLRObject self = (CLRObject)fi.GetValue(obj); + + if (null == self) + throw new NullReferenceException("Instance must be specified when setting a property"); + + IntPtr gs = Runtime.PyGILState_Ensure(); + try + { + Runtime.Incref(self.pyHandle); + using (PyObject pyself = new PyObject(self.pyHandle)) + using (PyObject pyvalue = new PyObject(Converter.ToPythonImplicit(value))) + pyself.SetAttr(propertyName, pyvalue); + } + finally + { + Runtime.PyGILState_Release(gs); + } + } + + public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, Object[] args) + { + // call the base constructor + obj.GetType().InvokeMember(origCtorName, + BindingFlags.InvokeMethod, + null, + obj, + args); + + List disposeList = new List(); + CLRObject self = null; + IntPtr gs = Runtime.PyGILState_Ensure(); + try + { + // create the python object + IntPtr type = TypeManager.GetTypeHandle(obj.GetType()); + self = new CLRObject(obj, type); + + // set __pyobj__ to self and deref the python object which will allow this + // object to be collected. + FieldInfo fi = obj.GetType().GetField("__pyobj__"); + fi.SetValue(obj, self); + + Runtime.Incref(self.pyHandle); + PyObject pyself = new PyObject(self.pyHandle); + disposeList.Add(pyself); + + Runtime.Incref(Runtime.PyNone); + PyObject pynone = new PyObject(Runtime.PyNone); + disposeList.Add(pynone); + + // call __init__ + PyObject init = pyself.GetAttr("__init__", pynone); + disposeList.Add(init); + if (init.Handle != Runtime.PyNone) + { + // if __init__ hasn't been overriden then it will be a managed object + ManagedType managedMethod = ManagedType.GetManagedObject(init.Handle); + if (null == managedMethod) + { + PyObject[] pyargs = new PyObject[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + pyargs[i] = new PyObject(Converter.ToPython(args[i], args[i].GetType())); + disposeList.Add(pyargs[i]); + } + + disposeList.Add(init.Invoke(pyargs)); + } + } + } + finally + { + foreach (PyObject x in disposeList) { + if (x != null) + x.Dispose(); + } + + // Decrement the python object's reference count. + // This doesn't actually destroy the object, it just sets the reference to this object + // to be a weak reference and it will be destroyed when the C# object is destroyed. + if (null != self) + Runtime.Decref(self.pyHandle); + + Runtime.PyGILState_Release(gs); + } + } + + public static void Finalize(IPythonDerivedType obj) + { + FieldInfo fi = obj.GetType().GetField("__pyobj__"); + CLRObject self = (CLRObject)fi.GetValue(obj); + + // If python's been terminated then just free the gchandle. + lock (Runtime.IsFinalizingLock) + { + if (0 == Runtime.Py_IsInitialized() || Runtime.IsFinalizing) + { + self.gcHandle.Free(); + return; + } + } + + // delete the python object in an asnyc task as we may not be able to acquire + // the GIL immediately and we don't want to block the GC thread. + var t = Task.Factory.StartNew(() => + { + lock (Runtime.IsFinalizingLock) + { + // If python's been terminated then just free the gchandle. + if (0 == Runtime.Py_IsInitialized() || Runtime.IsFinalizing) + { + self.gcHandle.Free(); + return; + } + + IntPtr gs = Runtime.PyGILState_Ensure(); + try + { + // the C# object is being destroyed which must mean there are no more + // references to the Python object as well so now we can dealloc the + // python object. + IntPtr dict = Marshal.ReadIntPtr(self.pyHandle, ObjectOffset.DictOffset(self.pyHandle)); + if (dict != IntPtr.Zero) + Runtime.Decref(dict); + Runtime.PyObject_GC_Del(self.pyHandle); + self.gcHandle.Free(); + } + finally + { + Runtime.PyGILState_Release(gs); + } + } + }); + } + } +} diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs index 164c37cb6..8744de417 100644 --- a/src/runtime/classmanager.cs +++ b/src/runtime/classmanager.cs @@ -105,7 +105,12 @@ private static ClassBase CreateClass(Type type) { impl = new ExceptionClassObject(type); } - else { + else if (null != type.GetField("__pyobj__")) { + impl = new ClassDerivedObject(type); + } + + else + { impl = new ClassObject(type); } @@ -345,7 +350,7 @@ private static ClassInfo GetClassInfo(Type type) { typeof(MethodInfo) ); - ob = new MethodObject(name, mlist); + ob = new MethodObject(type, name, mlist); ci.members[name] = ob; } diff --git a/src/runtime/classobject.cs b/src/runtime/classobject.cs index 5e79e8253..03f784ed7 100644 --- a/src/runtime/classobject.cs +++ b/src/runtime/classobject.cs @@ -223,7 +223,6 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) { // 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; @@ -234,13 +233,29 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) { free = true; } + // Get the args passed in. int i = Runtime.PyTuple_Size(args); - IntPtr real = Runtime.PyTuple_New(i + 1); + IntPtr defaultArgs = cls.indexer.GetDefaultArgs(args); + int numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs); + int temp = i + numOfDefaultArgs; + IntPtr real = Runtime.PyTuple_New(temp + 1); for (int n = 0; n < i; n++) { IntPtr item = Runtime.PyTuple_GetItem(args, n); Runtime.Incref(item); Runtime.PyTuple_SetItem(real, n, item); } + + // Add Default Args if needed + for (int n = 0; n < numOfDefaultArgs; n++) { + IntPtr item = Runtime.PyTuple_GetItem(defaultArgs, n); + Runtime.Incref(item); + Runtime.PyTuple_SetItem(real, n + i, item); + } + // no longer need defaultArgs + Runtime.Decref(defaultArgs); + i = temp; + + // Add value to argument list Runtime.Incref(v); Runtime.PyTuple_SetItem(real, i, v); diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs index c61f9523d..1de49aede 100644 --- a/src/runtime/clrobject.cs +++ b/src/runtime/clrobject.cs @@ -25,15 +25,15 @@ internal CLRObject(Object ob, IntPtr tp) : base() { int flags = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_flags); if ((flags & TypeFlags.Subclass) != 0) { - IntPtr dict = Marshal.ReadIntPtr(py, ObjectOffset.ob_dict); + IntPtr dict = Marshal.ReadIntPtr(py, ObjectOffset.DictOffset(tp)); if (dict == IntPtr.Zero) { dict = Runtime.PyDict_New(); - Marshal.WriteIntPtr(py, ObjectOffset.ob_dict, dict); + Marshal.WriteIntPtr(py, ObjectOffset.DictOffset(tp), dict); } } GCHandle gc = GCHandle.Alloc(this); - Marshal.WriteIntPtr(py, ObjectOffset.magic(), (IntPtr)gc); + Marshal.WriteIntPtr(py, ObjectOffset.magic(tp), (IntPtr)gc); this.tpHandle = tp; this.pyHandle = py; this.gcHandle = gc; diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 650a6178a..6bb2b0293 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -12,6 +12,7 @@ using System.Runtime.InteropServices; using System.Globalization; using System.Security; +using System.Collections; namespace Python.Runtime { @@ -28,23 +29,29 @@ private Converter() {} static NumberFormatInfo nfi; static Type objectType; static Type stringType; + static Type singleType; static Type doubleType; + static Type decimalType; + static Type int16Type; static Type int32Type; static Type int64Type; static Type flagsType; static Type boolType; - //static Type typeType; + static Type typeType; static Converter () { nfi = NumberFormatInfo.InvariantInfo; objectType = typeof(Object); stringType = typeof(String); + int16Type = typeof(Int16); int32Type = typeof(Int32); int64Type = typeof(Int64); + singleType = typeof(Single); doubleType = typeof(Double); + decimalType = typeof(Decimal); flagsType = typeof(FlagsAttribute); boolType = typeof(Boolean); - //typeType = typeof(Type); + typeType = typeof(Type); } @@ -72,6 +79,35 @@ internal static Type GetTypeByAlias(IntPtr op) { return null; } + internal static IntPtr GetPythonTypeByAlias(Type op) + { + if (op == stringType) { + return Runtime.PyUnicodeType; + } +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + else if ((op == int16Type) || + (op == int32Type) || + (op == int64Type)) { + return Runtime.PyIntType; + } +#endif + else if ((op == int16Type) || + (op == int32Type)) { + return Runtime.PyIntType; + } + else if (op == int64Type) { + return Runtime.PyLongType; + } + else if ((op == doubleType) || + (op == singleType)) { + return Runtime.PyFloatType; + } + else if (op == boolType) { + return Runtime.PyBoolType; + } + return IntPtr.Zero; + } + //==================================================================== // Return a Python object for the given native object, converting @@ -79,6 +115,10 @@ internal static Type GetTypeByAlias(IntPtr op) { // 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(T value) + { + return ToPython(value, typeof(T)); + } internal static IntPtr ToPython(Object value, Type type) { IntPtr result = IntPtr.Zero; @@ -91,6 +131,14 @@ internal static IntPtr ToPython(Object value, Type type) { return result; } + // it the type is a python subclass of a managed type then return the + // underying python object rather than construct a new wrapper object. + IPythonDerivedType pyderived = value as IPythonDerivedType; + if (null != pyderived) + { + return ClassDerivedObject.ToPython(pyderived); + } + // hmm - from Python, we almost never care what the declared // type is. we'd rather have the object bound to the actual // implementing class. @@ -165,6 +213,16 @@ internal static IntPtr ToPython(Object value, Type type) { return Runtime.PyLong_FromUnsignedLongLong((ulong)value); default: + if (value is IEnumerable) { + using (var resultlist = new PyList()) { + foreach (object o in (IEnumerable)value) { + using (var p = new PyObject(ToPython(o, o.GetType()))) + resultlist.Append(p); + } + Runtime.Incref(resultlist.Handle); + return resultlist.Handle; + } + } result = CLRObject.GetInstHandle(value, type); return result; } @@ -307,6 +365,57 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return false; } + // Conversion to 'Type' is done using the same mappings as above + // for objects. + + if (obType == typeType) + { + if (value == Runtime.PyStringType) + { + result = stringType; + return true; + } + + else if (value == Runtime.PyBoolType) + { + result = boolType; + return true; + } + + else if (value == Runtime.PyIntType) + { + result = int32Type; + return true; + } + + else if (value == Runtime.PyLongType) + { + result = int64Type; + return true; + } + + else if (value == Runtime.PyFloatType) + { + result = doubleType; + return true; + } + + else if (value == Runtime.PyListType || value == Runtime.PyTupleType) + { + result = typeof(object[]); + return true; + } + + if (setError) + { + Exceptions.SetError(Exceptions.TypeError, + "value cannot be converted to Type" + ); + } + + return false; + } + return ToPrimitive(value, obType, out result, setError); } @@ -335,6 +444,7 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, return true; case TypeCode.Int32: +#if !(PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) // Trickery to support 64-bit platforms. if (IntPtr.Size == 4) { op = Runtime.PyNumber_Int(value); @@ -357,8 +467,13 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, return true; } else { +#else + // When using Python3 always use the PyLong API + { +#endif op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero) { + Exceptions.Clear(); if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } @@ -381,6 +496,18 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, return true; case TypeCode.Byte: +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) + { + if (Runtime.PyBytes_Size(value) == 1) + { + op = Runtime.PyBytes_AS_STRING(value); + result = (byte)Marshal.ReadByte(op); + return true; + } + goto type_error; + } +#else if (Runtime.PyObject_TypeCheck(value, Runtime.PyStringType)) { if (Runtime.PyString_Size(value) == 1) { op = Runtime.PyString_AS_STRING(value); @@ -389,6 +516,7 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, } goto type_error; } +#endif op = Runtime.PyNumber_Int(value); if (op == IntPtr.Zero) { @@ -408,6 +536,16 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, return true; case TypeCode.SByte: +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { + if (Runtime.PyBytes_Size(value) == 1) { + op = Runtime.PyBytes_AS_STRING(value); + result = (byte)Marshal.ReadByte(op); + return true; + } + goto type_error; + } +#else if (Runtime.PyObject_TypeCheck(value, Runtime.PyStringType)) { if (Runtime.PyString_Size(value) == 1) { op = Runtime.PyString_AS_STRING(value); @@ -416,6 +554,7 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, } goto type_error; } +#endif op = Runtime.PyNumber_Int(value); if (op == IntPtr.Zero) { @@ -435,7 +574,16 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, return true; case TypeCode.Char: - +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { + if (Runtime.PyBytes_Size(value) == 1) { + op = Runtime.PyBytes_AS_STRING(value); + result = (byte)Marshal.ReadByte(op); + return true; + } + goto type_error; + } +#else if (Runtime.PyObject_TypeCheck(value, Runtime.PyStringType)) { if (Runtime.PyString_Size(value) == 1) { op = Runtime.PyString_AS_STRING(value); @@ -444,7 +592,7 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, } goto type_error; } - +#endif else if (Runtime.PyObject_TypeCheck(value, Runtime.PyUnicodeType)) { if (Runtime.PyUnicode_GetSize(value) == 1) { @@ -470,10 +618,10 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, goto type_error; } ival = Runtime.PyInt_AsLong(op); + Runtime.Decref(op); if (ival > Char.MaxValue || ival < Char.MinValue) { goto overflow; } - Runtime.Decref(op); result = (char)ival; return true; @@ -536,14 +684,16 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, goto type_error; } uint ui = (uint)Runtime.PyLong_AsUnsignedLong(op); - Runtime.Decref(op); + if (Exceptions.ErrorOccurred()) { + Runtime.Decref(op); goto overflow; } IntPtr check = Runtime.PyLong_FromUnsignedLong(ui); int err = Runtime.PyObject_Compare(check, op); Runtime.Decref(check); + Runtime.Decref(op); if (0 != err || Exceptions.ErrorOccurred()) { goto overflow; } @@ -576,7 +726,8 @@ static bool ToPrimitive(IntPtr value, Type obType, out Object result, } goto type_error; } - double dd = Runtime.PyFloat_AsDouble(value); + double dd = Runtime.PyFloat_AsDouble(op); + Runtime.Decref(op); if (dd > Single.MaxValue || dd < Single.MinValue) { goto overflow; } @@ -713,10 +864,13 @@ static bool ToEnum(IntPtr value, Type obType, out Object result, return false; } - - - } - + public static class ConverterExtension + { + public static PyObject ToPython(this object o) + { + return new PyObject(Converter.ToPython(o, o.GetType())); + } + } } diff --git a/src/runtime/delegateobject.cs b/src/runtime/delegateobject.cs index 839fb71e5..5a1ab9021 100644 --- a/src/runtime/delegateobject.cs +++ b/src/runtime/delegateobject.cs @@ -103,7 +103,36 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { //==================================================================== // Implements __cmp__ for reflected delegate types. //==================================================================== +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + public static new IntPtr tp_richcompare(IntPtr ob, IntPtr other, int op) { + if (op != Runtime.Py_EQ && op != Runtime.Py_NE) + { + Runtime.Incref(Runtime.PyNotImplemented); + return Runtime.PyNotImplemented; + } + + IntPtr pytrue = Runtime.PyTrue; + IntPtr pyfalse = Runtime.PyFalse; + + // swap true and false for NE + if (op != Runtime.Py_EQ) + { + pytrue = Runtime.PyFalse; + pyfalse = Runtime.PyTrue; + } + + Delegate d1 = GetTrueDelegate(ob); + Delegate d2 = GetTrueDelegate(other); + if (d1 == d2) + { + Runtime.Incref(pytrue); + return pytrue; + } + Runtime.Incref(pyfalse); + return pyfalse; + } +#else public static new int tp_compare(IntPtr ob, IntPtr other) { Delegate d1 = GetTrueDelegate(ob); Delegate d2 = GetTrueDelegate(other); @@ -112,7 +141,7 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { } return -1; } - +#endif } diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index f08217dac..fe6fdd3ff 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -31,7 +31,7 @@ internal class ExceptionClassObject : ClassObject { internal ExceptionClassObject(Type tp) : base(tp) { } -#if (PYTHON25 || PYTHON26 || PYTHON27) +#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) internal static Exception ToException(IntPtr ob) { CLRObject co = GetManagedObject(ob) as CLRObject; if (co == null) { @@ -114,7 +114,7 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key) return Runtime.PyObject_GenericGetAttr(ob, key); } -#endif // (PYTHON25 || PYTHON26 || PYTHON27) +#endif // (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) } /// @@ -136,7 +136,11 @@ private Exceptions() {} //=================================================================== internal static void Initialize() { +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + exceptions_module = Runtime.PyImport_ImportModule("builtins"); +#else exceptions_module = Runtime.PyImport_ImportModule("exceptions"); +#endif Exceptions.ErrorCheck(exceptions_module); warnings_module = Runtime.PyImport_ImportModule("warnings"); Exceptions.ErrorCheck(warnings_module); @@ -164,16 +168,19 @@ internal static void Initialize() { //=================================================================== 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); + if (0 != Runtime.Py_IsInitialized()) { + 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.PyObject_HasAttrString(warnings_module, "xx"); + Runtime.Decref(warnings_module); } - Runtime.Decref(exceptions_module); - Runtime.Decref(warnings_module); } /// @@ -565,15 +572,17 @@ internal static IntPtr RaiseTypeError(string message) { 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) +#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) public static IntPtr BaseException; #endif public static IntPtr Exception; public static IntPtr StopIteration; -#if (PYTHON25 || PYTHON26 || PYTHON27) +#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) public static IntPtr GeneratorExit; #endif +#if !(PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) public static IntPtr StandardError; +#endif public static IntPtr ArithmeticError; public static IntPtr LookupError; @@ -628,7 +637,7 @@ puplic static variables on the Exceptions class filled in from public static IntPtr SyntaxWarning; public static IntPtr RuntimeWarning; public static IntPtr FutureWarning; -#if (PYTHON25 || PYTHON26 || PYTHON27) +#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) public static IntPtr ImportWarning; public static IntPtr UnicodeWarning; //PyAPI_DATA(PyObject *) PyExc_BytesWarning; diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs index b0499bb0a..75ac67e59 100644 --- a/src/runtime/extensiontype.cs +++ b/src/runtime/extensiontype.cs @@ -40,7 +40,7 @@ public ExtensionType() : base() { IntPtr py = Runtime.PyType_GenericAlloc(tp, 0); GCHandle gc = GCHandle.Alloc(this); - Marshal.WriteIntPtr(py, ObjectOffset.magic(), (IntPtr)gc); + Marshal.WriteIntPtr(py, ObjectOffset.magic(tp), (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 diff --git a/src/runtime/generictype.cs b/src/runtime/generictype.cs index 082bc768c..e1ebc055c 100644 --- a/src/runtime/generictype.cs +++ b/src/runtime/generictype.cs @@ -44,57 +44,6 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { "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/src/runtime/genericutil.cs b/src/runtime/genericutil.cs index c3de0aa56..bb570e9ab 100644 --- a/src/runtime/genericutil.cs +++ b/src/runtime/genericutil.cs @@ -37,6 +37,9 @@ static GenericUtil() { //==================================================================== internal static void Register(Type t) { + if (null == t.Namespace || null == t.Name) + return; + Dictionary> nsmap = null; mapping.TryGetValue(t.Namespace, out nsmap); if (nsmap == null) { @@ -78,14 +81,33 @@ public static List GetGenericBaseNames(string ns) { // xxx //==================================================================== - public static List GenericsForType(Type t) { + public static Type GenericForType(Type t, int paramCount) + { + return GenericByName(t.Namespace, t.Name, paramCount); + } + + public static Type GenericByName(string ns, string name, int paramCount) + { + foreach (Type t in GenericsByName(ns, name)) + { + if (t.GetGenericArguments().Length == paramCount) + return t; + } + return null; + } + + public static List GenericsForType(Type t) + { + return GenericsByName(t.Namespace, t.Name); + } + + public static List GenericsByName(string ns, string basename) { Dictionary> nsmap = null; - mapping.TryGetValue(t.Namespace, out nsmap); + mapping.TryGetValue(ns, out nsmap); if (nsmap == null) { return null; } - string basename = t.Name; int tick = basename.IndexOf("`"); if (tick > -1) { basename = basename.Substring(0, tick); @@ -99,7 +121,7 @@ public static List GenericsForType(Type t) { List result = new List(); foreach (string name in names) { - string qname = t.Namespace + "." + name; + string qname = ns + "." + name; Type o = AssemblyManager.LookupType(qname); if (o != null) { result.Add(o); diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs index c736f0645..d8e62025a 100644 --- a/src/runtime/importhook.cs +++ b/src/runtime/importhook.cs @@ -23,29 +23,53 @@ internal class ImportHook { static CLRModule root; static MethodWrapper hook; +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + static IntPtr py_clr_module; + static IntPtr module_def; +#endif + //=================================================================== // 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(); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + IntPtr mod = Runtime.PyImport_ImportModule("builtins"); + py_import = Runtime.PyObject_GetAttrString(mod, "__import__"); +#else 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); +#endif + hook = new MethodWrapper(typeof(ImportHook), "__import__", "TernaryFunc"); + Runtime.PyObject_SetAttrString(mod, "__import__", hook.ptr); Runtime.Decref(hook.ptr); root = new CLRModule(); + +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + // create a python module with the same methods as the clr module-like object + module_def = ModuleDefOffset.AllocModuleDef("clr"); + py_clr_module = Runtime.PyModule_Create2(module_def, 3); + + // both dicts are borrowed references + IntPtr mod_dict = Runtime.PyModule_GetDict(py_clr_module); + IntPtr clr_dict = Runtime._PyObject_GetDictPtr(root.pyHandle); // PyObject** + clr_dict = (IntPtr)Marshal.PtrToStructure(clr_dict, typeof(IntPtr)); + + Runtime.PyDict_Update(mod_dict, clr_dict); + Runtime.PyDict_SetItemString(dict, "CLR", py_clr_module); + Runtime.PyDict_SetItemString(dict, "clr", py_clr_module); +#else 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); +#endif + } @@ -54,11 +78,74 @@ internal static void Initialize() { //=================================================================== internal static void Shutdown() { - Runtime.Decref(root.pyHandle); - Runtime.Decref(root.pyHandle); - Runtime.Decref(py_import); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + if (0 != Runtime.Py_IsInitialized()) { + Runtime.Decref(py_clr_module); + Runtime.Decref(root.pyHandle); + } + ModuleDefOffset.FreeModuleDef(module_def); +#else + if (0 != Runtime.Py_IsInitialized()) { + Runtime.Decref(root.pyHandle); + Runtime.Decref(root.pyHandle); + } +#endif + if (0 != Runtime.Py_IsInitialized()) { + Runtime.Decref(py_import); + } } + //=================================================================== + // Return the clr python module (new reference) + //=================================================================== + public static IntPtr GetCLRModule(IntPtr? fromList=null) { + root.InitializePreload(); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + // update the module dictionary with the contents of the root dictionary + root.LoadNames(); + IntPtr py_mod_dict = Runtime.PyModule_GetDict(py_clr_module); + IntPtr clr_dict = Runtime._PyObject_GetDictPtr(root.pyHandle); // PyObject** + clr_dict = (IntPtr)Marshal.PtrToStructure(clr_dict, typeof(IntPtr)); + Runtime.PyDict_Update(py_mod_dict, clr_dict); + + // find any items from the fromlist and get them from the root if they're not + // aleady in the module dictionary + if (fromList != null && fromList != IntPtr.Zero) { + if (Runtime.PyTuple_Check(fromList.GetValueOrDefault())) + { + Runtime.Incref(py_mod_dict); + using(PyDict mod_dict = new PyDict(py_mod_dict)) { + Runtime.Incref(fromList.GetValueOrDefault()); + using (PyTuple from = new PyTuple(fromList.GetValueOrDefault())) { + foreach (PyObject item in from) { + if (mod_dict.HasKey(item)) + continue; + + string s = item.AsManagedObject(typeof(string)) as string; + if (null == s) + continue; + + ManagedType attr = root.GetAttribute(s, true); + if (null == attr) + continue; + + Runtime.Incref(attr.pyHandle); + using (PyObject obj = new PyObject(attr.pyHandle)) { + mod_dict.SetItem(s, obj); + } + } + } + } + } + } + + Runtime.Incref(py_clr_module); + return py_clr_module; +#else + Runtime.Incref(root.pyHandle); + return root.pyHandle; +#endif + } //=================================================================== // The actual import hook that ties Python to the managed world. @@ -102,19 +189,31 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) { // 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; + IntPtr clr_module = GetCLRModule(fromList); + if (clr_module != IntPtr.Zero) { + IntPtr sys_modules = Runtime.PyImport_GetModuleDict(); + if (sys_modules != IntPtr.Zero) { + Runtime.PyDict_SetItemString(sys_modules, "clr", clr_module); + } + } + return clr_module; } if (mod_name == "CLR") { Exceptions.deprecation("The CLR module is deprecated. " + "Please use 'clr'."); - root.InitializePreload(); - Runtime.Incref(root.pyHandle); - return root.pyHandle; + IntPtr clr_module = GetCLRModule(fromList); + if (clr_module != IntPtr.Zero) { + IntPtr sys_modules = Runtime.PyImport_GetModuleDict(); + if (sys_modules != IntPtr.Zero) { + Runtime.PyDict_SetItemString(sys_modules, "clr", clr_module); + } + } + return clr_module; } string realname = mod_name; + string clr_prefix = null; if (mod_name.StartsWith("CLR.")) { + clr_prefix = "CLR."; // prepend when adding the module to sys.modules realname = mod_name.Substring(4); string msg = String.Format("Importing from the CLR.* namespace "+ "is deprecated. Please import '{0}' directly.", realname); @@ -174,6 +273,9 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) { Runtime.Incref(module); return module; } + if (clr_prefix != null) { + return GetCLRModule(fromList); + } module = Runtime.PyDict_GetItemString(modules, names[0]); Runtime.Incref(module); return module; @@ -209,9 +311,18 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) { if (CLRModule.preload) { tail.LoadNames(); } - Runtime.PyDict_SetItemString(modules, tail.moduleName, - tail.pyHandle - ); + + // Add the module to sys.modules + Runtime.PyDict_SetItemString(modules, + tail.moduleName, + tail.pyHandle); + + // If imported from CLR add CLR. to sys.modules as well + if (clr_prefix != null) { + Runtime.PyDict_SetItemString(modules, + clr_prefix + tail.moduleName, + tail.pyHandle); + } } ModuleObject mod = fromlist ? tail : head; diff --git a/src/runtime/indexer.cs b/src/runtime/indexer.cs index 8118dc339..0781a3a0a 100644 --- a/src/runtime/indexer.cs +++ b/src/runtime/indexer.cs @@ -62,7 +62,57 @@ internal void SetItem(IntPtr inst, IntPtr args) { SetterBinder.Invoke(inst, args, IntPtr.Zero); } - } + internal bool NeedsDefaultArgs(IntPtr args){ + int pynargs = Runtime.PyTuple_Size(args); + var methods = SetterBinder.GetMethods(); + if(methods.Length == 0) + return false; + + MethodBase mi = methods[0]; + ParameterInfo[] pi = mi.GetParameters(); + // need to subtract one for the value + int clrnargs = pi.Length - 1; + if (pynargs == clrnargs || pynargs > clrnargs) + return false; + + for (int v = pynargs; v < clrnargs; v++){ + if (pi[v].DefaultValue == DBNull.Value) + return false; + } + return true; + } + + /// + /// This will return default arguments a new instance of a tuple. The size + /// of the tuple will indicate the number of default arguments. + /// + /// This is pointing to the tuple args passed in + /// a new instance of the tuple containing the default args + internal IntPtr GetDefaultArgs(IntPtr args){ + + // if we don't need default args return empty tuple + if(!NeedsDefaultArgs(args)) + return Runtime.PyTuple_New(0); + int pynargs = Runtime.PyTuple_Size(args); + + // Get the default arg tuple + var methods = SetterBinder.GetMethods(); + MethodBase mi = methods[0]; + ParameterInfo[] pi = mi.GetParameters(); + int clrnargs = pi.Length - 1; + IntPtr defaultArgs = Runtime.PyTuple_New(clrnargs - pynargs); + for (int i = 0; i < (clrnargs - pynargs); i++) { + if (pi[i + pynargs].DefaultValue == DBNull.Value) + continue; + else{ + IntPtr arg = Converter.ToPython(pi[i + pynargs].DefaultValue, pi[i + pynargs].ParameterType); + Runtime.PyTuple_SetItem(defaultArgs, i, arg); + } + } + return defaultArgs; + } + } + } diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs index 9aad4c6e4..a1c541de4 100644 --- a/src/runtime/interop.cs +++ b/src/runtime/interop.cs @@ -12,6 +12,7 @@ using System.Collections.Specialized; using System.Runtime.InteropServices; using System.Reflection; +using System.Text; namespace Python.Runtime { @@ -77,11 +78,37 @@ static ObjectOffset() { ob_data = (n+3) * size; } - public static int magic() { + public static int magic(IntPtr ob) { +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) || + (Runtime.PyType_Check(ob) && Runtime.PyType_IsSubtype(ob, Exceptions.BaseException)))) + { + return ExceptionOffset.ob_data; + } +#endif return ob_data; } - public static int Size() { + public static int DictOffset(IntPtr ob) + { +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) || + (Runtime.PyType_Check(ob) && Runtime.PyType_IsSubtype(ob, Exceptions.BaseException)))) + { + return ExceptionOffset.ob_dict; + } +#endif + return ob_dict; + } + + public static int Size(IntPtr ob) { +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) || + (Runtime.PyType_Check(ob) && Runtime.PyType_IsSubtype(ob, Exceptions.BaseException)))) + { + return ExceptionOffset.Size(); + } +#endif #if (Py_DEBUG) return 6 * IntPtr.Size; #else @@ -95,206 +122,136 @@ public static int Size() { #endif public static int ob_refcnt; public static int ob_type; + private static int ob_dict; + private static int ob_data; + } + +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + internal class ExceptionOffset + { + static ExceptionOffset() + { + Type type = typeof(ExceptionOffset); + FieldInfo[] fi = type.GetFields(); + int size = IntPtr.Size; + for (int i = 0; i < fi.Length; i++) + { + fi[i].SetValue(null, (i * size) + ObjectOffset.ob_type + size); + } + } + + public static int Size() + { + return ob_data + IntPtr.Size; + } + + // PyException_HEAD + // (start after PyObject_HEAD) + public static int dict = 0; + public static int args = 0; + public static int traceback = 0; + public static int context = 0; + public static int cause = 0; + + // extra c# data public static int ob_dict; public static int ob_data; } +#endif - [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] - internal class TypeOffset { - - static TypeOffset() { - Type type = typeof(TypeOffset); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + internal class BytesOffset + { + static BytesOffset() + { + Type type = typeof(BytesOffset); FieldInfo[] fi = type.GetFields(); int size = IntPtr.Size; - for (int i = 0; i < fi.Length; i++) { + 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 { + /* 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 { + // 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; + // } + public static int ob_shash = 0; + public static int ob_sval = 0; /* start of data */ + + /* Invariants: + * ob_sval contains space for 'ob_size+1' elements. + * ob_sval[ob_size] == 0. + * ob_shash is the hash of the string or -1 if not computed yet. + */ + //} PyBytesObject; } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + internal class ModuleDefOffset + { + static ModuleDefOffset() + { + Type type = typeof(ModuleDefOffset); + FieldInfo[] fi = type.GetFields(); + int size = IntPtr.Size; + for (int i = 0; i < fi.Length; i++) + { + fi[i].SetValue(null, (i * size) + TypeOffset.ob_size); + } + } + + public static IntPtr AllocModuleDef(string modulename) { + byte[] ascii = Encoding.ASCII.GetBytes(modulename); + int size = name + ascii.Length + 1; + IntPtr ptr = Marshal.AllocHGlobal(size); + for (int i = 0; i <= m_free; i += IntPtr.Size) + Marshal.WriteIntPtr(ptr, i, IntPtr.Zero); + Marshal.Copy(ascii, 0, (IntPtr)(ptr + name), ascii.Length); + Marshal.WriteIntPtr(ptr, m_name, (IntPtr)(ptr + name)); + Marshal.WriteByte(ptr, name + ascii.Length, 0); + return ptr; + } + + public static void FreeModuleDef(IntPtr ptr) { + Marshal.FreeHGlobal(ptr); + } + + // typedef struct PyModuleDef{ + // typedef struct PyModuleDef_Base { + // starts after PyObject_HEAD (TypeOffset.ob_type + 1) + public static int m_init = 0; + public static int m_index = 0; + public static int m_copy = 0; + // } PyModuleDef_Base + public static int m_name = 0; + public static int m_doc = 0; + public static int m_size = 0; + public static int m_methods = 0; + public static int m_reload = 0; + public static int m_traverse = 0; + public static int m_clear = 0; + public static int m_free = 0; + // } PyModuleDef + + public static int name = 0; + } +#endif // PYTHON3 + /// /// TypeFlags(): The actual bit values for the Type Flags stored /// in a class. @@ -302,6 +259,8 @@ public static int magic() { /// to good use as PythonNet specific flags (Managed and Subclass) /// internal class TypeFlags { +#if (PYTHON23 || PYTHON24 || PYTHON25 || PYTHON26 || PYTHON27) + // these flags were removed in Python 3 public static int HaveGetCharBuffer = (1 << 0); public static int HaveSequenceIn = (1 << 1); public static int GC = 0; @@ -311,6 +270,7 @@ internal class TypeFlags { public static int HaveWeakRefs = (1 << 6); public static int HaveIter = (1 << 7); public static int HaveClass = (1 << 8); +#endif public static int HeapType = (1 << 9); public static int BaseType = (1 << 10); public static int Ready = (1 << 12); @@ -321,10 +281,10 @@ internal class TypeFlags { /* XXX Reusing reserved constants */ public static int Managed = (1 << 15); // PythonNet specific public static int Subclass = (1 << 16); // PythonNet specific -#if (PYTHON25 || PYTHON26 || PYTHON27) +#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) public static int HaveIndex = (1 << 17); #endif -#if (PYTHON26 || PYTHON27) +#if (PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) /* Objects support nb_index in PyNumberMethods */ public static int HaveVersionTag = (1 << 18); public static int ValidVersionTag = (1 << 19); @@ -341,7 +301,11 @@ internal class TypeFlags { public static int BaseExceptionSubclass = (1 << 30); public static int TypeSubclass = (1 << 31); #endif - public static int Default = (HaveGetCharBuffer | + +// Default flags for Python 2 +#if (PYTHON23 || PYTHON24 || PYTHON25 || PYTHON26 || PYTHON27) + public static int Default = ( + HaveGetCharBuffer | HaveSequenceIn | HaveInPlaceOps | HaveRichCompare | @@ -349,10 +313,19 @@ internal class TypeFlags { HaveIter | HaveClass | HaveStacklessExtension | -#if (PYTHON25 || PYTHON26 || PYTHON27) + #if (PYTHON25 || PYTHON26 || PYTHON27) HaveIndex | -#endif + #endif 0); +#endif + +// Default flags for Python 3 +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + public static int Default = ( + HaveStacklessExtension | + HaveVersionTag); +#endif + } @@ -410,7 +383,9 @@ static Interop() { pmap["nb_add"] = p["BinaryFunc"]; pmap["nb_subtract"] = p["BinaryFunc"]; pmap["nb_multiply"] = p["BinaryFunc"]; +#if !(PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) pmap["nb_divide"] = p["BinaryFunc"]; +#endif pmap["nb_remainder"] = p["BinaryFunc"]; pmap["nb_divmod"] = p["BinaryFunc"]; pmap["nb_power"] = p["TernaryFunc"]; @@ -433,7 +408,9 @@ static Interop() { pmap["nb_inplace_add"] = p["BinaryFunc"]; pmap["nb_inplace_subtract"] = p["BinaryFunc"]; pmap["nb_inplace_multiply"] = p["BinaryFunc"]; +#if !(PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) pmap["nb_inplace_divide"] = p["BinaryFunc"]; +#endif pmap["nb_inplace_remainder"] = p["BinaryFunc"]; pmap["nb_inplace_power"] = p["TernaryFunc"]; pmap["nb_inplace_lshift"] = p["BinaryFunc"]; @@ -445,7 +422,7 @@ static Interop() { pmap["nb_true_divide"] = p["BinaryFunc"]; pmap["nb_inplace_floor_divide"] = p["BinaryFunc"]; pmap["nb_inplace_true_divide"] = p["BinaryFunc"]; -#if (PYTHON25 || PYTHON26 || PYTHON27) +#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) pmap["nb_index"] = p["UnaryFunc"]; #endif @@ -468,16 +445,19 @@ static Interop() { 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); + internal static IntPtr GetThunk(MethodInfo method, string funcType = null) { + Type dt; + if (funcType != null) + dt = typeof(Interop).GetNestedType(funcType) as Type; + else + dt = GetPrototype(method.Name); + if (dt != null) { IntPtr tmp = Marshal.AllocHGlobal(IntPtr.Size); Delegate d = Delegate.CreateDelegate(dt, method); @@ -541,5 +521,4 @@ public Thunk(Delegate d) { fn = d; } } - } diff --git a/src/runtime/interop26.cs b/src/runtime/interop26.cs new file mode 100644 index 000000000..810e0e62f --- /dev/null +++ b/src/runtime/interop26.cs @@ -0,0 +1,153 @@ + +// Auto-generated by geninterop.py. +// DOT NOT MODIFIY BY HAND. +// ========================================================================== +// 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 (PYTHON26) +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Text; + +namespace Python.Runtime { + + [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; + } + + // Auto-generated from PyHeapTypeObject in Python.h + public static int ob_refcnt = 0; + public static int ob_type = 0; + public static int ob_size = 0; + public static int tp_name = 0; + public static int tp_basicsize = 0; + public static int tp_itemsize = 0; + 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; + public static int tp_as_number = 0; + public static int tp_as_sequence = 0; + public static int tp_as_mapping = 0; + 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; + public static int tp_as_buffer = 0; + public static int tp_flags = 0; + public static int tp_doc = 0; + public static int tp_traverse = 0; + public static int tp_clear = 0; + public static int tp_richcompare = 0; + public static int tp_weaklistoffset = 0; + public static int tp_iter = 0; + public static int tp_iternext = 0; + 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; + public static int tp_is_gc = 0; + public static int tp_bases = 0; + public static int tp_mro = 0; + public static int tp_cache = 0; + public static int tp_subclasses = 0; + public static int tp_weaklist = 0; + public static int tp_del = 0; + public static int tp_version_tag = 0; + 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; + 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; + 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; + public static int nb_index = 0; + public static int mp_length = 0; + public static int mp_subscript = 0; + public static int mp_ass_subscript = 0; + 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; + public static int sq_inplace_concat = 0; + public static int sq_inplace_repeat = 0; + public static int bf_getreadbuffer = 0; + public static int bf_getwritebuffer = 0; + public static int bf_getsegcount = 0; + public static int bf_getcharbuffer = 0; + public static int bf_getbuffer = 0; + public static int bf_releasebuffer = 0; + public static int name = 0; + public static int ht_slots = 0; + + /* here are optional user slots, followed by the members. */ + public static int members = 0; + } +} +#endif diff --git a/src/runtime/interop27.cs b/src/runtime/interop27.cs new file mode 100644 index 000000000..de4511806 --- /dev/null +++ b/src/runtime/interop27.cs @@ -0,0 +1,153 @@ + +// Auto-generated by geninterop.py. +// DOT NOT MODIFIY BY HAND. +// ========================================================================== +// 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 (PYTHON27) +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Text; + +namespace Python.Runtime { + + [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; + } + + // Auto-generated from PyHeapTypeObject in Python.h + public static int ob_refcnt = 0; + public static int ob_type = 0; + public static int ob_size = 0; + public static int tp_name = 0; + public static int tp_basicsize = 0; + public static int tp_itemsize = 0; + 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; + public static int tp_as_number = 0; + public static int tp_as_sequence = 0; + public static int tp_as_mapping = 0; + 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; + public static int tp_as_buffer = 0; + public static int tp_flags = 0; + public static int tp_doc = 0; + public static int tp_traverse = 0; + public static int tp_clear = 0; + public static int tp_richcompare = 0; + public static int tp_weaklistoffset = 0; + public static int tp_iter = 0; + public static int tp_iternext = 0; + 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; + public static int tp_is_gc = 0; + public static int tp_bases = 0; + public static int tp_mro = 0; + public static int tp_cache = 0; + public static int tp_subclasses = 0; + public static int tp_weaklist = 0; + public static int tp_del = 0; + public static int tp_version_tag = 0; + 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; + 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; + 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; + public static int nb_index = 0; + public static int mp_length = 0; + public static int mp_subscript = 0; + public static int mp_ass_subscript = 0; + 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; + public static int sq_inplace_concat = 0; + public static int sq_inplace_repeat = 0; + public static int bf_getreadbuffer = 0; + public static int bf_getwritebuffer = 0; + public static int bf_getsegcount = 0; + public static int bf_getcharbuffer = 0; + public static int bf_getbuffer = 0; + public static int bf_releasebuffer = 0; + public static int name = 0; + public static int ht_slots = 0; + + /* here are optional user slots, followed by the members. */ + public static int members = 0; + } +} +#endif diff --git a/src/runtime/interop32.cs b/src/runtime/interop32.cs new file mode 100644 index 000000000..711ef6323 --- /dev/null +++ b/src/runtime/interop32.cs @@ -0,0 +1,144 @@ + +// Auto-generated by geninterop.py. +// DOT NOT MODIFIY BY HAND. +// ========================================================================== +// 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 (PYTHON32) +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Text; + +namespace Python.Runtime { + + [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; + } + + // Auto-generated from PyHeapTypeObject in Python.h + public static int ob_refcnt = 0; + public static int ob_type = 0; + public static int ob_size = 0; + public static int tp_name = 0; + public static int tp_basicsize = 0; + public static int tp_itemsize = 0; + 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_reserved = 0; + public static int tp_repr = 0; + public static int tp_as_number = 0; + public static int tp_as_sequence = 0; + public static int tp_as_mapping = 0; + 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; + public static int tp_as_buffer = 0; + public static int tp_flags = 0; + public static int tp_doc = 0; + public static int tp_traverse = 0; + public static int tp_clear = 0; + public static int tp_richcompare = 0; + public static int tp_weaklistoffset = 0; + public static int tp_iter = 0; + public static int tp_iternext = 0; + 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; + public static int tp_is_gc = 0; + public static int tp_bases = 0; + public static int tp_mro = 0; + public static int tp_cache = 0; + public static int tp_subclasses = 0; + public static int tp_weaklist = 0; + public static int tp_del = 0; + public static int tp_version_tag = 0; + public static int nb_add = 0; + public static int nb_subtract = 0; + public static int nb_multiply = 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_bool = 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_int = 0; + public static int nb_reserved = 0; + public static int nb_float = 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_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; + 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; + public static int nb_index = 0; + public static int mp_length = 0; + public static int mp_subscript = 0; + public static int mp_ass_subscript = 0; + 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 was_sq_slice = 0; + public static int sq_ass_item = 0; + public static int was_sq_ass_slice = 0; + public static int sq_contains = 0; + public static int sq_inplace_concat = 0; + public static int sq_inplace_repeat = 0; + public static int bf_getbuffer = 0; + public static int bf_releasebuffer = 0; + public static int name = 0; + public static int ht_slots = 0; + + /* here are optional user slots, followed by the members. */ + public static int members = 0; + } +} +#endif diff --git a/src/runtime/interop33.cs b/src/runtime/interop33.cs new file mode 100644 index 000000000..47aaae96c --- /dev/null +++ b/src/runtime/interop33.cs @@ -0,0 +1,145 @@ + +// Auto-generated by geninterop.py. +// DOT NOT MODIFIY BY HAND. +// ========================================================================== +// 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; +using System.Text; + +namespace Python.Runtime { + + [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; + } + + // Auto-generated from PyHeapTypeObject in Python.h + public static int ob_refcnt = 0; + public static int ob_type = 0; + public static int ob_size = 0; + public static int tp_name = 0; + public static int tp_basicsize = 0; + public static int tp_itemsize = 0; + 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_reserved = 0; + public static int tp_repr = 0; + public static int tp_as_number = 0; + public static int tp_as_sequence = 0; + public static int tp_as_mapping = 0; + 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; + public static int tp_as_buffer = 0; + public static int tp_flags = 0; + public static int tp_doc = 0; + public static int tp_traverse = 0; + public static int tp_clear = 0; + public static int tp_richcompare = 0; + public static int tp_weaklistoffset = 0; + public static int tp_iter = 0; + public static int tp_iternext = 0; + 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; + public static int tp_is_gc = 0; + public static int tp_bases = 0; + public static int tp_mro = 0; + public static int tp_cache = 0; + public static int tp_subclasses = 0; + public static int tp_weaklist = 0; + public static int tp_del = 0; + public static int tp_version_tag = 0; + public static int nb_add = 0; + public static int nb_subtract = 0; + public static int nb_multiply = 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_bool = 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_int = 0; + public static int nb_reserved = 0; + public static int nb_float = 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_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; + 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; + public static int nb_index = 0; + public static int mp_length = 0; + public static int mp_subscript = 0; + public static int mp_ass_subscript = 0; + 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 was_sq_slice = 0; + public static int sq_ass_item = 0; + public static int was_sq_ass_slice = 0; + public static int sq_contains = 0; + public static int sq_inplace_concat = 0; + public static int sq_inplace_repeat = 0; + public static int bf_getbuffer = 0; + public static int bf_releasebuffer = 0; + public static int name = 0; + public static int ht_slots = 0; + public static int qualname = 0; + public static int ht_cached_keys = 0; + + /* here are optional user slots, followed by the members. */ + public static int members = 0; + } +} diff --git a/src/runtime/interop34.cs b/src/runtime/interop34.cs new file mode 100644 index 000000000..8b74930c7 --- /dev/null +++ b/src/runtime/interop34.cs @@ -0,0 +1,147 @@ + +// Auto-generated by geninterop.py. +// DOT NOT MODIFIY BY HAND. +// ========================================================================== +// 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 (PYTHON34) +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Text; + +namespace Python.Runtime { + + [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; + } + + // Auto-generated from PyHeapTypeObject in Python.h + public static int ob_refcnt = 0; + public static int ob_type = 0; + public static int ob_size = 0; + public static int tp_name = 0; + public static int tp_basicsize = 0; + public static int tp_itemsize = 0; + 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_reserved = 0; + public static int tp_repr = 0; + public static int tp_as_number = 0; + public static int tp_as_sequence = 0; + public static int tp_as_mapping = 0; + 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; + public static int tp_as_buffer = 0; + public static int tp_flags = 0; + public static int tp_doc = 0; + public static int tp_traverse = 0; + public static int tp_clear = 0; + public static int tp_richcompare = 0; + public static int tp_weaklistoffset = 0; + public static int tp_iter = 0; + public static int tp_iternext = 0; + 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; + public static int tp_is_gc = 0; + public static int tp_bases = 0; + public static int tp_mro = 0; + public static int tp_cache = 0; + public static int tp_subclasses = 0; + public static int tp_weaklist = 0; + public static int tp_del = 0; + public static int tp_version_tag = 0; + public static int tp_finalize = 0; + public static int nb_add = 0; + public static int nb_subtract = 0; + public static int nb_multiply = 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_bool = 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_int = 0; + public static int nb_reserved = 0; + public static int nb_float = 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_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; + 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; + public static int nb_index = 0; + public static int mp_length = 0; + public static int mp_subscript = 0; + public static int mp_ass_subscript = 0; + 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 was_sq_slice = 0; + public static int sq_ass_item = 0; + public static int was_sq_ass_slice = 0; + public static int sq_contains = 0; + public static int sq_inplace_concat = 0; + public static int sq_inplace_repeat = 0; + public static int bf_getbuffer = 0; + public static int bf_releasebuffer = 0; + public static int name = 0; + public static int ht_slots = 0; + public static int qualname = 0; + public static int ht_cached_keys = 0; + + /* here are optional user slots, followed by the members. */ + public static int members = 0; + } +} +#endif diff --git a/src/runtime/interop35.cs b/src/runtime/interop35.cs new file mode 100644 index 000000000..e573b9225 --- /dev/null +++ b/src/runtime/interop35.cs @@ -0,0 +1,152 @@ + +// Auto-generated by geninterop.py. +// DOT NOT MODIFIY BY HAND. +// ========================================================================== +// 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 (PYTHON35) +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Text; + +namespace Python.Runtime { + + [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; + } + + // Auto-generated from PyHeapTypeObject in Python.h + public static int ob_refcnt = 0; + public static int ob_type = 0; + public static int ob_size = 0; + public static int tp_name = 0; + public static int tp_basicsize = 0; + public static int tp_itemsize = 0; + 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_as_async = 0; + public static int tp_repr = 0; + public static int tp_as_number = 0; + public static int tp_as_sequence = 0; + public static int tp_as_mapping = 0; + 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; + public static int tp_as_buffer = 0; + public static int tp_flags = 0; + public static int tp_doc = 0; + public static int tp_traverse = 0; + public static int tp_clear = 0; + public static int tp_richcompare = 0; + public static int tp_weaklistoffset = 0; + public static int tp_iter = 0; + public static int tp_iternext = 0; + 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; + public static int tp_is_gc = 0; + public static int tp_bases = 0; + public static int tp_mro = 0; + public static int tp_cache = 0; + public static int tp_subclasses = 0; + public static int tp_weaklist = 0; + public static int tp_del = 0; + public static int tp_version_tag = 0; + public static int tp_finalize = 0; + public static int am_await = 0; + public static int am_aiter = 0; + public static int am_anext = 0; + public static int nb_add = 0; + public static int nb_subtract = 0; + public static int nb_multiply = 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_bool = 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_int = 0; + public static int nb_reserved = 0; + public static int nb_float = 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_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; + 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; + public static int nb_index = 0; + public static int nb_matrix_multiply = 0; + public static int nb_inplace_matrix_multiply = 0; + public static int mp_length = 0; + public static int mp_subscript = 0; + public static int mp_ass_subscript = 0; + 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 was_sq_slice = 0; + public static int sq_ass_item = 0; + public static int was_sq_ass_slice = 0; + public static int sq_contains = 0; + public static int sq_inplace_concat = 0; + public static int sq_inplace_repeat = 0; + public static int bf_getbuffer = 0; + public static int bf_releasebuffer = 0; + public static int name = 0; + public static int ht_slots = 0; + public static int qualname = 0; + public static int ht_cached_keys = 0; + + /* here are optional user slots, followed by the members. */ + public static int members = 0; + } +} +#endif diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs index 670bcd2b3..78e29c2b6 100644 --- a/src/runtime/managedtype.cs +++ b/src/runtime/managedtype.cs @@ -42,7 +42,7 @@ internal static ManagedType GetManagedObject(IntPtr ob) { if ((flags & TypeFlags.Managed) != 0) { IntPtr op = (tp == ob) ? Marshal.ReadIntPtr(tp, TypeOffset.magic()) : - Marshal.ReadIntPtr(ob, ObjectOffset.magic()); + Marshal.ReadIntPtr(ob, ObjectOffset.magic(ob)); GCHandle gc = (GCHandle)op; return (ManagedType)gc.Target; } diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs index 305437c84..25456a50d 100644 --- a/src/runtime/metatype.cs +++ b/src/runtime/metatype.cs @@ -46,7 +46,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { return Exceptions.RaiseTypeError("invalid argument list"); } - //IntPtr name = Runtime.PyTuple_GetItem(args, 0); + IntPtr name = Runtime.PyTuple_GetItem(args, 0); IntPtr bases = Runtime.PyTuple_GetItem(args, 1); IntPtr dict = Runtime.PyTuple_GetItem(args, 2); @@ -88,12 +88,19 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { ); } - // hack for now... fix for 1.0 - //return TypeManager.CreateSubType(args); - - - // right way + // If __assembly__ or __namespace__ are in the class dictionary then create + // a managed sub type. + // This creates a new managed type that can be used from .net to call back + // into python. + if (IntPtr.Zero != dict) { + Runtime.Incref(dict); + using (PyDict clsDict = new PyDict(dict)) { + if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__")) + return TypeManager.CreateSubType(name, base_type, dict); + } + } + // otherwise just create a basic type without reflecting back into the managed side. IntPtr func = Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_new); IntPtr type = NativeCall.Call_3(func, tp, args, kw); @@ -123,9 +130,6 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { IntPtr gc = Marshal.ReadIntPtr(base_type, TypeOffset.magic()); Marshal.WriteIntPtr(type, TypeOffset.magic(), gc); - //DebugUtil.DumpType(base_type); - //DebugUtil.DumpType(type); - return type; } @@ -256,10 +260,44 @@ public static void tp_dealloc(IntPtr tp) { return; } + static IntPtr DoInstanceCheck(IntPtr tp, IntPtr args, bool checkType) + { + ClassBase cb = GetManagedObject(tp) as ClassBase; + + if (cb == null) + return Runtime.PyFalse; + + using (PyList argsObj = new PyList(args)) + { + if (argsObj.Length() != 1) + return Exceptions.RaiseTypeError("Invalid parameter count"); + + PyObject arg = argsObj[0]; + PyObject otherType; + if (checkType) + otherType = arg; + else + otherType = arg.GetPythonType(); + + if (Runtime.PyObject_TYPE(otherType.Handle) != PyCLRMetaType) + return Runtime.PyFalse; + + ClassBase otherCb = GetManagedObject(otherType.Handle) as ClassBase; + if (otherCb == null) + return Runtime.PyFalse; + + return Converter.ToPython(cb.type.IsAssignableFrom(otherCb.type)); + } + } - - + public static IntPtr __instancecheck__(IntPtr tp, IntPtr args) + { + return DoInstanceCheck(tp, args, false); + } + + public static IntPtr __subclasscheck__(IntPtr tp, IntPtr args) + { + return DoInstanceCheck(tp, args, true); + } } - - } diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 80d3968fd..943f9da04 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -50,7 +50,10 @@ internal void AddMethod(MethodBase m) { //==================================================================== internal static MethodInfo MatchSignature(MethodInfo[] mi, Type[] tp) { - int count = tp.Length; + if (tp == null) { + return null; + } + int count = tp.Length; for (int i = 0; i < mi.Length; i++) { ParameterInfo[] pi = mi[i].GetParameters(); if (pi.Length != count) { @@ -73,8 +76,11 @@ internal static MethodInfo MatchSignature(MethodInfo[] mi, Type[] tp) { // return the MethodInfo that represents the matching closed generic. //==================================================================== - internal static MethodInfo MatchParameters(MethodInfo[] mi,Type[] tp) { - int count = tp.Length; + internal static MethodInfo MatchParameters(MethodInfo[] mi, Type[] tp) { + if (tp == null) { + return null; + } + int count = tp.Length; for (int i = 0; i < mi.Length; i++) { if (!mi[i].IsGenericMethodDefinition) { continue; @@ -89,51 +95,54 @@ internal static MethodInfo MatchParameters(MethodInfo[] mi,Type[] tp) { } - //==================================================================== - // 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; - } + //==================================================================== + // 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) + { + if ((genericTp == null) || (sigTp == null)) { + return null; + } + 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; + } //==================================================================== @@ -226,7 +235,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, int pynargs = Runtime.PyTuple_Size(args); object arg; bool isGeneric = false; - + ArrayList defaultArgList = null; if (info != null) { _methods = (MethodBase[])Array.CreateInstance( typeof(MethodBase), 1 @@ -236,9 +245,10 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, else { _methods = GetMethods(); } - + Type clrtype; 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; @@ -247,12 +257,20 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, int outs = 0; if (pynargs == clrnargs) { - match = true; + match = true; + } else if(pynargs < clrnargs){ + match = true; + defaultArgList = new ArrayList(); + for (int v = pynargs; v < clrnargs; v++) + { + if (pi[v].DefaultValue == DBNull.Value) + match = false; + else + defaultArgList.Add((object)pi[v].DefaultValue); + } } 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 + Attribute.IsDefined(pi[clrnargs-1], typeof(ParamArrayAttribute))) { + // This is a spam(params object[] egg) style method match = true; arrayStart = clrnargs - 1; } @@ -262,30 +280,95 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, 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); + if (n < pynargs) { + 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); + } + + // this logic below handles cases when multiple overloading methods + // are ambiguous, hence comparison between Python and CLR types + // is necessary + clrtype = null; + IntPtr pyoptype; + if (_methods.Length > 1) { + pyoptype = IntPtr.Zero; + pyoptype = Runtime.PyObject_Type(op); + Exceptions.Clear(); + if (pyoptype != IntPtr.Zero) { + clrtype = Converter.GetTypeByAlias(pyoptype); + } + Runtime.Decref(pyoptype); + } + + + if (clrtype != null) { + bool typematch = false; + if (pi[n].ParameterType != clrtype) { + IntPtr pytype = Converter.GetPythonTypeByAlias(pi[n].ParameterType); + pyoptype = Runtime.PyObject_Type(op); + Exceptions.Clear(); + if (pyoptype != IntPtr.Zero) { + if (pytype != pyoptype) { + typematch = false; + } + else { + typematch = true; + clrtype = pi[n].ParameterType; + } + } + if (!typematch) { + // this takes care of enum values + TypeCode argtypecode = Type.GetTypeCode(pi[n].ParameterType); + TypeCode paramtypecode = Type.GetTypeCode(clrtype); + if (argtypecode == paramtypecode) { + typematch = true; + clrtype = pi[n].ParameterType; + } + } + Runtime.Decref(pyoptype); + if (!typematch) { + margs = null; + break; + } + } + else { + typematch = true; + clrtype = pi[n].ParameterType; + } + } + else { + clrtype = pi[n].ParameterType; + } + + if (pi[n].IsOut || clrtype.IsByRef) + { + outs++; + } + + if (!Converter.ToManaged(op, clrtype, 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; } - else { - op = Runtime.PyTuple_GetItem(args, n); + else + { + if (defaultArgList != null) + margs[n] = defaultArgList[n - pynargs]; } - 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) { @@ -322,7 +405,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodInfo mi = MethodBinder.MatchParameters(methodinfo, types); return Bind(inst, args, kw, mi, null); } - return null; + return null; } internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) { diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs index 0459d36b2..64ab3edbe 100644 --- a/src/runtime/methodbinding.cs +++ b/src/runtime/methodbinding.cs @@ -9,6 +9,7 @@ using System; using System.Reflection; +using System.Collections.Generic; namespace Python.Runtime { @@ -23,14 +24,25 @@ internal class MethodBinding : ExtensionType { internal MethodInfo info; internal MethodObject m; internal IntPtr target; + internal IntPtr targetType; - public MethodBinding(MethodObject m, IntPtr target) : base() { + public MethodBinding(MethodObject m, IntPtr target, IntPtr targetType) : base() { Runtime.Incref(target); this.target = target; + + Runtime.Incref(targetType); + if (targetType == IntPtr.Zero) + targetType = Runtime.PyObject_Type(target); + this.targetType = targetType; + this.info = null; this.m = m; } + public MethodBinding(MethodObject m, IntPtr target) : this(m, target, IntPtr.Zero) + { + } + //==================================================================== // Implement binding of generic methods using the subscript syntax []. //==================================================================== @@ -114,25 +126,63 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { // 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. + List disposeList = new List(); + try + { + IntPtr target = self.target; + + if ((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; + } + target = Runtime.PyTuple_GetItem(args, 0); + Runtime.Incref(target); + disposeList.Add(target); + + args = Runtime.PyTuple_GetSlice(args, 1, len); + disposeList.Add(args); + } - 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; - } + // if the class is a IPythonDerivedClass and target is not the same as self.targetType + // (eg if calling the base class method) then call the original base class method instead + // of the target method. + IntPtr superType = IntPtr.Zero; + if (Runtime.PyObject_TYPE(target) != self.targetType) + { + CLRObject inst = CLRObject.GetManagedObject(target) as CLRObject; + if (inst != null && (inst.inst as IPythonDerivedType) != null) + { + ClassBase baseType = GetManagedObject(self.targetType) as ClassBase; + if (baseType != null) + { + string baseMethodName = "_" + baseType.type.Name + "__" + self.m.name; + IntPtr baseMethod = Runtime.PyObject_GetAttrString(target, baseMethodName); + if (baseMethod != IntPtr.Zero) + { + MethodBinding baseSelf = GetManagedObject(baseMethod) as MethodBinding; + if (baseSelf != null) + self = baseSelf; + Runtime.Decref(baseMethod); + } + else + { + Runtime.PyErr_Clear(); + } + } + } + } - return self.m.Invoke(self.target, args, kw, self.info); + return self.m.Invoke(target, args, kw, self.info); + } + finally + { + foreach (IntPtr ptr in disposeList) + Runtime.Decref(ptr); + } } @@ -184,6 +234,7 @@ public static IntPtr tp_repr(IntPtr ob) { public static new void tp_dealloc(IntPtr ob) { MethodBinding self = (MethodBinding)GetManagedObject(ob); Runtime.Decref(self.target); + Runtime.Decref(self.targetType); ExtensionType.FinalizeObject(self); } diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs index 15a5cd547..45e7de709 100644 --- a/src/runtime/methodobject.cs +++ b/src/runtime/methodobject.cs @@ -27,19 +27,21 @@ internal class MethodObject : ExtensionType { internal MethodBinder binder; internal bool is_static = false; internal IntPtr doc; + internal Type type; - public MethodObject(string name, MethodInfo[] info) : base() { - _MethodObject(name, info); + public MethodObject(Type type, string name, MethodInfo[] info) : base() { + _MethodObject(type, name, info); } - public MethodObject(string name, MethodInfo[] info, bool allow_threads) : base() + public MethodObject(Type type, string name, MethodInfo[] info, bool allow_threads) : base() { - _MethodObject(name, info); + _MethodObject(type, name, info); binder.allow_threads = allow_threads; } - private void _MethodObject(string name, MethodInfo[] info) + private void _MethodObject(Type type, string name, MethodInfo[] info) { + this.type = type; this.name = name; this.info = info; binder = new MethodBinder(); @@ -144,7 +146,7 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { if (ob == IntPtr.Zero) { if (self.unbound == null) { - self.unbound = new MethodBinding(self, IntPtr.Zero); + self.unbound = new MethodBinding(self, IntPtr.Zero, tp); } binding = self.unbound; Runtime.Incref(binding.pyHandle);; @@ -155,7 +157,22 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { return Exceptions.RaiseTypeError("invalid argument"); } - binding = new MethodBinding(self, ob); + // If the object this descriptor is being called with is a subclass of the type + // this descriptor was defined on then it will be because the base class method + // is being called via super(Derived, self).method(...). + // In which case create a MethodBinding bound to the base class. + CLRObject obj = GetManagedObject(ob) as CLRObject; + if (obj != null + && obj.inst.GetType() != self.type + && obj.inst is IPythonDerivedType + && self.type.IsAssignableFrom(obj.inst.GetType())) + { + ClassBase basecls = ClassManager.GetClass(self.type); + binding = new MethodBinding(self, ob, basecls.pyHandle); + return binding.pyHandle; + } + + binding = new MethodBinding(self, ob, tp); return binding.pyHandle; } diff --git a/src/runtime/methodwrapper.cs b/src/runtime/methodwrapper.cs index 04a49d592..764c77446 100644 --- a/src/runtime/methodwrapper.cs +++ b/src/runtime/methodwrapper.cs @@ -25,37 +25,22 @@ internal class MethodWrapper { public IntPtr mdef; public IntPtr ptr; - public MethodWrapper(Type type, string name) { + public MethodWrapper(Type type, string name, string funcType = null) { // 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); + IntPtr fp = Interop.GetThunk(type.GetMethod(name), funcType); // 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); + TypeManager.WriteMethodDef(mdef, name, fp, 0x0003); + ptr = Runtime.PyCFunction_NewEx(mdef, IntPtr.Zero, IntPtr.Zero); } public IntPtr Call(IntPtr args, IntPtr kw) { return Runtime.PyCFunction_Call(ptr, args, kw); } - - } - - -} - +} \ No newline at end of file diff --git a/src/runtime/modulefunctionobject.cs b/src/runtime/modulefunctionobject.cs index 5c9a4de21..2aa8c2306 100644 --- a/src/runtime/modulefunctionobject.cs +++ b/src/runtime/modulefunctionobject.cs @@ -19,8 +19,8 @@ namespace Python.Runtime internal class ModuleFunctionObject : MethodObject { - public ModuleFunctionObject(string name, MethodInfo[] info, bool allow_threads) - : base(name, info, allow_threads) + public ModuleFunctionObject(Type type, string name, MethodInfo[] info, bool allow_threads) + : base(type, name, info, allow_threads) { for (int i = 0; i < info.Length; i++) { diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs index c5735ca4a..d5dc2a562 100644 --- a/src/runtime/moduleobject.cs +++ b/src/runtime/moduleobject.cs @@ -37,14 +37,29 @@ public ModuleObject(string name) : base() { cache = new Dictionary(); _namespace = name; + // Use the filename from any of the assemblies just so there's something for + // anything that expects __file__ to be set. + string filename = "unknown"; + string docstring = "Namespace containing types from the following assemblies:\n\n"; + foreach (Assembly a in AssemblyManager.GetAssemblies(name)) { + filename = a.Location; + docstring += "- " + a.FullName + "\n"; + } + dict = Runtime.PyDict_New(); IntPtr pyname = Runtime.PyString_FromString(moduleName); + IntPtr pyfilename = Runtime.PyString_FromString(filename); + IntPtr pydocstring = Runtime.PyString_FromString(docstring); + IntPtr pycls = TypeManager.GetTypeHandle(this.GetType()); Runtime.PyDict_SetItemString(dict, "__name__", pyname); - Runtime.PyDict_SetItemString(dict, "__file__", Runtime.PyNone); - Runtime.PyDict_SetItemString(dict, "__doc__", Runtime.PyNone); + Runtime.PyDict_SetItemString(dict, "__file__", pyfilename); + Runtime.PyDict_SetItemString(dict, "__doc__", pydocstring); + Runtime.PyDict_SetItemString(dict, "__class__", pycls); Runtime.Decref(pyname); + Runtime.Decref(pyfilename); + Runtime.Decref(pydocstring); - Marshal.WriteIntPtr(this.pyHandle, ObjectOffset.ob_dict, dict); + Marshal.WriteIntPtr(this.pyHandle, ObjectOffset.DictOffset(this.pyHandle), dict); InitializeModuleMembers(); } @@ -216,7 +231,7 @@ internal void InitializeModuleMembers() string name = method.Name; MethodInfo[] mi = new MethodInfo[1]; mi[0] = method; - ModuleFunctionObject m = new ModuleFunctionObject(name, mi, allow_threads); + ModuleFunctionObject m = new ModuleFunctionObject(type, name, mi, allow_threads); StoreAttribute(name, m); } } @@ -394,11 +409,15 @@ public static Assembly AddReference(string name) { assembly = AssemblyManager.LoadAssembly(name); } + if (assembly == null) { + assembly = AssemblyManager.LoadAssemblyFullPath(name); + } if (assembly == null) { string msg = String.Format("Unable to find assembly '{0}'.", name); throw new System.IO.FileNotFoundException(msg); } + return assembly ; } @@ -425,6 +444,12 @@ public static String[] ListAssemblies(bool verbose) return names; } + [ModuleFunctionAttribute()] + public static int _AtExit() + { + return Runtime.AtExit(); + } + } } diff --git a/src/runtime/pydict.cs b/src/runtime/pydict.cs index cd85c7126..e2ceaa6d6 100644 --- a/src/runtime/pydict.cs +++ b/src/runtime/pydict.cs @@ -102,7 +102,8 @@ public bool HasKey(PyObject key) { /// public bool HasKey(string key) { - return HasKey(new PyString(key)); + using (PyString str = new PyString(key)) + return HasKey(str); } diff --git a/src/runtime/pyfloat.cs b/src/runtime/pyfloat.cs index 960892594..c6995887c 100644 --- a/src/runtime/pyfloat.cs +++ b/src/runtime/pyfloat.cs @@ -76,10 +76,11 @@ public PyFloat(double value) : base() { /// 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(); + using (PyString s = new PyString(value)) { + obj = Runtime.PyFloat_FromString(s.obj, IntPtr.Zero); + if (obj == IntPtr.Zero) { + throw new PythonException(); + } } } diff --git a/src/runtime/pyiter.cs b/src/runtime/pyiter.cs index 8d8ad44d7..c8599c2a3 100644 --- a/src/runtime/pyiter.cs +++ b/src/runtime/pyiter.cs @@ -21,42 +21,57 @@ 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. - /// + /// 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) {} + public PyIter(IntPtr ptr) : base(ptr) {} /// - /// PyIter Constructor - /// - /// - /// - /// Creates a Python iterator from an iterable. Like doing "iter(iterable)" in python. - /// + /// PyIter Constructor + /// + /// + /// + /// Creates a Python iterator from an iterable. Like doing "iter(iterable)" in python. + /// - public PyIter(PyObject iterable) : base() + public PyIter(PyObject iterable) : base() { obj = Runtime.PyObject_GetIter(iterable.obj); if (obj == IntPtr.Zero) throw new PythonException(); } + protected override void Dispose(bool disposing) + { + if (null != _current) + { + _current.Dispose(); + _current = null; + } + base.Dispose(disposing); + } + #region IEnumerator Members public bool MoveNext() { + // dispose of the previous object, if there was one + if (null != _current) + { + _current.Dispose(); + _current = null; + } + IntPtr next = Runtime.PyIter_Next(obj); if (next == IntPtr.Zero) - { - _current = null; //release reference return false; - } + _current = new PyObject(next); return true; } diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs index 099b9fdf4..d17e89a7a 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -8,6 +8,9 @@ // ========================================================================== using System; +using System.Dynamic; +using System.Linq.Expressions; +using System.Collections; namespace Python.Runtime { @@ -17,7 +20,7 @@ namespace Python.Runtime { /// http://www.python.org/doc/current/api/object.html for details. /// - public class PyObject : IDisposable { + public class PyObject : DynamicObject, IDisposable { protected internal IntPtr obj = IntPtr.Zero; private bool disposed = false; @@ -95,7 +98,7 @@ public static PyObject FromManagedObject(object ob) { public object AsManagedObject(Type t) { Object result; - if (!Converter.ToManaged(this.Handle, t, out result, false)) { + if (!Converter.ToManaged(this.obj, t, out result, false)) { throw new InvalidCastException("cannot convert object to target type"); } return result; @@ -115,19 +118,23 @@ public object AsManagedObject(Type t) { /// collection occurs. /// - public void Dispose() { + protected virtual void Dispose(bool disposing) { if (!disposed) { if (Runtime.Py_IsInitialized() > 0) { IntPtr gs = PythonEngine.AcquireLock(); Runtime.Decref(obj); - obj = IntPtr.Zero; + obj = IntPtr.Zero; PythonEngine.ReleaseLock(gs); } - GC.SuppressFinalize(this); disposed = true; } } + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + /// /// GetPythonType Method @@ -361,7 +368,9 @@ public virtual PyObject GetItem(PyObject key) { /// public virtual PyObject GetItem(string key) { - return GetItem(new PyString(key)); + using (PyString pyKey = new PyString(key)) { + return GetItem(pyKey); + } } @@ -376,8 +385,9 @@ public virtual PyObject GetItem(string key) { /// public virtual PyObject GetItem(int index) { - PyInt key = new PyInt(index); - return GetItem((PyObject)key); + using (PyInt key = new PyInt(index)) { + return GetItem((PyObject)key); + } } @@ -410,7 +420,9 @@ public virtual void SetItem(PyObject key, PyObject value) { /// public virtual void SetItem(string key, PyObject value) { - SetItem(new PyString(key), value); + using (PyString pyKey = new PyString(key)) { + SetItem(pyKey, value); + } } @@ -425,7 +437,9 @@ public virtual void SetItem(string key, PyObject value) { /// public virtual void SetItem(int index, PyObject value) { - SetItem(new PyInt(index), value); + using (PyInt pyindex = new PyInt(index)) { + SetItem(pyindex, value); + } } @@ -458,7 +472,9 @@ public virtual void DelItem(PyObject key) { /// public virtual void DelItem(string key) { - DelItem(new PyString(key)); + using (PyString pyKey = new PyString(key)) { + DelItem(pyKey); + } } @@ -473,7 +489,9 @@ public virtual void DelItem(string key) { /// public virtual void DelItem(int index) { - DelItem(new PyInt(index)); + using (PyInt pyindex = new PyInt(index)) { + DelItem(pyindex); + } } @@ -559,6 +577,21 @@ public PyObject GetIterator() { return new PyObject(r); } + /// + /// GetEnumerator Method + /// + /// + /// + /// Return a new PyIter object for the object. This allows any iterable + /// python object to be iterated over in C#. A PythonException will be + /// raised if the object is not iterable. + /// + + public IEnumerator GetEnumerator() + { + return new PyIter(this); + } + /// /// Invoke Method @@ -609,7 +642,7 @@ public PyObject Invoke(PyTuple args) { public PyObject Invoke(PyObject[] args, PyDict kw) { PyTuple t = new PyTuple(args); - IntPtr r = Runtime.PyObject_Call(obj, t.obj, kw.obj); + IntPtr r = Runtime.PyObject_Call(obj, t.obj, kw != null ? kw.obj : IntPtr.Zero); t.Dispose(); if (r == IntPtr.Zero) { throw new PythonException(); @@ -628,7 +661,7 @@ public PyObject Invoke(PyObject[] args, PyDict kw) { /// public PyObject Invoke(PyTuple args, PyDict kw) { - IntPtr r = Runtime.PyObject_Call(obj, args.obj, kw.obj); + IntPtr r = Runtime.PyObject_Call(obj, args.obj, kw != null ? kw.obj : IntPtr.Zero); if (r == IntPtr.Zero) { throw new PythonException(); } @@ -758,6 +791,22 @@ public bool IsCallable() { } + /// + /// IsIterable Method + /// + /// + /// + /// Returns true if the object is iterable object. This method + /// always succeeds. + /// + + public bool IsIterable() + { + return Runtime.PyIter_Check(obj); + } + + + /// /// IsTrue Method /// @@ -862,8 +911,242 @@ public override int GetHashCode() { return Runtime.PyObject_Hash(obj).ToInt32(); } + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + if (this.HasAttr(binder.Name)) + { + result = this.GetAttr(binder.Name); + return true; + } + else + return base.TryGetMember(binder, out result); + } + public override bool TrySetMember(SetMemberBinder binder, object value) + { + if (this.HasAttr(binder.Name)) + { + this.SetAttr(binder.Name, (PyObject)value); + return true; + } + else + return base.TrySetMember(binder, value); } + private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs) + { + int arg_count; + for (arg_count = 0; arg_count < inargs.Length && !(inargs[arg_count] is Py.KeywordArguments); ++arg_count); + IntPtr argtuple = Runtime.PyTuple_New(arg_count); + for (int i = 0; i < arg_count; i++) + { + IntPtr ptr; + if (inargs[i] is PyObject) + { + ptr = ((PyObject)inargs[i]).Handle; + Runtime.Incref(ptr); + } + else + { + ptr = Converter.ToPython(inargs[i], inargs[i].GetType()); + } + if (Runtime.PyTuple_SetItem(argtuple, i, ptr) < 0) + throw new PythonException(); + } + args = new PyTuple(argtuple); + kwargs = null; + for (int i = arg_count; i < inargs.Length; i++) + { + if (!(inargs[i] is Py.KeywordArguments)) + throw new ArgumentException("Keyword arguments must come after normal arguments."); + if (kwargs == null) + kwargs = (Py.KeywordArguments)inargs[i]; + else + kwargs.Update((Py.KeywordArguments)inargs[i]); + } + } + + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + if (this.HasAttr(binder.Name) && this.GetAttr(binder.Name).IsCallable()) + { + PyTuple pyargs = null; + PyDict kwargs = null; + try + { + GetArgs(args, out pyargs, out kwargs); + result = InvokeMethod(binder.Name, pyargs, kwargs); + } + finally + { + if (null != pyargs) + pyargs.Dispose(); + if (null != kwargs) + kwargs.Dispose(); + } + return true; + } + else + return base.TryInvokeMember(binder, args, out result); + } + public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) + { + if (this.IsCallable()) + { + PyTuple pyargs = null; + PyDict kwargs = null; + try + { + GetArgs(args, out pyargs, out kwargs); + result = Invoke(pyargs, kwargs); + } + finally + { + if (null != pyargs) + pyargs.Dispose(); + if (null != kwargs) + kwargs.Dispose(); + } + return true; + } + else + return base.TryInvoke(binder, args, out result); + } + + public override bool TryConvert(ConvertBinder binder, out object result) + { + return Converter.ToManaged(this.obj, binder.Type, out result, false); + } + + public override bool TryBinaryOperation(BinaryOperationBinder binder, Object arg, out Object result) { + IntPtr res; + if (!(arg is PyObject)) + arg = arg.ToPython(); + + switch (binder.Operation) + { + case ExpressionType.Add: + res = Runtime.PyNumber_Add(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.AddAssign: + res = Runtime.PyNumber_InPlaceAdd(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.Subtract: + res = Runtime.PyNumber_Subtract(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.SubtractAssign: + res = Runtime.PyNumber_InPlaceSubtract(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.Multiply: + res = Runtime.PyNumber_Multiply(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.MultiplyAssign: + res = Runtime.PyNumber_InPlaceMultiply(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.Divide: + res = Runtime.PyNumber_Divide(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.DivideAssign: + res = Runtime.PyNumber_InPlaceDivide(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.And: + res = Runtime.PyNumber_And(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.AndAssign: + res = Runtime.PyNumber_InPlaceAnd(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.ExclusiveOr: + res = Runtime.PyNumber_Xor(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.ExclusiveOrAssign: + res = Runtime.PyNumber_InPlaceXor(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.GreaterThan: + result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) > 0; + return true; + case ExpressionType.GreaterThanOrEqual: + result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) >= 0; + return true; + case ExpressionType.LeftShift: + res = Runtime.PyNumber_Lshift(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.LeftShiftAssign: + res = Runtime.PyNumber_InPlaceLshift(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.LessThan: + result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) < 0; + return true; + case ExpressionType.LessThanOrEqual: + result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) <= 0; + return true; + case ExpressionType.Modulo: + res = Runtime.PyNumber_Remainder(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.ModuloAssign: + res = Runtime.PyNumber_InPlaceRemainder(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.NotEqual: + result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) != 0; + return true; + case ExpressionType.Or: + res = Runtime.PyNumber_Or(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.OrAssign: + res = Runtime.PyNumber_InPlaceOr(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.Power: + res = Runtime.PyNumber_Power(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.RightShift: + res = Runtime.PyNumber_Rshift(this.obj, ((PyObject)arg).obj); + break; + case ExpressionType.RightShiftAssign: + res = Runtime.PyNumber_InPlaceRshift(this.obj, ((PyObject)arg).obj); + break; + default: + result = null; + return false; + } + result = new PyObject(res); + return true; + } + + public override bool TryUnaryOperation(UnaryOperationBinder binder, out Object result) + { + int r; + IntPtr res; + switch (binder.Operation) + { + case ExpressionType.Negate: + res = Runtime.PyNumber_Negative(this.obj); + break; + case ExpressionType.UnaryPlus: + res = Runtime.PyNumber_Positive(this.obj); + break; + case ExpressionType.OnesComplement: + res = Runtime.PyNumber_Invert(this.obj); + break; + case ExpressionType.Not: + r = Runtime.PyObject_Not(this.obj); + result = r == 1; + return r != -1; + case ExpressionType.IsFalse: + r = Runtime.PyObject_IsTrue(this.obj); + result = r == 0; + return r != -1; + case ExpressionType.IsTrue: + r = Runtime.PyObject_IsTrue(this.obj); + result = r == 1; + return r != -1; + case ExpressionType.Decrement: + case ExpressionType.Increment: + default: + result = null; + return false; + } + result = new PyObject(res); + return true; + } + } } diff --git a/src/runtime/pysequence.cs b/src/runtime/pysequence.cs index 9b41c308b..855cad72e 100644 --- a/src/runtime/pysequence.cs +++ b/src/runtime/pysequence.cs @@ -158,15 +158,6 @@ public PyObject Repeat(int count) { } return new PyObject(op); } - - #region IEnumerable Members - - public IEnumerator GetEnumerator() - { - return new PyIter(this); - } - - #endregion } } diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 07326185f..0cb99bffe 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -8,7 +8,9 @@ // ========================================================================== using System; +using System.IO; using System.Threading; +using System.Reflection; namespace Python.Runtime { @@ -64,6 +66,22 @@ public static string PythonHome { } } + public static string PythonPath { + get + { + string result = Runtime.Py_GetPath(); + if (result == null) + { + return ""; + } + return result; + } + set + { + Runtime.Py_SetPath(value); + } + } + public static string Version { get { return Runtime.Py_GetVersion(); @@ -117,55 +135,122 @@ public static void Initialize() { Runtime.Initialize(); initialized = true; Exceptions.Clear(); + + // register the atexit callback (this doesn't use Py_AtExit as the C atexit + // callbacks are called after python is fully finalized but the python ones + // are called while the python engine is still running). + string code = + "import atexit, clr\n" + + "atexit.register(clr._AtExit)\n"; + PyObject r = PythonEngine.RunString(code); + if (r != null) + r.Dispose(); + + // Load the clr.py resource into the clr module + IntPtr clr = Python.Runtime.ImportHook.GetCLRModule(); + IntPtr clr_dict = Runtime.PyModule_GetDict(clr); + + PyDict locals = new PyDict(); + try + { + IntPtr module = Runtime.PyImport_AddModule("clr._extras"); + IntPtr module_globals = Runtime.PyModule_GetDict(module); + IntPtr builtins = Runtime.PyEval_GetBuiltins(); + Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins); + + var assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = assembly.GetManifestResourceStream("clr.py")) + using (StreamReader reader = new StreamReader(stream)) + { + // add the contents of clr.py to the module + string clr_py = reader.ReadToEnd(); + PyObject result = RunString(clr_py, module_globals, locals.Handle); + if (null == result) + throw new PythonException(); + result.Dispose(); + } + + // add the imported module to the clr module, and copy the API functions + // and decorators into the main clr module. + Runtime.PyDict_SetItemString(clr_dict, "_extras", module); + foreach (PyObject key in locals.Keys()) + { + if (!key.ToString().StartsWith("_")){ + PyObject value = locals[key]; + Runtime.PyDict_SetItem(clr_dict, key.Handle, value.Handle); + value.Dispose(); + } + key.Dispose(); + } + } + finally + { + locals.Dispose(); + } } } - //==================================================================== // A helper to perform initialization from the context of an active // CPython interpreter process - this bootstraps the managed runtime // when it is imported by the CLR extension module. //==================================================================== - +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + public static IntPtr InitExt() { +#else 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(); +#endif + try + { + Initialize(); + + // Trickery - when the import hook is installed into an already + // running Python, the standard import machinery is still in + // control for the duration of the import that caused bootstrap. + // + // That is problematic because the std machinery tries to get + // sub-names directly from the module __dict__ rather than going + // through our module object's getattr hook. This workaround is + // evil ;) We essentially climb up the stack looking for the + // import that caused the bootstrap to happen, then re-execute + // the import explicitly after our hook has been installed. By + // doing this, the original outer import should work correctly. + // + // Note that this is only needed during the execution of the + // first import that installs the CLR import hook. This hack + // still doesn't work if you use the interactive interpreter, + // since there is no line info to get the import line ;( + + string code = + + "import traceback\n" + + "for item in traceback.extract_stack():\n" + + " line = item[3]\n" + + " if line is not None:\n" + + " if line.startswith('import CLR') or \\\n" + + " line.startswith('import clr') or \\\n" + + " line.startswith('from clr') or \\\n" + + " line.startswith('from CLR'):\n" + + " exec(line)\n" + + " break\n"; + + PyObject r = PythonEngine.RunString(code); + if (r != null) { + r.Dispose(); + } + } + catch (PythonException e) + { + e.Restore(); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + return IntPtr.Zero; +#endif } - } +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + return Python.Runtime.ImportHook.GetCLRModule(); +#endif + } /// /// Shutdown Method @@ -347,9 +432,70 @@ public static PyObject RunString(string code) { return new PyObject(result); } + public static PyObject RunString(string code, IntPtr globals, IntPtr locals) + { + IntPtr flag = (IntPtr)257; /* Py_file_input */ + IntPtr result = Runtime.PyRun_String(code, flag, globals, locals); + if (result == IntPtr.Zero) { + return null; + } + return new PyObject(result); + } + } + public static class Py + { + public static GILState GIL() + { + if (!PythonEngine.IsInitialized) + PythonEngine.Initialize(); - } + return new GILState(); + } + public class GILState : IDisposable + { + private IntPtr state; + internal GILState() + { + state = PythonEngine.AcquireLock(); + } + public void Dispose() + { + PythonEngine.ReleaseLock(state); + GC.SuppressFinalize(this); + } + ~GILState() + { + Dispose(); + } + } + + public class KeywordArguments : PyDict { } + + public static KeywordArguments kw(params object[] kv) + { + var dict = new KeywordArguments(); + if (kv.Length % 2 != 0) + throw new ArgumentException("Must have an equal number of keys and values"); + for (int i = 0; i < kv.Length; i += 2) + { + IntPtr value; + if (kv[i + 1] is PyObject) + value = ((PyObject)kv[i + 1]).Handle; + else + value = Converter.ToPython(kv[i + 1], kv[i + 1].GetType()); + if (Runtime.PyDict_SetItemString(dict.Handle, (string)kv[i], value) != 0) + throw new ArgumentException(string.Format("Cannot add key '{0}' to dictionary.", (string)kv[i])); + if (!(kv[i + 1] is PyObject)) + Runtime.Decref(value); + } + return dict; + } + public static PyObject Import(string name) + { + return PythonEngine.ImportModule(name); + } + } } diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs index 592e9ea37..552f004eb 100644 --- a/src/runtime/pythonexception.cs +++ b/src/runtime/pythonexception.cs @@ -34,14 +34,29 @@ public PythonException() : base() Runtime.Incref(_pyTB); if ((_pyType != IntPtr.Zero) && (_pyValue != IntPtr.Zero)) { - string type = new PyObject(_pyType).GetAttr("__name__").ToString(); - string message = Runtime.GetManagedString(_pyValue); + string type; + string message; + Runtime.Incref(_pyType); + using (PyObject pyType = new PyObject(_pyType)) + using (PyObject pyTypeName = pyType.GetAttr("__name__")) + { + type = pyTypeName.ToString(); + } + + Runtime.Incref(_pyValue); + using (PyObject pyValue = new PyObject(_pyValue)) + { + message = pyValue.ToString(); + } _message = type + " : " + message; } if (_pyTB != IntPtr.Zero) { PyObject tb_module = PythonEngine.ImportModule("traceback"); - _tb = tb_module.InvokeMethod("format_tb", new PyObject(_pyTB)).ToString(); + Runtime.Incref(_pyTB); + using (PyObject pyTB = new PyObject(_pyTB)) { + _tb = tb_module.InvokeMethod("format_tb", pyTB).ToString(); + } } PythonEngine.ReleaseLock(gs); } @@ -53,6 +68,18 @@ public PythonException() : base() Dispose(); } + /// + /// Restores python error. + /// + public void Restore() + { + IntPtr gs = PythonEngine.AcquireLock(); + Runtime.PyErr_Restore(_pyType, _pyValue, _pyTB); + _pyType = IntPtr.Zero; + _pyValue = IntPtr.Zero; + _pyTB = IntPtr.Zero; + PythonEngine.ReleaseLock(gs); + } /// /// PyType Property @@ -80,6 +107,18 @@ public IntPtr PyValue get { return _pyValue; } } + /// + /// PyTB Property + /// + /// + /// + /// Returns the TraceBack as a Python object. + /// + + public IntPtr PyTB { + get { return _pyTB; } + } + /// /// Message Property /// diff --git a/src/runtime/resources/clr.py b/src/runtime/resources/clr.py new file mode 100644 index 000000000..1fbd272b5 --- /dev/null +++ b/src/runtime/resources/clr.py @@ -0,0 +1,83 @@ +""" +Code in this module gets loaded into the main clr module. +""" + +class clrproperty(object): + """ + Property decorator for exposing python properties to .NET. + The property type must be specified as the only argument to clrproperty. + + e.g.:: + + class X(object): + @clrproperty(string) + def test(self): + return "x" + + Properties decorated this way can be called from .NET, e.g.:: + + dynamic x = getX(); // get an instance of X declared in Python + string z = x.test; // calls into python and returns "x" + """ + + def __init__(self, type_, fget=None, fset=None): + self.__name__ = getattr(fget, "__name__", None) + self._clr_property_type_ = type_ + self.fget = fget + self.fset = fset + + def __call__(self, fget): + return self.__class__(self._clr_property_type_, + fget=fget, + fset=self.fset) + + def setter(self, fset): + self.fset = fset + return self + + def getter(self, fget): + self.fget = fget + return self + + def __get__(self, instance, owner): + return self.fget.__get__(instance, owner)() + + def __set__(self, instance, value): + if not self.fset: + raise AttributeError("%s is read-only" % self.__name__) + return self.fset.__get__(instance, None)(value) + + +class clrmethod(object): + """ + Method decorator for exposing python methods to .NET. + The argument and return types must be specified as arguments to clrmethod. + + e.g.:: + + class X(object): + @clrmethod(int, [str]) + def test(self, x): + return len(x) + + Methods decorated this way can be called from .NET, e.g.:: + + dynamic x = getX(); // get an instance of X declared in Python + int z = x.test("hello"); // calls into python and returns len("hello") + """ + + def __init__(self, return_type, arg_types, clrname=None, func=None): + self.__name__ = getattr(func, "__name__", None) + self._clr_return_type_ = return_type + self._clr_arg_types_ = arg_types + self._clr_method_name_ = clrname or self.__name__ + self.__func = func + + def __call__(self, func): + return self.__class__(self._clr_return_type_, + self._clr_arg_types_, + clrname=self._clr_method_name_, + func=func) + + def __get__(self, instance, owner): + return self.__func.__get__(instance, owner) diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 19aa2ca2d..260d968d8 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -15,10 +15,86 @@ using Mono.Unix; #endif +#if (UCS2 && (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35)) +using System.Text; +#endif + namespace Python.Runtime { [SuppressUnmanagedCodeSecurityAttribute()] + static class NativeMethods + { +#if (MONO_LINUX || MONO_OSX) + static public IntPtr LoadLibrary(string fileName) { + return dlopen(fileName, RTLD_NOW | RTLD_SHARED); + } + + static public void FreeLibrary(IntPtr handle) { + dlclose(handle); + } + + static public IntPtr GetProcAddress(IntPtr dllHandle, string name) { + // look in the exe if dllHandle is NULL + if (IntPtr.Zero == dllHandle) + dllHandle = RTLD_DEFAULT; + + // clear previous errors if any + dlerror(); + var res = dlsym(dllHandle, name); + var errPtr = dlerror(); + if (errPtr != IntPtr.Zero) { + throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr)); + } + return res; + } + +#if (MONO_OSX) + static int RTLD_NOW = 0x2; + static int RTLD_SHARED = 0x20; + static IntPtr RTLD_DEFAULT = new IntPtr(-2); + + [DllImport("__Internal")] + private static extern IntPtr dlopen(String fileName, int flags); + + [DllImport("__Internal")] + private static extern IntPtr dlsym(IntPtr handle, String symbol); + + [DllImport("__Internal")] + private static extern int dlclose(IntPtr handle); + + [DllImport("__Internal")] + private static extern IntPtr dlerror(); +#else + static int RTLD_NOW = 0x2; + static int RTLD_SHARED = 0x20; + static IntPtr RTLD_DEFAULT = IntPtr.Zero; + + [DllImport("libdl.so")] + private static extern IntPtr dlopen(String fileName, int flags); + + [DllImport("libdl.so")] + private static extern IntPtr dlsym(IntPtr handle, String symbol); + + [DllImport("libdl.so")] + private static extern int dlclose(IntPtr handle); + + [DllImport("libdl.so")] + private static extern IntPtr dlerror(); +#endif + +#else + [DllImport("kernel32.dll")] + public static extern IntPtr LoadLibrary(string dllToLoad); + + [DllImport("kernel32.dll")] + public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); + + [DllImport("kernel32.dll")] + public static extern bool FreeLibrary(IntPtr hModule); +#endif + } + public class Runtime { /// @@ -37,34 +113,114 @@ public class Runtime { #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 +#if (PYTHON32) + public const string pyversion = "3.2"; + public const int pyversionnumber = 32; +#endif +#if (PYTHON33) + public const string pyversion = "3.3"; + public const int pyversionnumber = 33; +#endif +#if (PYTHON34) + public const string pyversion = "3.4"; + public const int pyversionnumber = 34; +#endif +#if (PYTHON35) + public const string pyversion = "3.5"; + public const int pyversionnumber = 35; +#endif +#if ! (PYTHON23 || PYTHON24 || PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) +#error You must define one of PYTHON23 to PYTHON35 +#endif + +#if (PYTHON23) + internal const string dllBase = "python23"; +#endif +#if (PYTHON24) + internal const string dllBase = "python24"; +#endif +#if (PYTHON25) + internal const string dllBase = "python25"; +#endif +#if (PYTHON26) + internal const string dllBase = "python26"; +#endif +#if (PYTHON27) + internal const string dllBase = "python27"; +#endif +#if (MONO_LINUX || MONO_OSX) +#if (PYTHON32) + internal const string dllBase = "python3.2"; +#endif +#if (PYTHON33) + internal const string dllBase = "python3.3"; +#endif +#if (PYTHON34) + internal const string dllBase = "python3.4"; +#endif +#if (PYTHON35) + internal const string dllBase = "python3.5"; +#endif +#else +#if (PYTHON32) + internal const string dllBase = "python32"; +#endif +#if (PYTHON33) + internal const string dllBase = "python33"; +#endif +#if (PYTHON34) + internal const string dllBase = "python34"; +#endif +#if (PYTHON35) + internal const string dllBase = "python35"; +#endif #endif +#if (PYTHON_WITH_PYDEBUG) + internal const string dllWithPyDebug = "d"; +#else + internal const string dllWithPyDebug = ""; +#endif +#if (PYTHON_WITH_PYMALLOC) + internal const string dllWithPyMalloc = "m"; +#else + internal const string dllWithPyMalloc = ""; +#endif +#if (PYTHON_WITH_WIDE_UNICODE) + internal const string dllWithWideUnicode = "u"; +#else + internal const string dllWithWideUnicode = ""; +#endif + +#if (PYTHON_WITHOUT_ENABLE_SHARED) + public const string dll = "__Internal"; +#else + public const string dll = dllBase + dllWithPyDebug + dllWithPyMalloc + dllWithWideUnicode; +#endif + + // set to true when python is finalizing + internal static Object IsFinalizingLock = new Object(); + internal static bool IsFinalizing = false; + internal static bool wrap_exceptions; internal static bool is32bit; @@ -72,19 +228,27 @@ public class Runtime { /// Intitialize the runtime... /// internal static void Initialize() { - + is32bit = IntPtr.Size == 4; - if (0 == Runtime.Py_IsInitialized()) { + if (0 == Runtime.Py_IsInitialized()) + { Runtime.Py_Initialize(); } - // make sure threads are initialized even if python was initialized already - Runtime.PyEval_InitThreads(); + if (0 == Runtime.PyEval_ThreadsInitialized()) + { + Runtime.PyEval_InitThreads(); + } +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + IntPtr op = Runtime.PyImport_ImportModule("builtins"); + IntPtr dict = Runtime.PyObject_GetAttrString(op, "__dict__"); + PyNotImplemented = Runtime.PyObject_GetAttrString(op, "NotImplemented"); +#else IntPtr dict = Runtime.PyImport_GetModuleDict(); IntPtr op = Runtime.PyDict_GetItemString(dict, "__builtin__"); - +#endif PyBaseObjectType = Runtime.PyObject_GetAttrString(op, "object"); PyModuleType = Runtime.PyObject_Type(op); @@ -100,6 +264,11 @@ internal static void Initialize() { PyMethodType = Runtime.PyObject_Type(op); Runtime.Decref(op); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + Runtime.Decref(dict); + Runtime.Decref(op); +#endif + op = Runtime.PyString_FromString("string"); PyStringType = Runtime.PyObject_Type(op); Runtime.Decref(op); @@ -108,6 +277,12 @@ internal static void Initialize() { PyUnicodeType = Runtime.PyObject_Type(op); Runtime.Decref(op); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + op = Runtime.PyBytes_FromString("bytes"); + PyBytesType = Runtime.PyObject_Type(op); + Runtime.Decref(op); +#endif + op = Runtime.PyTuple_New(0); PyTupleType = Runtime.PyObject_Type(op); Runtime.Decref(op); @@ -132,8 +307,13 @@ internal static void Initialize() { PyFloatType = Runtime.PyObject_Type(op); Runtime.Decref(op); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + PyClassType = IntPtr.Zero; + PyInstanceType = IntPtr.Zero; +#else 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); @@ -144,14 +324,29 @@ internal static void Initialize() { Runtime.Decref(i); Runtime.Decref(c); Runtime.Decref(d); +#endif Error = new IntPtr(-1); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + IntPtr dll = IntPtr.Zero; + if ("__Internal" != Runtime.dll) { + NativeMethods.LoadLibrary(Runtime.dll); + } + _PyObject_NextNotImplemented = NativeMethods.GetProcAddress(dll, "_PyObject_NextNotImplemented"); +#if !(MONO_LINUX || MONO_OSX) + if (IntPtr.Zero != dll) { + NativeMethods.FreeLibrary(dll); + } +#endif +#endif + + // 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) +#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) wrap_exceptions = false; #else IntPtr m = PyImport_ImportModule("exceptions"); @@ -188,6 +383,14 @@ internal static void Shutdown() { Py_Finalize(); } + // called *without* the GIL aquired by clr._AtExit + internal static int AtExit() { + lock (IsFinalizingLock) { + IsFinalizing = true; + } + return 0; + } + internal static IntPtr Py_single_input = (IntPtr)256; internal static IntPtr Py_file_input = (IntPtr)257; internal static IntPtr Py_eval_input = (IntPtr)258; @@ -211,6 +414,17 @@ internal static void Shutdown() { internal static IntPtr PyNoneType; internal static IntPtr PyTypeType; +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + internal static IntPtr PyBytesType; + internal static IntPtr PyNotImplemented; + internal const int Py_LT = 0; + internal const int Py_LE = 1; + internal const int Py_EQ = 2; + internal const int Py_NE = 3; + internal const int Py_GT = 4; + internal static IntPtr _PyObject_NextNotImplemented; +#endif + internal static IntPtr PyTrue; internal static IntPtr PyFalse; internal static IntPtr PyNone; @@ -362,6 +576,17 @@ internal unsafe static void Decref(IntPtr op) { #endif } + internal unsafe static long Refcount(IntPtr op) + { + void* p = (void*)op; + if ((void*)0 != p) + { + if (is32bit) { return (*(int*)p); } + else { return (*(long*)p); } + } + return 0; + } + #if (Py_DEBUG) // Py_IncRef and Py_DecRef are taking care of the extra payload // in Py_DEBUG builds of Python like _Py_RefTotal @@ -448,16 +673,28 @@ internal unsafe static extern void internal unsafe static extern IntPtr PyGILState_GetThisThreadState(); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int + Py_Main(int argc, [MarshalAsAttribute(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPWStr)] string[] argv); +#else [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] public unsafe static extern int Py_Main(int argc, string[] argv); +#endif [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern void PyEval_InitThreads(); + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern int + PyEval_ThreadsInitialized(); + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern void @@ -504,25 +741,70 @@ internal unsafe static extern IntPtr PyEval_GetLocals(); +#if PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35 [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.LPWStr)] internal unsafe static extern string Py_GetProgramName(); [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern void - Py_SetProgramName(string name); + Py_SetProgramName([MarshalAsAttribute(UnmanagedType.LPWStr)]string name); [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.LPWStr)] internal unsafe static extern string Py_GetPythonHome(); [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern void - Py_SetPythonHome(string home); + Py_SetPythonHome([MarshalAsAttribute(UnmanagedType.LPWStr)]string home); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.LPWStr)] + internal unsafe static extern string + Py_GetPath(); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern void + Py_SetPath([MarshalAsAttribute(UnmanagedType.LPWStr)]string home); +#else + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern string + Py_GetProgramName(); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern void + Py_SetProgramName(string name); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern string + Py_GetPythonHome(); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern void + Py_SetPythonHome(string home); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern string + Py_GetPath(); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern void + Py_SetPath(string home); +#endif [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] @@ -569,11 +851,6 @@ internal unsafe static extern IntPtr internal unsafe static extern IntPtr PyImport_ExecCodeModule(string name, IntPtr code); - [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, - ExactSpelling=true, CharSet=CharSet.Ansi)] - internal unsafe static extern IntPtr - PyCFunction_New(IntPtr ml, IntPtr self); - [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern IntPtr @@ -709,10 +986,42 @@ internal unsafe static extern IntPtr internal unsafe static extern IntPtr PyObject_CallObject(IntPtr pointer, IntPtr args); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern int + PyObject_RichCompareBool(IntPtr value1, IntPtr value2, int opid); + + internal static int PyObject_Compare(IntPtr value1, IntPtr value2) { + int res; + res = PyObject_RichCompareBool(value1, value2, Py_LT); + if (-1 == res) + return -1; + else if (1 == res) + return -1; + + res = PyObject_RichCompareBool(value1, value2, Py_EQ); + if (-1 == res) + return -1; + else if (1 == res) + return 0; + + res = PyObject_RichCompareBool(value1, value2, Py_GT); + if (-1 == res) + return -1; + else if (1 == res) + return 1; + + Exceptions.SetError(Exceptions.SystemError, "Error comparing objects"); + return -1; + } +#else [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern int PyObject_Compare(IntPtr value1, IntPtr value2); +#endif + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] @@ -729,11 +1038,16 @@ internal unsafe static extern int internal unsafe static extern int PyCallable_Check(IntPtr pointer); - [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, - ExactSpelling=true, CharSet=CharSet.Ansi)] + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] internal unsafe static extern int PyObject_IsTrue(IntPtr pointer); + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern int + PyObject_Not(IntPtr pointer); + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern int @@ -754,10 +1068,18 @@ internal unsafe static extern IntPtr internal unsafe static extern IntPtr PyObject_Str(IntPtr pointer); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint="PyObject_Str", + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyObject_Unicode(IntPtr pointer); +#else [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, CharSet = CharSet.Ansi)] internal unsafe static extern IntPtr PyObject_Unicode(IntPtr pointer); +#endif [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] @@ -769,10 +1091,18 @@ internal unsafe static extern IntPtr // Python number API //==================================================================== +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint = "PyNumber_Long", ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern IntPtr PyNumber_Int(IntPtr ob); +#else // Python 2 + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Int(IntPtr ob); +#endif [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] @@ -799,14 +1129,8 @@ internal 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); - - internal static IntPtr PyInt_FromInt32(int value) { + internal static IntPtr PyInt_FromInt32(int value) + { IntPtr v = new IntPtr(value); return PyInt_FromLong(v); } @@ -816,22 +1140,51 @@ internal static IntPtr PyInt_FromInt64(long value) { return PyInt_FromLong(v); } +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_FromLong", + ExactSpelling = true, CharSet = CharSet.Ansi)] + private unsafe static extern IntPtr + PyInt_FromLong(IntPtr value); [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint = "PyLong_AsLong", ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern int PyInt_AsLong(IntPtr value); [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint = "PyLong_FromString", ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + EntryPoint = "PyLong_GetMax", ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern int PyInt_GetMax(); +#else // Python 2 + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + private unsafe static extern IntPtr + PyInt_FromLong(IntPtr value); + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern int + PyInt_AsLong(IntPtr value); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern IntPtr + PyInt_FromString(string value, IntPtr end, int radix); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern int + PyInt_GetMax(); +#endif internal static bool PyLong_Check(IntPtr ob) { return PyObject_TYPE(ob) == Runtime.PyLongType; @@ -907,6 +1260,130 @@ internal unsafe static extern IntPtr internal unsafe static extern double PyFloat_AsDouble(IntPtr ob); + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Add(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Subtract(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Multiply(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Divide(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_And(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Xor(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Or(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Lshift(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Rshift(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Power(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Remainder(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceAdd(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceSubtract(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceMultiply(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceDivide(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceAnd(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceXor(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceOr(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceLshift(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceRshift(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlacePower(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_InPlaceRemainder(IntPtr o1, IntPtr o2); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Negative(IntPtr o1); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Positive(IntPtr o1); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyNumber_Invert(IntPtr o1); //==================================================================== // Python sequence API @@ -1005,6 +1482,57 @@ internal static IntPtr PyString_FromString(string value) { return PyString_FromStringAndSize(value, value.Length); } +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern IntPtr + PyBytes_FromString(string op); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern int + PyBytes_Size(IntPtr op); + + internal static IntPtr PyBytes_AS_STRING(IntPtr ob) { + return ob + BytesOffset.ob_sval; + } + + internal static IntPtr PyString_FromStringAndSize(string value, int length) + { + // copy the string into an unmanaged UTF-8 buffer + int len = Encoding.UTF8.GetByteCount(value); + byte[] buffer = new byte[len + 1]; + Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, 0); + IntPtr nativeUtf8 = Marshal.AllocHGlobal(buffer.Length); + try { + Marshal.Copy(buffer, 0, nativeUtf8, buffer.Length); + return PyUnicode_FromStringAndSize(nativeUtf8, length); + } + finally { + Marshal.FreeHGlobal(nativeUtf8); + } + } + +#if (PYTHON33 || PYTHON34 || PYTHON35) + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_FromStringAndSize(IntPtr value, int size); +#elif (UCS2) + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicodeUCS2_FromStringAndSize", + ExactSpelling = true, CharSet = CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_FromStringAndSize(IntPtr value, int size); +#else + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicodeUCS4_FromStringAndSize", + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern IntPtr + PyUnicode_FromStringAndSize(IntPtr value, int size); +#endif + +#else // Python2x [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern IntPtr @@ -1020,12 +1548,57 @@ internal unsafe static extern IntPtr ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern int PyString_Size(IntPtr pointer); +#endif internal static bool PyUnicode_Check(IntPtr ob) { return PyObject_TYPE(ob) == Runtime.PyUnicodeType; } #if (UCS2) +#if (PYTHON33 || PYTHON34 || PYTHON35) + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_FromObject(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint="PyUnicode_FromKindAndData", + ExactSpelling=true, + CharSet=CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_FromKindAndString(int kind, string s, int size); + + internal static IntPtr PyUnicode_FromUnicode(string s, int size) { + return PyUnicode_FromKindAndString(2, s, size); + } + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Unicode)] + internal unsafe static extern int + PyUnicode_GetSize(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Unicode)] + internal unsafe static extern char * + PyUnicode_AsUnicode(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicode_AsUnicode", + ExactSpelling = true, CharSet = CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_AS_UNICODE(IntPtr op); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_FromOrdinal(int c); + +#else [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, EntryPoint="PyUnicodeUCS2_FromObject", ExactSpelling=true, CharSet=CharSet.Unicode)] @@ -1067,6 +1640,7 @@ internal unsafe static extern IntPtr ExactSpelling=true, CharSet=CharSet.Unicode)] internal unsafe static extern IntPtr PyUnicode_FromOrdinal(int c); +#endif internal static IntPtr PyUnicode_FromString(string s) { @@ -1077,6 +1651,8 @@ internal unsafe static string GetManagedString(IntPtr op) { IntPtr type = PyObject_TYPE(op); +// Python 3 strings are all unicode +#if !(PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) if (type == Runtime.PyStringType) { return Marshal.PtrToStringAnsi( @@ -1084,6 +1660,7 @@ internal unsafe static string GetManagedString(IntPtr op) Runtime.PyString_Size(op) ); } +#endif if (type == Runtime.PyUnicodeType) { @@ -1097,6 +1674,52 @@ internal unsafe static string GetManagedString(IntPtr op) #endif #if (UCS4) +#if (PYTHON33 || PYTHON34 || PYTHON35) + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_FromObject(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicode_FromKindAndData", + ExactSpelling = true)] + internal unsafe static extern IntPtr + PyUnicode_FromKindAndString(int kind, + [MarshalAs (UnmanagedType.CustomMarshaler, + MarshalTypeRef=typeof(Utf32Marshaler))] string s, + int size); + + internal static IntPtr PyUnicode_FromUnicode(string s, int size) { + return PyUnicode_FromKindAndString(4, s, size); + } + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern int + PyUnicode_GetSize(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true)] + internal unsafe static extern IntPtr + PyUnicode_AsUnicode(IntPtr ob); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicode_AsUnicode", + ExactSpelling = true, CharSet = CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_AS_UNICODE(IntPtr op); + + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Unicode)] + internal unsafe static extern IntPtr + PyUnicode_FromOrdinal(int c); + +#else [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyUnicodeUCS4_FromObject", ExactSpelling = true, CharSet = CharSet.Unicode)] @@ -1142,6 +1765,8 @@ internal unsafe static extern IntPtr internal unsafe static extern IntPtr PyUnicode_FromOrdinal(int c); +#endif + internal static IntPtr PyUnicode_FromString(string s) { return PyUnicode_FromUnicode(s, (s.Length)); @@ -1151,6 +1776,8 @@ internal unsafe static string GetManagedString(IntPtr op) { IntPtr type = PyObject_TYPE(op); +// Python 3 strings are all unicode +#if !(PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) if (type == Runtime.PyStringType) { return Marshal.PtrToStringAnsi( @@ -1158,11 +1785,16 @@ internal unsafe static string GetManagedString(IntPtr op) Runtime.PyString_Size(op) ); } +#endif if (type == Runtime.PyUnicodeType) { IntPtr p = Runtime.PyUnicode_AsUnicode(op); - return UnixMarshal.PtrToString(p, Encoding.UTF32); + int length = Runtime.PyUnicode_GetSize(op); + int size = length * 4; + byte[] buffer = new byte[size]; + Marshal.Copy(p, buffer, 0, size); + return Encoding.UTF32.GetString(buffer, 0, size); } return null; @@ -1212,6 +1844,11 @@ internal unsafe static extern int internal unsafe static extern int PyDict_DelItem(IntPtr pointer, IntPtr key); + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern int + PyDict_DelItemString(IntPtr pointer, string key); + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern int @@ -1355,10 +1992,20 @@ internal unsafe static extern int // Python iterator API //==================================================================== +#if !(PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, CharSet = CharSet.Ansi)] internal unsafe static extern bool PyIter_Check(IntPtr pointer); +#else + internal static bool + PyIter_Check(IntPtr pointer) + { + IntPtr ob_type = (IntPtr)Marshal.PtrToStructure(pointer + ObjectOffset.ob_type, typeof(IntPtr)); + IntPtr tp_iternext = ob_type + TypeOffset.tp_iternext; + return tp_iternext != null && tp_iternext != _PyObject_NextNotImplemented; + } +#endif [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, CharSet = CharSet.Ansi)] @@ -1369,6 +2016,11 @@ internal unsafe static extern IntPtr // Python module API //==================================================================== + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern IntPtr + PyModule_New(string name); + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern string @@ -1384,6 +2036,13 @@ internal unsafe static extern IntPtr internal unsafe static extern string PyModule_GetFilename(IntPtr module); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, + ExactSpelling=true, CharSet=CharSet.Ansi)] + internal unsafe static extern IntPtr + PyModule_Create2(IntPtr module, int apiver); +#endif + [DllImport(Runtime.dll, CallingConvention=CallingConvention.Cdecl, ExactSpelling=true, CharSet=CharSet.Ansi)] internal unsafe static extern IntPtr diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index 41b845737..d022625ab 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -26,11 +26,9 @@ 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); } @@ -86,11 +84,12 @@ internal static IntPtr GetTypeHandle(ManagedType obj, Type type) { internal static IntPtr CreateType(Type impl) { IntPtr type = AllocateTypeObject(impl.Name); + int ob_size = ObjectOffset.Size(type); // Set tp_basicsize to the size of our managed instance objects. - Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)obSize); + Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size); - IntPtr offset = (IntPtr)ObjectOffset.ob_dict; + IntPtr offset = (IntPtr)ObjectOffset.DictOffset(type); Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); InitializeSlots(type, impl); @@ -124,11 +123,21 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) { } IntPtr base_ = IntPtr.Zero; + int ob_size = ObjectOffset.Size(Runtime.PyTypeType); + int tp_dictoffset = ObjectOffset.DictOffset(Runtime.PyTypeType); + // 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)) { +#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + if (typeof(System.Exception).IsAssignableFrom(clrType)) + { + ob_size = ObjectOffset.Size(Exceptions.BaseException); + tp_dictoffset = ObjectOffset.DictOffset(Exceptions.BaseException); + } + + if (clrType == typeof(System.Exception)) + { base_ = Exceptions.Exception; Runtime.Incref(base_); } else @@ -143,11 +152,9 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) { 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_basicsize, (IntPtr)ob_size); Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); - - IntPtr offset = (IntPtr)ObjectOffset.ob_dict; - Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); + Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset); InitializeSlots(type, impl.GetType()); @@ -188,57 +195,99 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) { 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); - + internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict) + { + // Utility to create a subtype of a managed type with the ability for the + // a python subtype able to override the managed implementation 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); + // the derived class can have class attributes __assembly__ and __module__ which + // control the name of the assembly and module the new type is created in. + object assembly = null; + object namespaceStr = null; + + List disposeList = new List(); + try + { + PyObject assemblyKey = new PyObject(Converter.ToPython("__assembly__", typeof(String))); + disposeList.Add(assemblyKey); + if (0 != Runtime.PyMapping_HasKey(py_dict, assemblyKey.Handle)) + { + PyObject pyAssembly = new PyObject(Runtime.PyDict_GetItem(py_dict, assemblyKey.Handle)); + disposeList.Add(pyAssembly); + if (!Converter.ToManagedValue(pyAssembly.Handle, typeof(String), out assembly, false)) + throw new InvalidCastException("Couldn't convert __assembly__ value to string"); + } - Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_); - Runtime.Incref(base_); + PyObject namespaceKey = new PyObject(Converter.ToPythonImplicit("__namespace__")); + disposeList.Add(namespaceKey); + if (0 != Runtime.PyMapping_HasKey(py_dict, namespaceKey.Handle)) + { + PyObject pyNamespace = new PyObject(Runtime.PyDict_GetItem(py_dict, namespaceKey.Handle)); + disposeList.Add(pyNamespace); + if (!Converter.ToManagedValue(pyNamespace.Handle, typeof(String), out namespaceStr, false)) + throw new InvalidCastException("Couldn't convert __namespace__ value to string"); + } + } + finally + { + foreach (PyObject o in disposeList) + o.Dispose(); + } - 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); + // create the new managed type subclassing the base managed type + ClassBase baseClass = ManagedType.GetManagedObject(py_base_type) as ClassBase; + if (null == baseClass) + { + return Exceptions.RaiseTypeError("invalid base class, expected CLR class type"); + } - CopySlot(base_, type, TypeOffset.tp_traverse); - CopySlot(base_, type, TypeOffset.tp_clear); - CopySlot(base_, type, TypeOffset.tp_is_gc); + try + { + Type subType = ClassDerivedObject.CreateDerivedType(name, + baseClass.type, + py_dict, + (string)namespaceStr, + (string)assembly); - Runtime.PyType_Ready(type); + // create the new ManagedType and python type + ClassBase subClass = ClassManager.GetClass(subType); + IntPtr py_type = GetTypeHandle(subClass, subType); + // by default the class dict will have all the C# methods in it, but as this is a + // derived class we want the python overrides in there instead if they exist. + IntPtr cls_dict = Marshal.ReadIntPtr(py_type, TypeOffset.tp_dict); + Runtime.PyDict_Update(cls_dict, py_dict); - IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); - IntPtr mod = Runtime.PyString_FromString("CLR"); - Runtime.PyDict_SetItemString(tp_dict, "__module__", mod); + return py_type; + } + catch (Exception e) + { + return Exceptions.RaiseTypeError(e.Message); + } + } - // for now, move up hidden handle... - IntPtr gc = Marshal.ReadIntPtr(base_, TypeOffset.magic()); - Marshal.WriteIntPtr(type, TypeOffset.magic(), gc); + internal static IntPtr WriteMethodDef(IntPtr mdef, IntPtr name, IntPtr func, int flags, IntPtr doc) + { + Marshal.WriteIntPtr(mdef, name); + Marshal.WriteIntPtr(mdef, (1 * IntPtr.Size), func); + Marshal.WriteInt32(mdef, (2 * IntPtr.Size), flags); + Marshal.WriteIntPtr(mdef, (3 * IntPtr.Size), doc); + return mdef + 4 * IntPtr.Size; + } - return type; + internal static IntPtr WriteMethodDef(IntPtr mdef, string name, IntPtr func, int flags = 0x0001, string doc = null) + { + IntPtr namePtr = Marshal.StringToHGlobalAnsi(name); + IntPtr docPtr = doc != null ? Marshal.StringToHGlobalAnsi(doc) : IntPtr.Zero; + + return WriteMethodDef(mdef, namePtr, func, flags, docPtr); } + internal static IntPtr WriteMethodDefSentinel(IntPtr mdef) + { + return WriteMethodDef(mdef, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero); + } internal static IntPtr CreateMetaType(Type impl) { @@ -273,7 +322,27 @@ internal static IntPtr CreateMetaType(Type impl) { flags |= TypeFlags.Managed; flags |= TypeFlags.HeapType; flags |= TypeFlags.HaveGC; - Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + + // We need space for 3 PyMethodDef structs, each of them + // 4 int-ptrs in size. + IntPtr mdef = Runtime.PyMem_Malloc(3 * (4 * IntPtr.Size)); + IntPtr mdefStart = mdef; + mdef = WriteMethodDef( + mdef, + "__instancecheck__", + Interop.GetThunk(typeof(MetaType).GetMethod("__instancecheck__"), "BinaryFunc") + ); + + mdef = WriteMethodDef( + mdef, + "__subclasscheck__", + Interop.GetThunk(typeof(MetaType).GetMethod("__subclasscheck__"), "BinaryFunc") + ); + + mdef = WriteMethodDefSentinel(mdef); + + Marshal.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart); Runtime.PyType_Ready(type); @@ -338,12 +407,24 @@ internal static IntPtr AllocateTypeObject(string name) { // 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. - +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + // For python3 we leak two objects. One for the ascii representation + // required for tp_name, and another for the unicode representation + // for ht_name. + IntPtr temp = Runtime.PyBytes_FromString(name); + IntPtr raw = Runtime.PyBytes_AS_STRING(temp); + temp = Runtime.PyUnicode_FromString(name); +#else IntPtr temp = Runtime.PyString_FromString(name); IntPtr raw = Runtime.PyString_AS_STRING(temp); +#endif Marshal.WriteIntPtr(type, TypeOffset.tp_name, raw); Marshal.WriteIntPtr(type, TypeOffset.name, temp); +#if (PYTHON33 || PYTHON34 || PYTHON35) + Marshal.WriteIntPtr(type, TypeOffset.qualname, temp); +#endif + long ptr = type.ToInt64(); // 64-bit safe temp = new IntPtr(ptr + TypeOffset.nb_add); @@ -355,8 +436,13 @@ internal static IntPtr AllocateTypeObject(string name) { temp = new IntPtr(ptr + TypeOffset.mp_length); Marshal.WriteIntPtr(type, TypeOffset.tp_as_mapping, temp); +#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) + temp = new IntPtr(ptr + TypeOffset.bf_getbuffer); + Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, temp); +#else temp = new IntPtr(ptr + TypeOffset.bf_getreadbuffer); Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, temp); +#endif return type; } @@ -416,19 +502,22 @@ private static void InitMethods(IntPtr pytype, Type type) { Type marker = typeof(PythonMethodAttribute); BindingFlags flags = BindingFlags.Public | BindingFlags.Static; + HashSet addedMethods = new HashSet(); 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); + if (!addedMethods.Contains(method.Name)) { + 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(type, method_name, mi); + Runtime.PyDict_SetItemString(dict, method_name, m.pyHandle); + addedMethods.Add(method_name); + } } } type = type.BaseType; diff --git a/src/runtime/typemethod.cs b/src/runtime/typemethod.cs index ab95f28ed..9170e5a4c 100644 --- a/src/runtime/typemethod.cs +++ b/src/runtime/typemethod.cs @@ -19,11 +19,11 @@ namespace Python.Runtime { internal class TypeMethod : MethodObject { - public TypeMethod(string name, MethodInfo[] info) : - base(name, info) {} + public TypeMethod(Type type, string name, MethodInfo[] info) : + base(type, name, info) {} - public TypeMethod(string name, MethodInfo[] info, bool allow_threads) : - base(name, info, allow_threads) { } + public TypeMethod(Type type, string name, MethodInfo[] info, bool allow_threads) : + base(type, name, info, allow_threads) { } public override IntPtr Invoke(IntPtr ob, IntPtr args, IntPtr kw) { MethodInfo mi = this.info[0]; diff --git a/src/testing/Python.Test.csproj b/src/testing/Python.Test.csproj index b0c41a866..46ca484bc 100644 --- a/src/testing/Python.Test.csproj +++ b/src/testing/Python.Test.csproj @@ -120,6 +120,7 @@ + @@ -140,8 +141,11 @@ $(SolutionDir) + $(TargetPath) + $(TargetDir)$(TargetName).pdb - + + diff --git a/src/testing/indexertest.cs b/src/testing/indexertest.cs index fa4a8c8e2..7caba4e64 100644 --- a/src/testing/indexertest.cs +++ b/src/testing/indexertest.cs @@ -347,6 +347,23 @@ public MultiTypeIndexerTest() : base() {} } + public class MultiDefaultKeyIndexerTest : IndexerBase { + + public MultiDefaultKeyIndexerTest() : base() { } + + public string this[int i1, int i2 = 2] { + get { + string key = i1.ToString() + i2.ToString(); + return (string)t[key]; + } + set { + string key = i1.ToString() + i2.ToString(); + t[key] = value; + } + } + + } + diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index 086aa58d5..28ef8f553 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -117,6 +117,14 @@ public static bool TestStringRefParams (string s, ref string s1) { return true; } + public static bool TestNonParamsArrayInLastPlace(int i1, int[] i2) { + return false; + } + + public static bool TestNonParamsArrayInLastPlace(int i1, int i2, int i3) { + return true; + } + public static bool TestValueOutParams (string s, out int i1) { i1 = 42; return true; @@ -155,6 +163,16 @@ public static void TestVoidSingleRefParam (ref int i) { i = 42; } + public static int TestSingleDefaultParam(int i = 5) { + return i; + } + public static int TestTwoDefaultParam(int i = 5, int j = 6) { + return i + j; + } + public static int TestOneArgAndTwoDefaultParam(int z, int i = 5, int j = 6) { + return i + j + z; + } + // overload selection test support diff --git a/src/testing/subclasstest.cs b/src/testing/subclasstest.cs new file mode 100644 index 000000000..7a68c6dca --- /dev/null +++ b/src/testing/subclasstest.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Python.Test +{ + public interface IInterfaceTest + { + // simple test with no arguments + string foo(); + + // test passing objects and boxing primitives + string bar(string s, int i); + + // test events on interfaces + event TestEventHandler TestEvent; + + void OnTestEvent(int value); + } + + public class SubClassTest : IInterfaceTest + { + public event TestEventHandler TestEvent; + + public SubClassTest() + { + } + + // simple test with no arguments + public virtual string foo() + { + return "foo"; + } + + // test passing objects and boxing primitives + public virtual string bar(string s, int i) + { + return s; + } + + // virtual methods that aren't overriden in python still work + public virtual string not_overriden() + { + return "not_overriden"; + } + + public virtual IList return_list() + { + return new List { "a", "b", "c" }; + } + + public static IList test_list(SubClassTest x) + { + // calls into python if return_list is overriden + return x.return_list(); + } + + // raise the test event + public virtual void OnTestEvent(int value) + { + if (null != TestEvent) + TestEvent(this, new TestEventArgs(value)); + } + } + + public class TestFunctions + { + public static string test_foo(IInterfaceTest x) + { + // calls into python if foo is overriden + return x.foo(); + } + + public static string test_bar(IInterfaceTest x, string s, int i) + { + // calls into python if bar is overriden + return x.bar(s, i); + } + + // test instances can be constructed in managed code + public static IInterfaceTest create_instance(Type t) + { + return (IInterfaceTest)t.GetConstructor(new Type[] {}).Invoke(new Object[] {}); + } + + // test instances pass through managed code unchanged + public static IInterfaceTest pass_through(IInterfaceTest s) + { + return s; + } + + public static int test_event(IInterfaceTest x, int value) + { + // reuse the event handler from eventtest.cs + EventTest et = new EventTest(); + x.TestEvent += et.GenericHandler; + + // raise the event (should trigger both python and managed handlers) + x.OnTestEvent(value); + + x.TestEvent -= et.GenericHandler; + return et.value; + } + } +} diff --git a/src/tests/runtests.py b/src/tests/runtests.py index 452b701f8..60bf075bf 100644 --- a/src/tests/runtests.py +++ b/src/tests/runtests.py @@ -18,7 +18,7 @@ try: import System except ImportError: - print "Load clr import hook" + print("Load clr import hook") import clr test_modules = ( @@ -69,6 +69,6 @@ def main(verbosity=1): if __name__ == '__main__': main(1) if '--pause' in sys.argv: - print "Press enter to continue" + print("Press enter to continue") raw_input() diff --git a/src/tests/test_array.py b/src/tests/test_array.py index 3a2259e45..a545c1b4c 100644 --- a/src/tests/test_array.py +++ b/src/tests/test_array.py @@ -10,6 +10,11 @@ import sys, os, string, unittest, types import Python.Test as Test import System +import six + +if six.PY3: + long = int + unichr = chr class ArrayTests(unittest.TestCase): @@ -422,8 +427,8 @@ def testInt64Array(self): self.assertTrue(items[0] == 0) self.assertTrue(items[4] == 4) - max = 9223372036854775807L - min = -9223372036854775808L + max = long(9223372036854775807) + min = long(-9223372036854775808) items[0] = max self.assertTrue(items[0] == max) @@ -522,7 +527,7 @@ def testUInt32Array(self): self.assertTrue(items[0] == 0) self.assertTrue(items[4] == 4) - max = 4294967295L + max = long(4294967295) min = 0 items[0] = max @@ -572,7 +577,7 @@ def testUInt64Array(self): self.assertTrue(items[0] == 0) self.assertTrue(items[4] == 4) - max = 18446744073709551615L + max = long(18446744073709551615) min = 0 items[0] = max @@ -1056,7 +1061,7 @@ def testArrayIteration(self): empty = Test.NullArrayTest().empty for i in empty: - raise TypeError, 'iteration over empty array' + raise TypeError('iteration over empty array') def testTupleArrayConversion(self): @@ -1131,7 +1136,10 @@ def testSequenceArrayConversion(self): """Test conversion of sequence-like objects to array arguments.""" from Python.Test import ArrayConversionTest from Python.Test import Spam - from UserList import UserList + if six.PY3: + from collections import UserList + else: + from UserList import UserList items = UserList() for i in range(10): @@ -1146,7 +1154,10 @@ def testSequenceNestedArrayConversion(self): """Test conversion of sequences to array-of-array arguments.""" from Python.Test import ArrayConversionTest from Python.Test import Spam - from UserList import UserList + if six.PY3: + from collections import UserList + else: + from UserList import UserList items = UserList() for i in range(10): @@ -1235,7 +1246,10 @@ def testSequenceArrayConversionTypeChecking(self): """Test error handling for sequence conversion to array arguments.""" from Python.Test import ArrayConversionTest from Python.Test import Spam - from UserList import UserList + if six.PY3: + from collections import UserList + else: + from UserList import UserList # This should work, because null / None is a valid value in an # array of reference types. @@ -1353,9 +1367,9 @@ def testSpecialArrayCreation(self): self.assertTrue(value[1] == 127) self.assertTrue(value.Length == 2) - value = Array[System.Char]([u'A', u'Z']) - self.assertTrue(value[0] == u'A') - self.assertTrue(value[1] == u'Z') + value = Array[System.Char]([six.u('A'), six.u('Z')]) + self.assertTrue(value[0] == six.u('A')) + self.assertTrue(value[1] == six.u('Z')) self.assertTrue(value.Length == 2) value = Array[System.Char]([0, 65535]) @@ -1378,29 +1392,31 @@ def testSpecialArrayCreation(self): self.assertTrue(value[1] == 2147483647) self.assertTrue(value.Length == 2) - value = Array[System.Int64]([0, 9223372036854775807L]) + value = Array[System.Int64]([0, long(9223372036854775807)]) self.assertTrue(value[0] == 0) - self.assertTrue(value[1] == 9223372036854775807L) + self.assertTrue(value[1] == long(9223372036854775807)) self.assertTrue(value.Length == 2) - value = Array[long]([0, 9223372036854775807L]) - self.assertTrue(value[0] == 0) - self.assertTrue(value[1] == 9223372036854775807L) - self.assertTrue(value.Length == 2) + # there's no explicit long type in python3, use System.Int64 instead + if not six.PY3: + value = Array[long]([0, long(9223372036854775807)]) + self.assertTrue(value[0] == 0) + self.assertTrue(value[1] == long(9223372036854775807)) + self.assertTrue(value.Length == 2) value = Array[System.UInt16]([0, 65000]) self.assertTrue(value[0] == 0) - self.assertTrue(value[1] == 65000) + self.assertTrue(value[1] == 65000) self.assertTrue(value.Length == 2) - value = Array[System.UInt32]([0, 4294967295L]) + value = Array[System.UInt32]([0, long(4294967295)]) self.assertTrue(value[0] == 0) - self.assertTrue(value[1] == 4294967295L) + self.assertTrue(value[1] == long(4294967295)) self.assertTrue(value.Length == 2) - value = Array[System.UInt64]([0, 18446744073709551615L]) + value = Array[System.UInt64]([0, long(18446744073709551615)]) self.assertTrue(value[0] == 0) - self.assertTrue(value[1] == 18446744073709551615L) + self.assertTrue(value[1] == long(18446744073709551615)) self.assertTrue(value.Length == 2) value = Array[System.Single]([0.0, 3.402823e38]) diff --git a/src/tests/test_class.py b/src/tests/test_class.py index b87587586..da50ea876 100644 --- a/src/tests/test_class.py +++ b/src/tests/test_class.py @@ -11,6 +11,12 @@ import sys, os, string, unittest, types import Python.Test as Test import System +import six + +if six.PY3: + DictProxyType = type(object.__dict__) +else: + DictProxyType = types.DictProxyType class ClassTests(unittest.TestCase): @@ -32,7 +38,7 @@ def testClassStandardAttrs(self): """Test standard class attributes.""" self.assertTrue(ClassTest.__name__ == 'ClassTest') self.assertTrue(ClassTest.__module__ == 'Python.Test') - self.assertTrue(type(ClassTest.__dict__) == types.DictProxyType) + self.assertTrue(type(ClassTest.__dict__) == DictProxyType) self.assertTrue(len(ClassTest.__doc__) > 0) diff --git a/src/tests/test_compat.py b/src/tests/test_compat.py index 7bb80d488..66c9cff16 100644 --- a/src/tests/test_compat.py +++ b/src/tests/test_compat.py @@ -8,6 +8,12 @@ # =========================================================================== import sys, os, string, unittest, types +import six + +if six.PY3: + ClassType = type +else: + ClassType = types.ClassType class CompatibilityTests(unittest.TestCase): @@ -19,8 +25,11 @@ def isCLRModule(self, object): return type(object).__name__ == 'ModuleObject' def isCLRRootModule(self, object): + if six.PY3: + # in Python 3 the clr module is a normal python module + return object.__name__ == "clr" return type(object).__name__ == 'CLRModule' - + def isCLRClass(self, object): return type(object).__name__ == 'CLR Metatype' # for now @@ -36,9 +45,15 @@ def testSimpleImport(self): self.assertTrue(type(sys) == types.ModuleType) self.assertTrue(sys.__name__ == 'sys') - import httplib - self.assertTrue(type(httplib) == types.ModuleType) - self.assertTrue(httplib.__name__ == 'httplib') + if six.PY3: + import http.client + self.assertTrue(type(http.client) == types.ModuleType) + self.assertTrue(http.client.__name__ == 'http.client') + + else: + import httplib + self.assertTrue(type(httplib) == types.ModuleType) + self.assertTrue(httplib.__name__ == 'httplib') def testSimpleImportWithAlias(self): @@ -51,9 +66,15 @@ def testSimpleImportWithAlias(self): self.assertTrue(type(mySys) == types.ModuleType) self.assertTrue(mySys.__name__ == 'sys') - import httplib as myHttplib - self.assertTrue(type(myHttplib) == types.ModuleType) - self.assertTrue(myHttplib.__name__ == 'httplib') + if six.PY3: + import http.client as myHttplib + self.assertTrue(type(myHttplib) == types.ModuleType) + self.assertTrue(myHttplib.__name__ == 'http.client') + + else: + import httplib as myHttplib + self.assertTrue(type(myHttplib) == types.ModuleType) + self.assertTrue(myHttplib.__name__ == 'httplib') def testDottedNameImport(self): @@ -127,7 +148,7 @@ def testDottedNameImportFrom(self): self.assertTrue(pulldom.__name__ == 'xml.dom.pulldom') from xml.dom.pulldom import PullDOM - self.assertTrue(type(PullDOM) == types.ClassType) + self.assertTrue(type(PullDOM) == ClassType) self.assertTrue(PullDOM.__name__ == 'PullDOM') @@ -146,7 +167,7 @@ def testDottedNameImportFromWithAlias(self): self.assertTrue(myPulldom.__name__ == 'xml.dom.pulldom') from xml.dom.pulldom import PullDOM as myPullDOM - self.assertTrue(type(myPullDOM) == types.ClassType) + self.assertTrue(type(myPullDOM) == ClassType) self.assertTrue(myPullDOM.__name__ == 'PullDOM') @@ -178,7 +199,7 @@ def testExplicitAssemblyLoad(self): self.assertTrue(assembly != None) import CLR.System.Data - self.assertTrue(sys.modules.has_key('System.Data')) + self.assertTrue('System.Data' in sys.modules) assembly = Assembly.LoadWithPartialName('SpamSpamSpamSpamEggsAndSpam') self.assertTrue(assembly == None) @@ -265,7 +286,7 @@ def main(): try: import System except ImportError: - print "Load clr import hook" + print("Load clr import hook") import clr main() diff --git a/src/tests/test_constructors.py b/src/tests/test_constructors.py index 4486e50bd..593b8afd8 100644 --- a/src/tests/test_constructors.py +++ b/src/tests/test_constructors.py @@ -55,7 +55,7 @@ class sub(System.Exception): instance = sub() ob = SubclassConstructorTest(instance) - print ob + print(ob) self.assertTrue(isinstance(ob.value, System.Exception)) diff --git a/src/tests/test_conversion.py b/src/tests/test_conversion.py index 8408f6fe3..961c7b9e8 100644 --- a/src/tests/test_conversion.py +++ b/src/tests/test_conversion.py @@ -10,6 +10,11 @@ import sys, os, string, unittest, types from Python.Test import ConversionTest import System +import six + +if six.PY3: + long = int + unichr = chr class ConversionTests(unittest.TestCase): @@ -176,16 +181,16 @@ def testCharConversion(self): self.assertTrue(System.Char.MinValue == unichr(0)) object = ConversionTest() - self.assertTrue(object.CharField == u'A') + self.assertTrue(object.CharField == six.u('A')) object.CharField = 'B' - self.assertTrue(object.CharField == u'B') + self.assertTrue(object.CharField == six.u('B')) - object.CharField = u'B' - self.assertTrue(object.CharField == u'B') + object.CharField = six.u('B') + self.assertTrue(object.CharField == six.u('B')) object.CharField = 67 - self.assertTrue(object.CharField == u'C') + self.assertTrue(object.CharField == six.u('C')) def test(): ConversionTest().CharField = 65536 @@ -307,23 +312,23 @@ def test(): def testInt64Conversion(self): """Test int64 conversion.""" - self.assertTrue(System.Int64.MaxValue == 9223372036854775807L) - self.assertTrue(System.Int64.MinValue == -9223372036854775808L) + self.assertTrue(System.Int64.MaxValue == long(9223372036854775807)) + self.assertTrue(System.Int64.MinValue == long(-9223372036854775808)) object = ConversionTest() self.assertTrue(object.Int64Field == 0) - object.Int64Field = 9223372036854775807L - self.assertTrue(object.Int64Field == 9223372036854775807L) + object.Int64Field = long(9223372036854775807) + self.assertTrue(object.Int64Field == long(9223372036854775807)) - object.Int64Field = -9223372036854775808L - self.assertTrue(object.Int64Field == -9223372036854775808L) + object.Int64Field = long(-9223372036854775808) + self.assertTrue(object.Int64Field == long(-9223372036854775808)) - object.Int64Field = System.Int64(9223372036854775807L) - self.assertTrue(object.Int64Field == 9223372036854775807L) + object.Int64Field = System.Int64(long(9223372036854775807)) + self.assertTrue(object.Int64Field == long(9223372036854775807)) - object.Int64Field = System.Int64(-9223372036854775808L) - self.assertTrue(object.Int64Field == -9223372036854775808L) + object.Int64Field = System.Int64(long(-9223372036854775808)) + self.assertTrue(object.Int64Field == long(-9223372036854775808)) def test(): ConversionTest().Int64Field = "spam" @@ -336,22 +341,22 @@ def test(): self.assertRaises(TypeError, test) def test(): - ConversionTest().Int64Field = 9223372036854775808L + ConversionTest().Int64Field = long(9223372036854775808) self.assertRaises(OverflowError, test) def test(): - ConversionTest().Int64Field = -9223372036854775809L + ConversionTest().Int64Field = long(-9223372036854775809) self.assertRaises(OverflowError, test) def test(): - value = System.Int64(9223372036854775808L) + value = System.Int64(long(9223372036854775808)) self.assertRaises(OverflowError, test) def test(): - value = System.Int64(-9223372036854775809L) + value = System.Int64(long(-9223372036854775809)) self.assertRaises(OverflowError, test) @@ -409,20 +414,20 @@ def test(): def testUInt32Conversion(self): """Test uint32 conversion.""" - self.assertTrue(System.UInt32.MaxValue == 4294967295L) + self.assertTrue(System.UInt32.MaxValue == long(4294967295)) self.assertTrue(System.UInt32.MinValue == 0) object = ConversionTest() self.assertTrue(object.UInt32Field == 0) - object.UInt32Field = 4294967295L - self.assertTrue(object.UInt32Field == 4294967295L) + object.UInt32Field = long(4294967295) + self.assertTrue(object.UInt32Field == long(4294967295)) object.UInt32Field = -0 self.assertTrue(object.UInt32Field == 0) - object.UInt32Field = System.UInt32(4294967295L) - self.assertTrue(object.UInt32Field == 4294967295L) + object.UInt32Field = System.UInt32(long(4294967295)) + self.assertTrue(object.UInt32Field == long(4294967295)) object.UInt32Field = System.UInt32(0) self.assertTrue(object.UInt32Field == 0) @@ -438,7 +443,7 @@ def test(): self.assertRaises(TypeError, test) def test(): - ConversionTest().UInt32Field = 4294967296L + ConversionTest().UInt32Field = long(4294967296) self.assertRaises(OverflowError, test) @@ -448,7 +453,7 @@ def test(): self.assertRaises(OverflowError, test) def test(): - value = System.UInt32(4294967296L) + value = System.UInt32(long(4294967296)) self.assertRaises(OverflowError, test) @@ -460,20 +465,20 @@ def test(): def testUInt64Conversion(self): """Test uint64 conversion.""" - self.assertTrue(System.UInt64.MaxValue == 18446744073709551615L) + self.assertTrue(System.UInt64.MaxValue == long(18446744073709551615)) self.assertTrue(System.UInt64.MinValue == 0) object = ConversionTest() self.assertTrue(object.UInt64Field == 0) - object.UInt64Field = 18446744073709551615L - self.assertTrue(object.UInt64Field == 18446744073709551615L) + object.UInt64Field = long(18446744073709551615) + self.assertTrue(object.UInt64Field == long(18446744073709551615)) object.UInt64Field = -0 self.assertTrue(object.UInt64Field == 0) - object.UInt64Field = System.UInt64(18446744073709551615L) - self.assertTrue(object.UInt64Field == 18446744073709551615L) + object.UInt64Field = System.UInt64(long(18446744073709551615)) + self.assertTrue(object.UInt64Field == long(18446744073709551615)) object.UInt64Field = System.UInt64(0) self.assertTrue(object.UInt64Field == 0) @@ -489,7 +494,7 @@ def test(): self.assertRaises(TypeError, test) def test(): - ConversionTest().UInt64Field = 18446744073709551616L + ConversionTest().UInt64Field = long(18446744073709551616) self.assertRaises(OverflowError, test) @@ -499,7 +504,7 @@ def test(): self.assertRaises(OverflowError, test) def test(): - value = System.UInt64(18446744073709551616L) + value = System.UInt64(long(18446744073709551616)) self.assertRaises(OverflowError, test) @@ -618,7 +623,7 @@ def testDecimalConversion(self): max_d = Decimal.Parse("79228162514264337593543950335") min_d = Decimal.Parse("-79228162514264337593543950335") - self.assertTrue(Decimal.ToInt64(Decimal(10)) == 10L) + self.assertTrue(Decimal.ToInt64(Decimal(10)) == long(10)) object = ConversionTest() self.assertTrue(object.DecimalField == Decimal(0)) @@ -659,25 +664,25 @@ def testStringConversion(self): object = ConversionTest() self.assertTrue(object.StringField == "spam") - self.assertTrue(object.StringField == u"spam") + self.assertTrue(object.StringField == six.u("spam")) object.StringField = "eggs" self.assertTrue(object.StringField == "eggs") - self.assertTrue(object.StringField == u"eggs") + self.assertTrue(object.StringField == six.u("eggs")) - object.StringField = u"spam" + object.StringField = six.u("spam") self.assertTrue(object.StringField == "spam") - self.assertTrue(object.StringField == u"spam") + self.assertTrue(object.StringField == six.u("spam")) - object.StringField = u'\uffff\uffff' - self.assertTrue(object.StringField == u'\uffff\uffff') + object.StringField = six.u('\uffff\uffff') + self.assertTrue(object.StringField == six.u('\uffff\uffff')) object.StringField = System.String("spam") self.assertTrue(object.StringField == "spam") - self.assertTrue(object.StringField == u"spam") + self.assertTrue(object.StringField == six.u("spam")) - object.StringField = System.String(u'\uffff\uffff') - self.assertTrue(object.StringField == u'\uffff\uffff') + object.StringField = System.String(six.u('\uffff\uffff')) + self.assertTrue(object.StringField == six.u('\uffff\uffff')) object.StringField = None self.assertTrue(object.StringField == None) @@ -829,11 +834,11 @@ def testByteArrayConversion(self): self.assertTrue(array[0] == 0) self.assertTrue(array[4] == 4) - value = "testing" + value = six.b("testing") object.ByteArrayField = value array = object.ByteArrayField for i in range(len(value)): - self.assertTrue(array[i] == ord(value[i])) + self.assertTrue(array[i] == six.indexbytes(value, i)) def testSByteArrayConversion(self): @@ -848,11 +853,11 @@ def testSByteArrayConversion(self): self.assertTrue(array[0] == 0) self.assertTrue(array[4] == 4) - value = "testing" + value = six.b("testing") object.SByteArrayField = value array = object.SByteArrayField for i in range(len(value)): - self.assertTrue(array[i] == ord(value[i])) + self.assertTrue(array[i] == six.indexbytes(value, i)) diff --git a/src/tests/test_delegate.py b/src/tests/test_delegate.py index 21c53ea3f..0d2315925 100644 --- a/src/tests/test_delegate.py +++ b/src/tests/test_delegate.py @@ -15,6 +15,12 @@ import sys, os, string, unittest, types import Python.Test as Test import System +import six + +if six.PY3: + DictProxyType = type(object.__dict__) +else: + DictProxyType = types.DictProxyType class DelegateTests(unittest.TestCase): @@ -24,7 +30,7 @@ def testDelegateStandardAttrs(self): """Test standard delegate attributes.""" self.assertTrue(PublicDelegate.__name__ == 'PublicDelegate') self.assertTrue(PublicDelegate.__module__ == 'Python.Test') - self.assertTrue(type(PublicDelegate.__dict__) == types.DictProxyType) + self.assertTrue(type(PublicDelegate.__dict__) == DictProxyType) self.assertTrue(PublicDelegate.__doc__ == None) diff --git a/src/tests/test_enum.py b/src/tests/test_enum.py index 98db3f3c6..26b14c274 100644 --- a/src/tests/test_enum.py +++ b/src/tests/test_enum.py @@ -10,6 +10,13 @@ import sys, os, string, unittest, types from System import DayOfWeek from Python import Test +import six + +if six.PY3: + DictProxyType = type(object.__dict__) + long = int +else: + DictProxyType = types.DictProxyType class EnumTests(unittest.TestCase): @@ -19,7 +26,7 @@ def testEnumStandardAttrs(self): """Test standard enum attributes.""" self.assertTrue(DayOfWeek.__name__ == 'DayOfWeek') self.assertTrue(DayOfWeek.__module__ == 'System') - self.assertTrue(type(DayOfWeek.__dict__) == types.DictProxyType) + self.assertTrue(type(DayOfWeek.__dict__) == DictProxyType) self.assertTrue(DayOfWeek.__doc__ == None) @@ -71,23 +78,23 @@ def testIntEnum(self): def testUIntEnum(self): """Test uint enum.""" - self.assertTrue(Test.UIntEnum.Zero == 0L) - self.assertTrue(Test.UIntEnum.One == 1L) - self.assertTrue(Test.UIntEnum.Two == 2L) + self.assertTrue(Test.UIntEnum.Zero == long(0)) + self.assertTrue(Test.UIntEnum.One == long(1)) + self.assertTrue(Test.UIntEnum.Two == long(2)) def testLongEnum(self): """Test long enum.""" - self.assertTrue(Test.LongEnum.Zero == 0L) - self.assertTrue(Test.LongEnum.One == 1L) - self.assertTrue(Test.LongEnum.Two == 2L) + self.assertTrue(Test.LongEnum.Zero == long(0)) + self.assertTrue(Test.LongEnum.One == long(1)) + self.assertTrue(Test.LongEnum.Two == long(2)) def testULongEnum(self): """Test ulong enum.""" - self.assertTrue(Test.ULongEnum.Zero == 0L) - self.assertTrue(Test.ULongEnum.One == 1L) - self.assertTrue(Test.ULongEnum.Two == 2L) + self.assertTrue(Test.ULongEnum.Zero == long(0)) + self.assertTrue(Test.ULongEnum.One == long(1)) + self.assertTrue(Test.ULongEnum.Two == long(2)) def testInstantiateEnumFails(self): diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py index de6dd01e5..8435424da 100644 --- a/src/tests/test_exceptions.py +++ b/src/tests/test_exceptions.py @@ -9,6 +9,10 @@ import sys, os, string, unittest, types import System +import six + +if six.PY3: + unicode = str # Note: all of these tests are known to fail because Python currently # doesn't allow new-style classes to be used as exceptions. I'm leaving @@ -21,10 +25,11 @@ class ExceptionTests(unittest.TestCase): def testUnifiedExceptionSemantics(self): """Test unified exception semantics.""" from System import Exception, Object - import exceptions e = Exception('Something bad happened') - self.assertTrue(isinstance(e, exceptions.Exception)) + if not six.PY3: + import exceptions + self.assertTrue(isinstance(e, exceptions.Exception)) self.assertTrue(isinstance(e, Exception)) @@ -49,7 +54,6 @@ def testExtendedExceptionAttributes(self): """Test accessing extended exception attributes.""" from Python.Test import ExceptionTest, ExtendedException from System import Exception, OverflowException - import exceptions e = ExceptionTest.GetExtendedException() self.assertTrue(isinstance(e, ExtendedException)) @@ -93,7 +97,7 @@ def testRaiseClassExceptionWithValue(self): from System import NullReferenceException def test(): - raise NullReferenceException, 'Aiiieee!' + raise NullReferenceException('Aiiieee!') self.assertRaises(NullReferenceException, test) @@ -185,7 +189,8 @@ def testCatchExceptionFromManagedMethod(self): try: ExceptionTest().ThrowException() - except OverflowException, e: + except OverflowException: + e = sys.exc_info()[1] self.assertTrue(isinstance(e, OverflowException)) return @@ -199,13 +204,15 @@ def testCatchExceptionFromManagedProperty(self): try: v = ExceptionTest().ThrowProperty - except OverflowException, e: + except OverflowException: + e = sys.exc_info()[1] self.assertTrue(isinstance(e, OverflowException)) return try: ExceptionTest().ThrowProperty = 1 - except OverflowException, e: + except OverflowException: + e = sys.exc_info()[1] self.assertTrue(isinstance(e, OverflowException)) return @@ -227,7 +234,10 @@ def testCatchExceptionManagedClass(self): def testCatchExceptionPythonClass(self): """Test catching the python class of an exception.""" from System import OverflowException - from exceptions import Exception + if six.PY3: + from builtins import Exception + else: + from exceptions import Exception try: raise OverflowException('overflow') @@ -267,7 +277,8 @@ def testCatchExceptionWithAssignment(self): try: raise OverflowException('overflow') - except OverflowException, e: + except OverflowException: + e = sys.exc_info()[1] self.assertTrue(isinstance(e, OverflowException)) @@ -303,9 +314,10 @@ def testStrOfException(self): try: Convert.ToDateTime('this will fail') - except FormatException, e: + except FormatException: + e = sys.exc_info()[1] msg = unicode(e).encode("utf8") # fix for international installation - self.assertTrue(msg.find('System.Convert.ToDateTime') > -1, msg) + self.assertTrue(msg.find(unicode('System.Convert.ToDateTime').encode("utf8")) > -1, msg) def testPythonCompatOfManagedExceptions(self): @@ -336,12 +348,20 @@ def testExceptionIsInstanceOfSystemObject(self): # without causing a crash in the CPython interpreter). This test is # here mainly to remind me to update the caveat in the documentation # one day when when exceptions can be new-style classes. + + # This behaviour is now over-shadowed by the implementation of + # __instancecheck__ (i.e., overloading isinstance), so for all Python + # version >= 2.6 we expect isinstance(, Object) to + # be true, even though it does not really subclass Object. from System import OverflowException from System import Object o = OverflowException('error') - self.assertFalse(isinstance(o, Object)) - + + if sys.version_info >= (2, 6): + self.assertTrue(isinstance(o, Object)) + else: + self.assertFalse(isinstance(o, Object)) def test_suite(): diff --git a/src/tests/test_field.py b/src/tests/test_field.py index e266f65d1..1ec9c7744 100644 --- a/src/tests/test_field.py +++ b/src/tests/test_field.py @@ -11,6 +11,12 @@ from Python.Test import FieldTest from Python.Test import ShortEnum import System +import six + +if six.PY3: + IntType = int +else: + IntType = types.IntType class FieldTests(unittest.TestCase): @@ -212,15 +218,15 @@ def testFieldDescriptorGetSet(self): self.assertTrue(object.PublicStaticField == 0) descriptor = FieldTest.__dict__['PublicStaticField'] - self.assertTrue(type(descriptor) != types.IntType) + self.assertTrue(type(descriptor) != IntType) object.PublicStaticField = 0 descriptor = FieldTest.__dict__['PublicStaticField'] - self.assertTrue(type(descriptor) != types.IntType) + self.assertTrue(type(descriptor) != IntType) FieldTest.PublicStaticField = 0 descriptor = FieldTest.__dict__['PublicStaticField'] - self.assertTrue(type(descriptor) != types.IntType) + self.assertTrue(type(descriptor) != IntType) def testFieldDescriptorWrongType(self): @@ -286,15 +292,15 @@ def testByteField(self): def testCharField(self): """Test char fields.""" object = FieldTest() - self.assertTrue(object.CharField == u'A') + self.assertTrue(object.CharField == six.u('A')) self.assertTrue(object.CharField == 'A') object.CharField = 'B' - self.assertTrue(object.CharField == u'B') + self.assertTrue(object.CharField == six.u('B')) self.assertTrue(object.CharField == 'B') - object.CharField = u'C' - self.assertTrue(object.CharField == u'C') + object.CharField = six.u('C') + self.assertTrue(object.CharField == six.u('C')) self.assertTrue(object.CharField == 'C') diff --git a/src/tests/test_generic.py b/src/tests/test_generic.py index 256bca29a..1d7c6ef67 100644 --- a/src/tests/test_generic.py +++ b/src/tests/test_generic.py @@ -14,6 +14,13 @@ import sys, os, string, unittest, types import Python.Test as Test import System +import six + +if six.PY3: + long = int + unichr = chr + unicode = str + class GenericTests(unittest.TestCase): """Test CLR generics support.""" @@ -42,13 +49,13 @@ def testPythonTypeAliasing(self): dict = Dictionary[long, long]() self.assertEquals(dict.Count, 0) - dict.Add(1L, 1L) - self.assertTrue(dict[1L] == 1L) + dict.Add(long(1), long(1)) + self.assertTrue(dict[long(1)] == long(1)) dict = Dictionary[System.Int64, System.Int64]() self.assertEquals(dict.Count, 0) - dict.Add(1L, 1L) - self.assertTrue(dict[1L] == 1L) + dict.Add(long(1), long(1)) + self.assertTrue(dict[long(1)] == long(1)) dict = Dictionary[float, float]() self.assertEquals(dict.Count, 0) @@ -172,15 +179,17 @@ def testGenericTypeBinding(self): self._testGenericWrapperByType(bool, True) self._testGenericWrapperByType(System.Byte, 255) self._testGenericWrapperByType(System.SByte, 127) - self._testGenericWrapperByType(System.Char, u'A') + self._testGenericWrapperByType(System.Char, six.u('A')) self._testGenericWrapperByType(System.Int16, 32767) self._testGenericWrapperByType(System.Int32, 2147483647) self._testGenericWrapperByType(int, 2147483647) - self._testGenericWrapperByType(System.Int64, 9223372036854775807L) - self._testGenericWrapperByType(long, 9223372036854775807L) + self._testGenericWrapperByType(System.Int64, long(9223372036854775807)) + # Python 3 has no explicit long type, use System.Int64 instead + if not six.PY3: + self._testGenericWrapperByType(long, long(9223372036854775807)) self._testGenericWrapperByType(System.UInt16, 65000) - self._testGenericWrapperByType(System.UInt32, 4294967295L) - self._testGenericWrapperByType(System.UInt64, 18446744073709551615L) + self._testGenericWrapperByType(System.UInt32, long(4294967295)) + self._testGenericWrapperByType(System.UInt64, long(18446744073709551615)) self._testGenericWrapperByType(System.Single, 3.402823e38) self._testGenericWrapperByType(System.Double, 1.7976931348623157e308) self._testGenericWrapperByType(float, 1.7976931348623157e308) @@ -309,15 +318,17 @@ def testGenericMethodTypeHandling(self): self._testGenericMethodByType(bool, True) self._testGenericMethodByType(System.Byte, 255) self._testGenericMethodByType(System.SByte, 127) - self._testGenericMethodByType(System.Char, u'A') + self._testGenericMethodByType(System.Char, six.u('A')) self._testGenericMethodByType(System.Int16, 32767) self._testGenericMethodByType(System.Int32, 2147483647) self._testGenericMethodByType(int, 2147483647) - self._testGenericMethodByType(System.Int64, 9223372036854775807L) - self._testGenericMethodByType(long, 9223372036854775807L) + # Python 3 has no explicit long type, use System.Int64 instead + if not six.PY3: + self._testGenericMethodByType(System.Int64, long(9223372036854775807)) + self._testGenericMethodByType(long, long(9223372036854775807)) + self._testGenericMethodByType(System.UInt32, long(4294967295)) + self._testGenericMethodByType(System.Int64, long(1844674407370955161)) self._testGenericMethodByType(System.UInt16, 65000) - self._testGenericMethodByType(System.UInt32, 4294967295L) - self._testGenericMethodByType(System.Int64, 1844674407370955161L) self._testGenericMethodByType(System.Single, 3.402823e38) self._testGenericMethodByType(System.Double, 1.7976931348623157e308) self._testGenericMethodByType(float, 1.7976931348623157e308) @@ -330,6 +341,45 @@ def testGenericMethodTypeHandling(self): self._testGenericMethodByType(InterfaceTest, InterfaceTest(), 1) self._testGenericMethodByType(ISayHello1, InterfaceTest(), 1) + def testCorrectOverloadSelection(self): + """ + Test correct overloading selection for common types. + """ + from System.Drawing import Font + + from System import (String, Double, Single, + Int16, Int32, Int64) + from System import Math + + substr = String("substring") + self.assertTrue(substr.Substring(2) == substr.Substring.__overloads__[Int32]( + Int32(2))) + self.assertTrue(substr.Substring(2, 3) == substr.Substring.__overloads__[Int32,Int32]( + Int32(2), Int32(3))) + + for atype, value1, value2 in zip([Double, Single, Int16, Int32, Int64], + [1.0, 1.0, 1, 1, 1], + [2.0, 0.5, 2, 0, -1]): + self.assertTrue(Math.Abs(atype(value1)) == Math.Abs.__overloads__[atype](atype(value1))) + self.assertTrue(Math.Abs(value1) == Math.Abs.__overloads__[atype](atype(value1))) + self.assertTrue( + Math.Max(atype(value1), + atype(value2)) == Math.Max.__overloads__[atype, atype]( + atype(value1), + atype(value2))) + if (atype is Int64) and six.PY2: + value2 = long(value2) + self.assertTrue( + Math.Max(atype(value1), + value2) == Math.Max.__overloads__[atype, atype]( + atype(value1), + atype(value2))) + + clr.AddReference("System.Runtime.InteropServices") + from System.Runtime.InteropServices import GCHandle, GCHandleType + from System import Array, Byte + CSArray = Array.CreateInstance(Byte, 1000) + handler = GCHandle.Alloc(CSArray, GCHandleType.Pinned) def testGenericMethodOverloadSelection(self): """ @@ -439,9 +489,9 @@ def testMethodOverloadSelectionWithGenericTypes(self): self.assertTrue(value.value == 127) vtype = GenericWrapper[System.Char] - input = vtype(u'A') + input = vtype(six.u('A')) value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value.value == u'A') + self.assertTrue(value.value == six.u('A')) vtype = GenericWrapper[System.Char] input = vtype(65535) @@ -464,14 +514,16 @@ def testMethodOverloadSelectionWithGenericTypes(self): self.assertTrue(value.value == 2147483647) vtype = GenericWrapper[System.Int64] - input = vtype(9223372036854775807L) + input = vtype(long(9223372036854775807)) value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value.value == 9223372036854775807L) + self.assertTrue(value.value == long(9223372036854775807)) - vtype = GenericWrapper[long] - input = vtype(9223372036854775807L) - value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value.value == 9223372036854775807L) + # Python 3 has no explicit long type, use System.Int64 instead + if not six.PY3: + vtype = GenericWrapper[long] + input = vtype(long(9223372036854775807)) + value = MethodTest.Overloaded.__overloads__[vtype](input) + self.assertTrue(value.value == long(9223372036854775807)) vtype = GenericWrapper[System.UInt16] input = vtype(65000) @@ -479,14 +531,14 @@ def testMethodOverloadSelectionWithGenericTypes(self): self.assertTrue(value.value == 65000) vtype = GenericWrapper[System.UInt32] - input = vtype(4294967295L) + input = vtype(long(4294967295)) value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value.value == 4294967295L) + self.assertTrue(value.value == long(4294967295)) vtype = GenericWrapper[System.UInt64] - input = vtype(18446744073709551615L) + input = vtype(long(18446744073709551615)) value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value.value == 18446744073709551615L) + self.assertTrue(value.value == long(18446744073709551615)) vtype = GenericWrapper[System.Single] input = vtype(3.402823e38) @@ -580,9 +632,9 @@ def testOverloadSelectionWithArraysOfGenericTypes(self): gtype = GenericWrapper[System.Char] vtype = System.Array[gtype] - input = vtype([gtype(u'A'), gtype(u'A')]) + input = vtype([gtype(six.u('A')), gtype(six.u('A'))]) value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value[0].value == u'A') + self.assertTrue(value[0].value == six.u('A')) self.assertTrue(value.Length == 2) gtype = GenericWrapper[System.Char] @@ -615,19 +667,21 @@ def testOverloadSelectionWithArraysOfGenericTypes(self): gtype = GenericWrapper[System.Int64] vtype = System.Array[gtype] - input = vtype([gtype(9223372036854775807L), - gtype(9223372036854775807L)]) + input = vtype([gtype(long(9223372036854775807)), + gtype(long(9223372036854775807))]) value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value[0].value == 9223372036854775807L) - self.assertTrue(value.Length == 2) - - gtype = GenericWrapper[long] - vtype = System.Array[gtype] - input = vtype([gtype(9223372036854775807L), - gtype(9223372036854775807L)]) - value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value[0].value == 9223372036854775807L) + self.assertTrue(value[0].value == long(9223372036854775807)) self.assertTrue(value.Length == 2) + + # Python 3 has no explicit long type, use System.Int64 instead + if not six.PY3: + gtype = GenericWrapper[long] + vtype = System.Array[gtype] + input = vtype([gtype(long(9223372036854775807)), + gtype(long(9223372036854775807))]) + value = MethodTest.Overloaded.__overloads__[vtype](input) + self.assertTrue(value[0].value == long(9223372036854775807)) + self.assertTrue(value.Length == 2) gtype = GenericWrapper[System.UInt16] vtype = System.Array[gtype] @@ -638,17 +692,17 @@ def testOverloadSelectionWithArraysOfGenericTypes(self): gtype = GenericWrapper[System.UInt32] vtype = System.Array[gtype] - input = vtype([gtype(4294967295L), gtype(4294967295L)]) + input = vtype([gtype(long(4294967295)), gtype(long(4294967295))]) value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value[0].value == 4294967295L) + self.assertTrue(value[0].value == long(4294967295)) self.assertTrue(value.Length == 2) gtype = GenericWrapper[System.UInt64] vtype = System.Array[gtype] - input = vtype([gtype(18446744073709551615L), - gtype(18446744073709551615L)]) + input = vtype([gtype(long(18446744073709551615)), + gtype(long(18446744073709551615))]) value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value[0].value == 18446744073709551615L) + self.assertTrue(value[0].value == long(18446744073709551615)) self.assertTrue(value.Length == 2) gtype = GenericWrapper[System.Single] diff --git a/src/tests/test_indexer.py b/src/tests/test_indexer.py index 2b1d4e100..691ebb871 100644 --- a/src/tests/test_indexer.py +++ b/src/tests/test_indexer.py @@ -8,7 +8,14 @@ # =========================================================================== import sys, os, string, unittest, types +import clr +clr.AddReference("Python.Test") import Python.Test as Test +import six + +if six.PY3: + long = int + unichr = chr class IndexerTests(unittest.TestCase): @@ -238,8 +245,8 @@ def test(): def testInt64Indexer(self): """Test Int64 indexers.""" object = Test.Int64IndexerTest() - max = 9223372036854775807L - min = -9223372036854775808L + max = long(9223372036854775807) + min = long(-9223372036854775808) self.assertTrue(object[max] == None) @@ -292,7 +299,7 @@ def test(): def testUInt32Indexer(self): """Test UInt32 indexers.""" object = Test.UInt32IndexerTest() - max = 4294967295L + max = long(4294967295) min = 0 self.assertTrue(object[max] == None) @@ -319,7 +326,7 @@ def test(): def testUInt64Indexer(self): """Test UInt64 indexers.""" object = Test.UInt64IndexerTest() - max = 18446744073709551615L + max = long(18446744073709551615) min = 0 self.assertTrue(object[max] == None) @@ -431,19 +438,19 @@ def testStringIndexer(self): object = Test.StringIndexerTest() self.assertTrue(object["spam"] == None) - self.assertTrue(object[u"spam"] == None) + self.assertTrue(object[six.u("spam")] == None) object["spam"] = "spam" self.assertTrue(object["spam"] == "spam") - self.assertTrue(object["spam"] == u"spam") - self.assertTrue(object[u"spam"] == "spam") - self.assertTrue(object[u"spam"] == u"spam") + self.assertTrue(object["spam"] == six.u("spam")) + self.assertTrue(object[six.u("spam")] == "spam") + self.assertTrue(object[six.u("spam")] == six.u("spam")) - object[u"eggs"] = u"eggs" + object[six.u("eggs")] = six.u("eggs") self.assertTrue(object["eggs"] == "eggs") - self.assertTrue(object["eggs"] == u"eggs") - self.assertTrue(object[u"eggs"] == "eggs") - self.assertTrue(object[u"eggs"] == u"eggs") + self.assertTrue(object["eggs"] == six.u("eggs")) + self.assertTrue(object[six.u("eggs")] == "eggs") + self.assertTrue(object[six.u("eggs")] == six.u("eggs")) def test(): object = Test.StringIndexerTest() @@ -509,8 +516,8 @@ def testObjectIndexer(self): object[1] = "one" self.assertTrue(object[1] == "one") - object[1L] = "long" - self.assertTrue(object[1L] == "long") + object[long(1)] = "long" + self.assertTrue(object[long(1)] == "long") def test(): class eggs: @@ -625,6 +632,17 @@ def test(): object[0, 1, spam] = "wrong" self.assertRaises(TypeError, test) + + + def testMultiDefaultKeyIndexer(self): + """Test indexers that take multiple indices with a default key arguments.""" + #default argument is 2 in the MultiDefaultKeyIndexerTest object + object = Test.MultiDefaultKeyIndexerTest() + object[0, 2] = "zero one spam" + self.assertTrue(object[0] == "zero one spam") + + object[1] = "one nine spam" + self.assertTrue(object[1, 2] == "one nine spam") def testIndexerWrongKeyType(self): diff --git a/src/tests/test_interface.py b/src/tests/test_interface.py index 1e9c0ad96..4412aefb2 100644 --- a/src/tests/test_interface.py +++ b/src/tests/test_interface.py @@ -11,6 +11,13 @@ import sys, os, string, unittest, types import Python.Test as Test import System +import six + +if six.PY3: + DictProxyType = type(object.__dict__) +else: + DictProxyType = types.DictProxyType + class InterfaceTests(unittest.TestCase): """Test CLR interface support.""" @@ -20,7 +27,7 @@ def testInterfaceStandardAttrs(self): from Python.Test import IPublicInterface as ip self.assertTrue(ip.__name__ == 'IPublicInterface') self.assertTrue(ip.__module__ == 'Python.Test') - self.assertTrue(type(ip.__dict__) == types.DictProxyType) + self.assertTrue(type(ip.__dict__) == DictProxyType) def testGlobalInterfaceVisibility(self): diff --git a/src/tests/test_method.py b/src/tests/test_method.py index 03a23cf84..ca5729a43 100644 --- a/src/tests/test_method.py +++ b/src/tests/test_method.py @@ -13,6 +13,12 @@ from Python.Test import MethodTest, MethodTestSub import System +import six + +if six.PY3: + long = int + unichr = chr + class MethodTests(unittest.TestCase): """Test CLR method support.""" @@ -235,11 +241,11 @@ def testMethodCallStructConversion(self): def testSubclassInstanceConversion(self): """Test subclass instance conversion in method call.""" - class sub(System.Exception): + class TestSubException(System.Exception): pass object = MethodTest() - instance = sub() + instance = TestSubException() result = object.TestSubclassConversion(instance) self.assertTrue(isinstance(result, System.Exception)) @@ -298,6 +304,10 @@ def testValueParamsArgs(self): self.assertTrue(result[1] == 2) self.assertTrue(result[2] == 3) + def testNonParamsArrayInLastPlace(self): + """Test overload resolution with of non-"params" array as last parameter.""" + result = MethodTest.TestNonParamsArrayInLastPlace(1, 2, 3) + self.assertTrue(result) def testStringOutParams(self): """Test use of string out-parameters.""" @@ -441,6 +451,27 @@ def test(): # None cannot be converted to a value type self.assertRaises(TypeError, test) + + def testSingleDefaultParam(self): + """Test void method with single ref-parameter.""" + result = MethodTest.TestSingleDefaultParam() + self.assertTrue(result == 5) + + def testOneArgAndTwoDefaultParam(self): + """Test void method with single ref-parameter.""" + result = MethodTest.TestOneArgAndTwoDefaultParam(11) + self.assertTrue(result == 22) + + result = MethodTest.TestOneArgAndTwoDefaultParam(15) + self.assertTrue(result == 26) + + result = MethodTest.TestOneArgAndTwoDefaultParam(20) + self.assertTrue(result == 31) + + def testTwoDefaultParam(self): + """Test void method with single ref-parameter.""" + result = MethodTest.TestTwoDefaultParam() + self.assertTrue(result == 11) def testExplicitSelectionWithOutModifier(self): @@ -501,8 +532,8 @@ def testExplicitOverloadSelection(self): value = MethodTest.Overloaded.__overloads__[System.SByte](127) self.assertTrue(value == 127) - value = MethodTest.Overloaded.__overloads__[System.Char](u'A') - self.assertTrue(value == u'A') + value = MethodTest.Overloaded.__overloads__[System.Char](six.u('A')) + self.assertTrue(value == six.u('A')) value = MethodTest.Overloaded.__overloads__[System.Char](65535) self.assertTrue(value == unichr(65535)) @@ -517,25 +548,27 @@ def testExplicitOverloadSelection(self): self.assertTrue(value == 2147483647) value = MethodTest.Overloaded.__overloads__[System.Int64]( - 9223372036854775807L + long(9223372036854775807) ) - self.assertTrue(value == 9223372036854775807L) + self.assertTrue(value == long(9223372036854775807)) - value = MethodTest.Overloaded.__overloads__[long]( - 9223372036854775807L - ) - self.assertTrue(value == 9223372036854775807L) + # Python 3 has no explicit long type, use System.Int64 instead + if not six.PY3: + value = MethodTest.Overloaded.__overloads__[long]( + long(9223372036854775807) + ) + self.assertTrue(value == long(9223372036854775807)) value = MethodTest.Overloaded.__overloads__[System.UInt16](65000) self.assertTrue(value == 65000) - value = MethodTest.Overloaded.__overloads__[System.UInt32](4294967295L) - self.assertTrue(value == 4294967295L) + value = MethodTest.Overloaded.__overloads__[System.UInt32](long(4294967295)) + self.assertTrue(value == long(4294967295)) value = MethodTest.Overloaded.__overloads__[System.UInt64]( - 18446744073709551615L + long(18446744073709551615) ) - self.assertTrue(value == 18446744073709551615L) + self.assertTrue(value == long(18446744073709551615)) value = MethodTest.Overloaded.__overloads__[System.Single](3.402823e38) self.assertTrue(value == 3.402823e38) @@ -617,10 +650,10 @@ def testOverloadSelectionWithArrayTypes(self): self.assertTrue(value[1] == 127) vtype = Array[System.Char] - input = vtype([u'A', u'Z']) + input = vtype([six.u('A'), six.u('Z')]) value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value[0] == u'A') - self.assertTrue(value[1] == u'Z') + self.assertTrue(value[0] == six.u('A')) + self.assertTrue(value[1] == six.u('Z')) vtype = Array[System.Char] input = vtype([0, 65535]) @@ -647,16 +680,18 @@ def testOverloadSelectionWithArrayTypes(self): self.assertTrue(value[1] == 2147483647) vtype = Array[System.Int64] - input = vtype([0, 9223372036854775807L]) + input = vtype([0, long(9223372036854775807)]) value = MethodTest.Overloaded.__overloads__[vtype](input) self.assertTrue(value[0] == 0) - self.assertTrue(value[1] == 9223372036854775807L) + self.assertTrue(value[1] == long(9223372036854775807)) - vtype = Array[long] - input = vtype([0, 9223372036854775807L]) - value = MethodTest.Overloaded.__overloads__[vtype](input) - self.assertTrue(value[0] == 0) - self.assertTrue(value[1] == 9223372036854775807L) + # Python 3 has no explicit long type, use System.Int64 instead + if not six.PY3: + vtype = Array[long] + input = vtype([0, long(9223372036854775807)]) + value = MethodTest.Overloaded.__overloads__[vtype](input) + self.assertTrue(value[0] == 0) + self.assertTrue(value[1] == long(9223372036854775807)) vtype = Array[System.UInt16] input = vtype([0, 65000]) @@ -665,16 +700,16 @@ def testOverloadSelectionWithArrayTypes(self): self.assertTrue(value[1] == 65000) vtype = Array[System.UInt32] - input = vtype([0, 4294967295L]) + input = vtype([0, long(4294967295)]) value = MethodTest.Overloaded.__overloads__[vtype](input) self.assertTrue(value[0] == 0) - self.assertTrue(value[1] == 4294967295L) + self.assertTrue(value[1] == long(4294967295)) vtype = Array[System.UInt64] - input = vtype([0, 18446744073709551615L]) + input = vtype([0, long(18446744073709551615)]) value = MethodTest.Overloaded.__overloads__[vtype](input) self.assertTrue(value[0] == 0) - self.assertTrue(value[1] == 18446744073709551615L) + self.assertTrue(value[1] == long(18446744073709551615)) vtype = Array[System.Single] input = vtype([0.0, 3.402823e38]) diff --git a/src/tests/test_module.py b/src/tests/test_module.py index 62ea78311..0d340652a 100644 --- a/src/tests/test_module.py +++ b/src/tests/test_module.py @@ -13,6 +13,13 @@ # testImplicitAssemblyLoad() passes on deprecation warning; perfect! # ##clr.AddReference('System.Windows.Forms') import sys, os, string, unittest, types, warnings +from fnmatch import fnmatch +import six + +if six.PY3: + ClassType = type +else: + ClassType = types.ClassType class ModuleTests(unittest.TestCase): @@ -22,6 +29,9 @@ def isCLRModule(self, object): return type(object).__name__ == 'ModuleObject' def isCLRRootModule(self, object): + if six.PY3: + # in Python 3 the clr module is a normal python module + return object.__name__ == "clr" return type(object).__name__ == 'CLRModule' def isCLRClass(self, object): @@ -62,8 +72,9 @@ def testModuleInterface(self): import System self.assertEquals(type(System.__dict__), type({})) self.assertEquals(System.__name__, 'System') - self.assertEquals(System.__file__, None) - self.assertEquals(System.__doc__, None) + # the filename can be any module from the System namespace (eg System.Data.dll or System.dll) + self.assertTrue(fnmatch(System.__file__, "*System*.dll")) + self.assertTrue(System.__doc__.startswith("Namespace containing types from the following assemblies:")) self.assertTrue(self.isCLRClass(System.String)) self.assertTrue(self.isCLRClass(System.Int32)) @@ -78,9 +89,14 @@ def testSimpleImport(self): self.assertTrue(type(sys) == types.ModuleType) self.assertTrue(sys.__name__ == 'sys') - import httplib - self.assertTrue(type(httplib) == types.ModuleType) - self.assertTrue(httplib.__name__ == 'httplib') + if six.PY3: + import http.client as httplib + self.assertTrue(type(httplib) == types.ModuleType) + self.assertTrue(httplib.__name__ == 'http.client') + else: + import httplib + self.assertTrue(type(httplib) == types.ModuleType) + self.assertTrue(httplib.__name__ == 'httplib') def testSimpleImportWithAlias(self): @@ -93,9 +109,14 @@ def testSimpleImportWithAlias(self): self.assertTrue(type(mySys) == types.ModuleType) self.assertTrue(mySys.__name__ == 'sys') - import httplib as myHttplib - self.assertTrue(type(myHttplib) == types.ModuleType) - self.assertTrue(myHttplib.__name__ == 'httplib') + if six.PY3: + import http.client as myHttplib + self.assertTrue(type(myHttplib) == types.ModuleType) + self.assertTrue(myHttplib.__name__ == 'http.client') + else: + import httplib as myHttplib + self.assertTrue(type(myHttplib) == types.ModuleType) + self.assertTrue(myHttplib.__name__ == 'httplib') def testDottedNameImport(self): @@ -169,7 +190,7 @@ def testDottedNameImportFrom(self): self.assertTrue(pulldom.__name__ == 'xml.dom.pulldom') from xml.dom.pulldom import PullDOM - self.assertTrue(type(PullDOM) == types.ClassType) + self.assertTrue(type(PullDOM) == ClassType) self.assertTrue(PullDOM.__name__ == 'PullDOM') @@ -188,7 +209,7 @@ def testDottedNameImportFromWithAlias(self): self.assertTrue(myPulldom.__name__ == 'xml.dom.pulldom') from xml.dom.pulldom import PullDOM as myPullDOM - self.assertTrue(type(myPullDOM) == types.ClassType) + self.assertTrue(type(myPullDOM) == ClassType) self.assertTrue(myPullDOM.__name__ == 'PullDOM') @@ -233,7 +254,7 @@ def testExplicitAssemblyLoad(self): self.assertTrue(assembly != None) import System.Data - self.assertTrue(sys.modules.has_key('System.Data')) + self.assertTrue('System.Data' in sys.modules) assembly = Assembly.LoadWithPartialName('SpamSpamSpamSpamEggsAndSpam') self.assertTrue(assembly == None) @@ -340,10 +361,10 @@ def test_ClrListAssemblies(self): from clr import ListAssemblies verbose = list(ListAssemblies(True)) short = list(ListAssemblies(False)) - self.assertTrue(u'mscorlib' in short) - self.assertTrue(u'System' in short) - self.assertTrue('Culture=' in verbose[0]) - self.assertTrue('Version=' in verbose[0]) + self.assertTrue(six.u('mscorlib') in short) + self.assertTrue(six.u('System') in short) + self.assertTrue(six.u('Culture=') in verbose[0]) + self.assertTrue(six.u('Version=') in verbose[0]) def test_ClrAddReference(self): from clr import AddReference diff --git a/src/tests/test_property.py b/src/tests/test_property.py index 851ff8af0..4b00040ef 100644 --- a/src/tests/test_property.py +++ b/src/tests/test_property.py @@ -9,6 +9,12 @@ import sys, os, string, unittest, types from Python.Test import PropertyTest +import six + +if six.PY3: + IntType = int +else: + IntType = types.IntType class PropertyTests(unittest.TestCase): @@ -139,15 +145,15 @@ def testPropertyDescriptorGetSet(self): self.assertTrue(object.PublicStaticProperty == 0) descriptor = PropertyTest.__dict__['PublicStaticProperty'] - self.assertTrue(type(descriptor) != types.IntType) + self.assertTrue(type(descriptor) != IntType) object.PublicStaticProperty = 0 descriptor = PropertyTest.__dict__['PublicStaticProperty'] - self.assertTrue(type(descriptor) != types.IntType) + self.assertTrue(type(descriptor) != IntType) PropertyTest.PublicStaticProperty = 0 descriptor = PropertyTest.__dict__['PublicStaticProperty'] - self.assertTrue(type(descriptor) != types.IntType) + self.assertTrue(type(descriptor) != IntType) def testPropertyDescriptorWrongType(self): diff --git a/src/tests/test_subclass.py b/src/tests/test_subclass.py new file mode 100644 index 000000000..f116eb4de --- /dev/null +++ b/src/tests/test_subclass.py @@ -0,0 +1,163 @@ +# =========================================================================== +# 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. +# =========================================================================== +import clr +clr.AddReference('Python.Test') +clr.AddReference('System') + +import sys, os, string, unittest, types +from Python.Test import TestFunctions, SubClassTest, IInterfaceTest, TestEventArgs +from System.Collections.Generic import List +from System import NotImplementedException + +# class that implements the test interface +class InterfaceTestClass(IInterfaceTest): + __namespace__ = "Python.Test" + + def foo(self): + return "InterfaceTestClass" + + def bar(self, x, i): + return "/".join([x] * i) + +# class that derives from a class deriving from IInterfaceTest +class DerivedClass(SubClassTest): + __namespace__ = "Python.Test" + + def foo(self): + return "DerivedClass" + + def base_foo(self): + return SubClassTest.foo(self) + + def super_foo(self): + return super(DerivedClass, self).foo() + + def bar(self, x, i): + return "_".join([x] * i) + + def return_list(self): + l = List[str]() + l.Add("A") + l.Add("B") + l.Add("C") + return l + +# class that implements IInterfaceTest.TestEvent +class DerivedEventTest(IInterfaceTest): + __namespace__ = "Python.Test" + + def __init__(self): + self.event_handlers = [] + + # event handling + def add_TestEvent(self, handler): + self.event_handlers.append(handler) + + def remove_TestEvent(self, handler): + self.event_handlers.remove(handler) + + def OnTestEvent(self, value): + args = TestEventArgs(value) + for handler in self.event_handlers: + handler(self, args) + + +class SubClassTests(unittest.TestCase): + """Test subclassing managed types""" + + def testBaseClass(self): + """Test base class managed type""" + object = SubClassTest() + self.assertEqual(object.foo(), "foo") + self.assertEqual(TestFunctions.test_foo(object), "foo") + self.assertEqual(object.bar("bar", 2), "bar") + self.assertEqual(TestFunctions.test_bar(object, "bar", 2), "bar") + self.assertEqual(object.not_overriden(), "not_overriden") + self.assertEqual(list(object.return_list()), ["a", "b", "c"]) + self.assertEqual(list(SubClassTest.test_list(object)), ["a", "b", "c"]) + + def testInterface(self): + """Test python classes can derive from C# interfaces""" + object = InterfaceTestClass() + self.assertEqual(object.foo(), "InterfaceTestClass") + self.assertEqual(TestFunctions.test_foo(object), "InterfaceTestClass") + self.assertEqual(object.bar("bar", 2), "bar/bar") + self.assertEqual(TestFunctions.test_bar(object, "bar", 2), "bar/bar") + + x = TestFunctions.pass_through(object) + self.assertEqual(id(x), id(object)) + + def testDerivedClass(self): + """Test python class derived from managed type""" + object = DerivedClass() + self.assertEqual(object.foo(), "DerivedClass") + self.assertEqual(object.base_foo(), "foo") + self.assertEqual(object.super_foo(), "foo") + self.assertEqual(TestFunctions.test_foo(object), "DerivedClass") + self.assertEqual(object.bar("bar", 2), "bar_bar") + self.assertEqual(TestFunctions.test_bar(object, "bar", 2), "bar_bar") + self.assertEqual(object.not_overriden(), "not_overriden") + self.assertEqual(list(object.return_list()), ["A", "B", "C"]) + self.assertEqual(list(SubClassTest.test_list(object)), ["A", "B", "C"]) + + x = TestFunctions.pass_through(object) + self.assertEqual(id(x), id(object)) + + def testCreateInstance(self): + """Test derived instances can be created from managed code""" + object = TestFunctions.create_instance(DerivedClass) + self.assertEqual(object.foo(), "DerivedClass") + self.assertEqual(TestFunctions.test_foo(object), "DerivedClass") + self.assertEqual(object.bar("bar", 2), "bar_bar") + self.assertEqual(TestFunctions.test_bar(object, "bar", 2), "bar_bar") + self.assertEqual(object.not_overriden(), "not_overriden") + + x = TestFunctions.pass_through(object) + self.assertEqual(id(x), id(object)) + + object2 = TestFunctions.create_instance(InterfaceTestClass) + self.assertEqual(object2.foo(), "InterfaceTestClass") + self.assertEqual(TestFunctions.test_foo(object2), "InterfaceTestClass") + self.assertEqual(object2.bar("bar", 2), "bar/bar") + self.assertEqual(TestFunctions.test_bar(object2, "bar", 2), "bar/bar") + + y = TestFunctions.pass_through(object2) + self.assertEqual(id(y), id(object2)) + + def testEvents(self): + + class EventHandler: + def handler(self, x, args): + self.value = args.value + + event_handler = EventHandler() + + x = SubClassTest() + x.TestEvent += event_handler.handler + self.assertEqual(TestFunctions.test_event(x, 1), 1) + self.assertEqual(event_handler.value, 1) + + i = InterfaceTestClass() + self.assertRaises(NotImplementedException, TestFunctions.test_event, i, 2) + + d = DerivedEventTest() + d.add_TestEvent(event_handler.handler) + self.assertEqual(TestFunctions.test_event(d, 3), 3) + self.assertEqual(event_handler.value, 3) + self.assertEqual(len(d.event_handlers), 1) + + +def test_suite(): + return unittest.makeSuite(SubClassTests) + +def main(): + unittest.TextTestRunner().run(test_suite()) + +if __name__ == '__main__': + main() diff --git a/src/tests/test_thread.py b/src/tests/test_thread.py index 171efa3bb..22d4c9538 100644 --- a/src/tests/test_thread.py +++ b/src/tests/test_thread.py @@ -7,13 +7,19 @@ # FOR A PARTICULAR PURPOSE. # =========================================================================== -import sys, os, string, unittest, types, thread +import sys, os, string, unittest, types from Python.Test import ThreadTest +import six + +if six.PY3: + import _thread as thread +else: + import thread def dprint(msg): # Debugging helper to trace thread-related tests. - if 0: print msg + if 0: print(msg) class ThreadTests(unittest.TestCase): @@ -39,7 +45,7 @@ def testDoubleCallbackToPython(self): def testPythonThreadCallsToCLR(self): """Test calls by Python-spawned threads into managed code.""" # This test is very likely to hang if something is wrong ;) - import threading, thread, time + import threading, time from System import String done = [] diff --git a/subclasstest.cs b/subclasstest.cs new file mode 100644 index 000000000..64cea87c6 --- /dev/null +++ b/subclasstest.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Python.Test +{ + public interface IInterfaceTest + { + // simple test with no arguments + string foo(); + + // test passing objects and boxing primitives + string bar(string s, int i); + } + + public class SubClassTest : IInterfaceTest + { + public SubClassTest() + { + } + + // simple test with no arguments + public virtual string foo() + { + return "foo"; + } + + // test passing objects and boxing primitives + public virtual string bar(string s, int i) + { + return s; + } + + // virtual methods that aren't overriden in python still work + public virtual string not_overriden() + { + return "not_overriden"; + } + + public virtual IList return_list() + { + return new List { "a", "b", "c" }; + } + + public static IList test_list(SubClassTest x) + { + // calls into python if return_list is overriden + return x.return_list(); + } + } + + public class TestFunctions + { + public static string test_foo(IInterfaceTest x) + { + // calls into python if foo is overriden + return x.foo(); + } + + public static string test_bar(IInterfaceTest x, string s, int i) + { + // calls into python if bar is overriden + return x.bar(s, i); + } + + // test instances can be constructed in managed code + public static IInterfaceTest create_instance(Type t) + { + return (IInterfaceTest)t.GetConstructor(new Type[] {}).Invoke(new Object[] {}); + } + + // test instances pass through managed code unchanged + public static IInterfaceTest pass_through(IInterfaceTest s) + { + return s; + } + } +} diff --git a/test_subclass.py b/test_subclass.py new file mode 100644 index 000000000..5a8e0fb8d --- /dev/null +++ b/test_subclass.py @@ -0,0 +1,116 @@ +# =========================================================================== +# 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. +# =========================================================================== +import clr +clr.AddReference('Python.Test') +clr.AddReference('System') + +import sys, os, string, unittest, types +from Python.Test import TestFunctions, SubClassTest, IInterfaceTest +from System.Collections.Generic import List + +# class that implements the test interface +class InterfaceTestClass(IInterfaceTest): + def foo(self): + return "InterfaceTestClass" + + def bar(self, x, i): + return "/".join([x] * i) + +# class that derives from a class deriving from IInterfaceTest +class DerivedClass(SubClassTest): + + def foo(self): + return "DerivedClass" + + def base_foo(self): + return SubClassTest.foo(self) + + def super_foo(self): + return super(DerivedClass, self).foo() + + def bar(self, x, i): + return "_".join([x] * i) + + def return_list(self): + l = List[str]() + l.Add("A") + l.Add("B") + l.Add("C") + return l + +class SubClassTests(unittest.TestCase): + """Test subclassing managed types""" + + def testBaseClass(self): + """Test base class managed type""" + object = SubClassTest() + self.assertEqual(object.foo(), "foo") + self.assertEqual(TestFunctions.test_foo(object), "foo") + self.assertEqual(object.bar("bar", 2), "bar") + self.assertEqual(TestFunctions.test_bar(object, "bar", 2), "bar") + self.assertEqual(object.not_overriden(), "not_overriden") + self.assertEqual(list(object.return_list()), ["a", "b", "c"]) + self.assertEqual(list(SubClassTest.test_list(object)), ["a", "b", "c"]) + + def testInterface(self): + """Test python classes can derive from C# interfaces""" + object = InterfaceTestClass() + self.assertEqual(object.foo(), "InterfaceTestClass") + self.assertEqual(TestFunctions.test_foo(object), "InterfaceTestClass") + self.assertEqual(object.bar("bar", 2), "bar/bar") + self.assertEqual(TestFunctions.test_bar(object, "bar", 2), "bar/bar") + + x = TestFunctions.pass_through(object) + self.assertEqual(id(x), id(object)) + + def testDerivedClass(self): + """Test python class derived from managed type""" + object = DerivedClass() + self.assertEqual(object.foo(), "DerivedClass") + self.assertEqual(object.base_foo(), "foo") + self.assertEqual(object.super_foo(), "foo") + self.assertEqual(TestFunctions.test_foo(object), "DerivedClass") + self.assertEqual(object.bar("bar", 2), "bar_bar") + self.assertEqual(TestFunctions.test_bar(object, "bar", 2), "bar_bar") + self.assertEqual(object.not_overriden(), "not_overriden") + self.assertEqual(list(object.return_list()), ["A", "B", "C"]) + self.assertEqual(list(SubClassTest.test_list(object)), ["A", "B", "C"]) + + x = TestFunctions.pass_through(object) + self.assertEqual(id(x), id(object)) + + def testCreateInstance(self): + """Test derived instances can be created from managed code""" + object = TestFunctions.create_instance(DerivedClass) + self.assertEqual(object.foo(), "DerivedClass") + self.assertEqual(TestFunctions.test_foo(object), "DerivedClass") + self.assertEqual(object.bar("bar", 2), "bar_bar") + self.assertEqual(TestFunctions.test_bar(object, "bar", 2), "bar_bar") + self.assertEqual(object.not_overriden(), "not_overriden") + + x = TestFunctions.pass_through(object) + self.assertEqual(id(x), id(object)) + + object2 = TestFunctions.create_instance(InterfaceTestClass) + self.assertEqual(object2.foo(), "InterfaceTestClass") + self.assertEqual(TestFunctions.test_foo(object2), "InterfaceTestClass") + self.assertEqual(object2.bar("bar", 2), "bar/bar") + self.assertEqual(TestFunctions.test_bar(object2, "bar", 2), "bar/bar") + + y = TestFunctions.pass_through(object2) + self.assertEqual(id(y), id(object2)) + +def test_suite(): + return unittest.makeSuite(SubClassTests) + +def main(): + unittest.TextTestRunner().run(test_suite()) + +if __name__ == '__main__': + main() diff --git a/tools/geninterop/fake_libc_include/_ansi.h b/tools/geninterop/fake_libc_include/_ansi.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/_ansi.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/_fake_defines.h b/tools/geninterop/fake_libc_include/_fake_defines.h new file mode 100644 index 000000000..2479c665e --- /dev/null +++ b/tools/geninterop/fake_libc_include/_fake_defines.h @@ -0,0 +1,46 @@ +#ifndef _FAKE_DEFINES_H +#define _FAKE_DEFINES_H + +#define NULL 0 +#define BUFSIZ 1024 +#define FOPEN_MAX 20 +#define FILENAME_MAX 1024 + +#ifndef SEEK_SET +#define SEEK_SET 0 /* set file offset to offset */ +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#endif +#ifndef SEEK_END +#define SEEK_END 2 /* set file offset to EOF plus offset */ +#endif + +#define __LITTLE_ENDIAN 1234 +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#define __BIG_ENDIAN 4321 +#define BIG_ENDIAN __BIG_ENDIAN +#define __BYTE_ORDER __LITTLE_ENDIAN +#define BYTE_ORDER __BYTE_ORDER + +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS 0 + +#define UCHAR_MAX 255 +#define USHRT_MAX 65535 +#define UINT_MAX 4294967295U +#define RAND_MAX 32767 +#define INT_MAX 32767 + +/* C99 stdbool.h defines */ +#define __bool_true_false_are_defined 1 +#define false 0 +#define true 1 + +/* va_arg macros and type*/ +typedef int va_list; +#define va_start(_ap, _type) __builtin_va_start((_ap)) +#define va_arg(_ap, _type) __builtin_va_arg((_ap)) +#define va_end(_list) + +#endif diff --git a/tools/geninterop/fake_libc_include/_fake_typedefs.h b/tools/geninterop/fake_libc_include/_fake_typedefs.h new file mode 100644 index 000000000..14553219e --- /dev/null +++ b/tools/geninterop/fake_libc_include/_fake_typedefs.h @@ -0,0 +1,155 @@ +#ifndef _FAKE_TYPEDEFS_H +#define _FAKE_TYPEDEFS_H + +typedef int size_t; +typedef int __builtin_va_list; +typedef int __gnuc_va_list; +typedef int __int8_t; +typedef int __uint8_t; +typedef int __int16_t; +typedef int __uint16_t; +typedef int __int_least16_t; +typedef int __uint_least16_t; +typedef int __int32_t; +typedef int __uint32_t; +typedef int __int64_t; +typedef int __uint64_t; +typedef int __int_least32_t; +typedef int __uint_least32_t; +typedef int __s8; +typedef int __u8; +typedef int __s16; +typedef int __u16; +typedef int __s32; +typedef int __u32; +typedef int __s64; +typedef int __u64; +typedef int _LOCK_T; +typedef int _LOCK_RECURSIVE_T; +typedef int _off_t; +typedef int __dev_t; +typedef int __uid_t; +typedef int __gid_t; +typedef int _off64_t; +typedef int _fpos_t; +typedef int _ssize_t; +typedef int wint_t; +typedef int _mbstate_t; +typedef int _flock_t; +typedef int _iconv_t; +typedef int __ULong; +typedef int __FILE; +typedef int ptrdiff_t; +typedef int wchar_t; +typedef int __off_t; +typedef int __pid_t; +typedef int __loff_t; +typedef int u_char; +typedef int u_short; +typedef int u_int; +typedef int u_long; +typedef int ushort; +typedef int uint; +typedef int clock_t; +typedef int time_t; +typedef int daddr_t; +typedef int caddr_t; +typedef int ino_t; +typedef int off_t; +typedef int dev_t; +typedef int uid_t; +typedef int gid_t; +typedef int pid_t; +typedef int key_t; +typedef int ssize_t; +typedef int mode_t; +typedef int nlink_t; +typedef int fd_mask; +typedef int _types_fd_set; +typedef int clockid_t; +typedef int timer_t; +typedef int useconds_t; +typedef int suseconds_t; +typedef int FILE; +typedef int fpos_t; +typedef int cookie_read_function_t; +typedef int cookie_write_function_t; +typedef int cookie_seek_function_t; +typedef int cookie_close_function_t; +typedef int cookie_io_functions_t; +typedef int div_t; +typedef int ldiv_t; +typedef int lldiv_t; +typedef int sigset_t; +typedef int __sigset_t; +typedef int _sig_func_ptr; +typedef int sig_atomic_t; +typedef int __tzrule_type; +typedef int __tzinfo_type; +typedef int mbstate_t; +typedef int sem_t; +typedef int pthread_t; +typedef int pthread_attr_t; +typedef int pthread_mutex_t; +typedef int pthread_mutexattr_t; +typedef int pthread_cond_t; +typedef int pthread_condattr_t; +typedef int pthread_key_t; +typedef int pthread_once_t; +typedef int pthread_rwlock_t; +typedef int pthread_rwlockattr_t; +typedef int pthread_spinlock_t; +typedef int pthread_barrier_t; +typedef int pthread_barrierattr_t; +typedef int jmp_buf; +typedef int rlim_t; +typedef int sa_family_t; +typedef int sigjmp_buf; +typedef int stack_t; +typedef int siginfo_t; +typedef int z_stream; + +/* C99 exact-width integer types */ +typedef int int8_t; +typedef int uint8_t; +typedef int int16_t; +typedef int uint16_t; +typedef int int32_t; +typedef int uint32_t; +typedef int int64_t; +typedef int uint64_t; + +/* C99 minimum-width integer types */ +typedef int int_least8_t; +typedef int uint_least8_t; +typedef int int_least16_t; +typedef int uint_least16_t; +typedef int int_least32_t; +typedef int uint_least32_t; +typedef int int_least64_t; +typedef int uint_least64_t; + +/* C99 fastest minimum-width integer types */ +typedef int int_fast8_t; +typedef int uint_fast8_t; +typedef int int_fast16_t; +typedef int uint_fast16_t; +typedef int int_fast32_t; +typedef int uint_fast32_t; +typedef int int_fast64_t; +typedef int uint_fast64_t; + +/* C99 integer types capable of holding object pointers */ +typedef int intptr_t; +typedef int uintptr_t; + +/* C99 greatest-width integer types */ +typedef int intmax_t; +typedef int uintmax_t; + +/* C99 stdbool.h bool type. _Bool is built-in in C99 */ +typedef _Bool bool; + +typedef int va_list; + +#endif diff --git a/tools/geninterop/fake_libc_include/_syslist.h b/tools/geninterop/fake_libc_include/_syslist.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/_syslist.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/alloca.h b/tools/geninterop/fake_libc_include/alloca.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/alloca.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/ar.h b/tools/geninterop/fake_libc_include/ar.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/ar.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/argz.h b/tools/geninterop/fake_libc_include/argz.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/argz.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/arpa/inet.h b/tools/geninterop/fake_libc_include/arpa/inet.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/arpa/inet.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/asm-generic/int-ll64.h b/tools/geninterop/fake_libc_include/asm-generic/int-ll64.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/asm-generic/int-ll64.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/assert.h b/tools/geninterop/fake_libc_include/assert.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/assert.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/complex.h b/tools/geninterop/fake_libc_include/complex.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/complex.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/ctype.h b/tools/geninterop/fake_libc_include/ctype.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/ctype.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/dirent.h b/tools/geninterop/fake_libc_include/dirent.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/dirent.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/dlfcn.h b/tools/geninterop/fake_libc_include/dlfcn.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/dlfcn.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/endian.h b/tools/geninterop/fake_libc_include/endian.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/endian.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/envz.h b/tools/geninterop/fake_libc_include/envz.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/envz.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/errno.h b/tools/geninterop/fake_libc_include/errno.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/errno.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/fastmath.h b/tools/geninterop/fake_libc_include/fastmath.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/fastmath.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/fcntl.h b/tools/geninterop/fake_libc_include/fcntl.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/fcntl.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/features.h b/tools/geninterop/fake_libc_include/features.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/features.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/fenv.h b/tools/geninterop/fake_libc_include/fenv.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/fenv.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/float.h b/tools/geninterop/fake_libc_include/float.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/float.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/getopt.h b/tools/geninterop/fake_libc_include/getopt.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/getopt.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/grp.h b/tools/geninterop/fake_libc_include/grp.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/grp.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/iconv.h b/tools/geninterop/fake_libc_include/iconv.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/iconv.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/ieeefp.h b/tools/geninterop/fake_libc_include/ieeefp.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/ieeefp.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/inttypes.h b/tools/geninterop/fake_libc_include/inttypes.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/inttypes.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/io.h b/tools/geninterop/fake_libc_include/io.h new file mode 100644 index 000000000..e69de29bb diff --git a/tools/geninterop/fake_libc_include/iso646.h b/tools/geninterop/fake_libc_include/iso646.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/iso646.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/langinfo.h b/tools/geninterop/fake_libc_include/langinfo.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/langinfo.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/libgen.h b/tools/geninterop/fake_libc_include/libgen.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/libgen.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/libintl.h b/tools/geninterop/fake_libc_include/libintl.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/libintl.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/limits.h b/tools/geninterop/fake_libc_include/limits.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/limits.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/linux/socket.h b/tools/geninterop/fake_libc_include/linux/socket.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/linux/socket.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/linux/version.h b/tools/geninterop/fake_libc_include/linux/version.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/linux/version.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/locale.h b/tools/geninterop/fake_libc_include/locale.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/locale.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/malloc.h b/tools/geninterop/fake_libc_include/malloc.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/malloc.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/math.h b/tools/geninterop/fake_libc_include/math.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/math.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/netdb.h b/tools/geninterop/fake_libc_include/netdb.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/netdb.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/netinet/in.h b/tools/geninterop/fake_libc_include/netinet/in.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/netinet/in.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/netinet/tcp.h b/tools/geninterop/fake_libc_include/netinet/tcp.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/netinet/tcp.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/newlib.h b/tools/geninterop/fake_libc_include/newlib.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/newlib.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/openssl/err.h b/tools/geninterop/fake_libc_include/openssl/err.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/openssl/err.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/openssl/evp.h b/tools/geninterop/fake_libc_include/openssl/evp.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/openssl/evp.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/openssl/hmac.h b/tools/geninterop/fake_libc_include/openssl/hmac.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/openssl/hmac.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/openssl/ssl.h b/tools/geninterop/fake_libc_include/openssl/ssl.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/openssl/ssl.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/openssl/x509v3.h b/tools/geninterop/fake_libc_include/openssl/x509v3.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/openssl/x509v3.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/paths.h b/tools/geninterop/fake_libc_include/paths.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/paths.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/process.h b/tools/geninterop/fake_libc_include/process.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/process.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/pthread.h b/tools/geninterop/fake_libc_include/pthread.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/pthread.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/pwd.h b/tools/geninterop/fake_libc_include/pwd.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/pwd.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/reent.h b/tools/geninterop/fake_libc_include/reent.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/reent.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/regdef.h b/tools/geninterop/fake_libc_include/regdef.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/regdef.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/regex.h b/tools/geninterop/fake_libc_include/regex.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/regex.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sched.h b/tools/geninterop/fake_libc_include/sched.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sched.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/search.h b/tools/geninterop/fake_libc_include/search.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/search.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/semaphore.h b/tools/geninterop/fake_libc_include/semaphore.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/semaphore.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/setjmp.h b/tools/geninterop/fake_libc_include/setjmp.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/setjmp.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/signal.h b/tools/geninterop/fake_libc_include/signal.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/signal.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/stdarg.h b/tools/geninterop/fake_libc_include/stdarg.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/stdarg.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/stdbool.h b/tools/geninterop/fake_libc_include/stdbool.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/stdbool.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/stddef.h b/tools/geninterop/fake_libc_include/stddef.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/stddef.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/stdint.h b/tools/geninterop/fake_libc_include/stdint.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/stdint.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/stdio.h b/tools/geninterop/fake_libc_include/stdio.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/stdio.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/stdlib.h b/tools/geninterop/fake_libc_include/stdlib.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/stdlib.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/string.h b/tools/geninterop/fake_libc_include/string.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/string.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/ioctl.h b/tools/geninterop/fake_libc_include/sys/ioctl.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/ioctl.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/mman.h b/tools/geninterop/fake_libc_include/sys/mman.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/mman.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/poll.h b/tools/geninterop/fake_libc_include/sys/poll.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/poll.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/resource.h b/tools/geninterop/fake_libc_include/sys/resource.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/resource.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/select.h b/tools/geninterop/fake_libc_include/sys/select.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/select.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/socket.h b/tools/geninterop/fake_libc_include/sys/socket.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/socket.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/stat.h b/tools/geninterop/fake_libc_include/sys/stat.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/stat.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/sysctl.h b/tools/geninterop/fake_libc_include/sys/sysctl.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/sysctl.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/time.h b/tools/geninterop/fake_libc_include/sys/time.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/time.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/types.h b/tools/geninterop/fake_libc_include/sys/types.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/types.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/uio.h b/tools/geninterop/fake_libc_include/sys/uio.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/uio.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/un.h b/tools/geninterop/fake_libc_include/sys/un.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/un.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/utsname.h b/tools/geninterop/fake_libc_include/sys/utsname.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/utsname.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/sys/wait.h b/tools/geninterop/fake_libc_include/sys/wait.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/sys/wait.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/syslog.h b/tools/geninterop/fake_libc_include/syslog.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/syslog.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/tar.h b/tools/geninterop/fake_libc_include/tar.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/tar.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/termios.h b/tools/geninterop/fake_libc_include/termios.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/termios.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/tgmath.h b/tools/geninterop/fake_libc_include/tgmath.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/tgmath.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/time.h b/tools/geninterop/fake_libc_include/time.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/time.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/unctrl.h b/tools/geninterop/fake_libc_include/unctrl.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/unctrl.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/unistd.h b/tools/geninterop/fake_libc_include/unistd.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/unistd.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/utime.h b/tools/geninterop/fake_libc_include/utime.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/utime.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/utmp.h b/tools/geninterop/fake_libc_include/utmp.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/utmp.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/wchar.h b/tools/geninterop/fake_libc_include/wchar.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/wchar.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/wctype.h b/tools/geninterop/fake_libc_include/wctype.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/wctype.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/fake_libc_include/zlib.h b/tools/geninterop/fake_libc_include/zlib.h new file mode 100644 index 000000000..f952c1d67 --- /dev/null +++ b/tools/geninterop/fake_libc_include/zlib.h @@ -0,0 +1,2 @@ +#include "_fake_defines.h" +#include "_fake_typedefs.h" diff --git a/tools/geninterop/geninterop.py b/tools/geninterop/geninterop.py new file mode 100644 index 000000000..2d4e15bc9 --- /dev/null +++ b/tools/geninterop/geninterop.py @@ -0,0 +1,292 @@ +""" +TypeOffset is a C# class that mirrors the in-memory layout of heap +allocated Python objects. + +This script parses the Python C headers and outputs the TypeOffset +C# class. + +Requirements: + - pycparser + - clang +""" +from distutils.sysconfig import get_config_var +from subprocess import Popen, CalledProcessError, PIPE +from pycparser import c_parser, c_ast +import logging +import sys +import os + +_log = logging.getLogger() +logging.basicConfig(level=logging.DEBUG) + + +# rename some members from their C name when generating the C# +_typeoffset_member_renames = { + "ht_name": "name", + "ht_qualname": "qualname" +} + + +class AstParser(object): + """Walk an AST and determine the members of all structs""" + + def __init__(self): + self.__typedefs = {} + self.__typedecls = {} + self.__structs = {} + self.__struct_stack = [] + self.__struct_members_stack = [] + self.__ptr_decl_depth = 0 + self.__struct_members = {} + + def get_struct_members(self, name): + """return a list of (name, type) of struct members""" + if name in self.__typedefs: + node = self.__get_leaf_node(self.__typedefs[name]) + name = node.name + if name not in self.__struct_members: + raise Exception("Unknown struct '%s'" % name) + return self.__struct_members[name] + + def visit(self, node): + if isinstance(node, c_ast.FileAST): + self.visit_ast(node) + elif isinstance(node, c_ast.Typedef): + self.visit_typedef(node) + elif isinstance(node, c_ast.TypeDecl): + self.visit_typedecl(node) + elif isinstance(node, c_ast.Struct): + self.visit_struct(node) + elif isinstance(node, c_ast.Decl): + self.visit_decl(node) + elif isinstance(node, c_ast.PtrDecl): + self.visit_ptrdecl(node) + elif isinstance(node, c_ast.IdentifierType): + self.visit_identifier(node) + + def visit_ast(self, ast): + for name, node in ast.children(): + self.visit(node) + + def visit_typedef(self, typedef): + self.__typedefs[typedef.name] = typedef.type + self.visit(typedef.type) + + def visit_typedecl(self, typedecl): + self.visit(typedecl.type) + + def visit_struct(self, struct): + self.__structs[self.__get_struct_name(struct)] = struct + if struct.decls: + # recurse into the struct + self.__struct_stack.insert(0, struct) + for decl in struct.decls: + self.__struct_members_stack.insert(0, decl.name) + self.visit(decl) + self.__struct_members_stack.pop(0) + self.__struct_stack.pop(0) + elif self.__ptr_decl_depth: + # the struct is empty, but add it as a member to the current struct + # as the current member maybe a pointer to it. + self.__add_struct_member(struct.name) + + def visit_decl(self, decl): + self.visit(decl.type) + + def visit_ptrdecl(self, ptrdecl): + self.__ptr_decl_depth += 1 + self.visit(ptrdecl.type) + self.__ptr_decl_depth -= 1 + + def visit_identifier(self, identifier): + type_name = " ".join(identifier.names) + self.__add_struct_member(type_name) + + def __add_struct_member(self, type_name): + if not (self.__struct_stack and self.__struct_members_stack): + return + + # add member to current struct + current_struct = self.__struct_stack[0] + member_name = self.__struct_members_stack[0] + struct_members = self.__struct_members.setdefault(self.__get_struct_name(current_struct), []) + + # get the node associated with this type + node = None + if type_name in self.__typedefs: + node = self.__get_leaf_node(self.__typedefs[type_name]) + elif type_name in self.__structs: + node = self.__structs[type_name] + + # If it's a struct (and not a pointer to a struct) expand it into the current struct definition + if not self.__ptr_decl_depth and isinstance(node, c_ast.Struct): + for decl in node.decls or []: + self.__struct_members_stack.insert(0, decl.name) + self.visit(decl) + self.__struct_members_stack.pop(0) + else: + # otherwise add it as a single member + struct_members.append((member_name, type_name)) + + + def __get_leaf_node(self, node): + if isinstance(node, c_ast.Typedef): + return self.__get_leaf_node(node.type) + if isinstance(node, c_ast.TypeDecl): + return self.__get_leaf_node(node.type) + return node + + def __get_struct_name(self, node): + return node.name or "_struct_%d" % id(node) + + +def check_output(*popenargs, **kwargs): + """subprocess.check_output from python 2.7. + Added here to support building for earlier versions + of Python. + """ + process = Popen(stdout=PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise CalledProcessError(retcode, cmd) + if sys.version_info[0] > 2: + return output.decode("ascii") + return output + + +def preprocess_python_headers(): + """Return Python.h pre-processed, ready for parsing. + Requires clang. + """ + fake_libc_include = os.path.join(os.path.dirname(__file__), "fake_libc_include") + include_dirs = [fake_libc_include] + + include_py = get_config_var("INCLUDEPY") + include_dirs.append(include_py) + + defines = [ + "-D", "__attribute__(x)=", + "-D", "__inline__=inline", + "-D", "__asm__=;#pragma asm", + "-D", "__int64=long long" + ] + + if hasattr(sys, "abiflags"): + if "d" in sys.abiflags: + defines.extend(("-D", "PYTHON_WITH_PYDEBUG")) + if "m" in sys.abiflags: + defines.extend(("-D", "PYTHON_WITH_PYMALLOC")) + if "u" in sys.abiflags: + defines.extend(("-D", "PYTHON_WITH_WIDE_UNICODE")) + + python_h = os.path.join(include_py, "Python.h") + cmd = ["clang", "-I"] + include_dirs + defines + ["-E", python_h] + + # normalize as the parser doesn't like windows line endings. + lines = [] + for line in check_output(cmd).splitlines(): + if line.startswith("#"): + line = line.replace("\\", "/") + lines.append(line) + return "\n".join(lines) + + +def gen_interop_code(members): + """Generate the TypeOffset C# class""" + + defines = [ + "PYTHON%d%s" % (sys.version_info[:2]) + ] + + if hasattr(sys, "abiflags"): + if "d" in sys.abiflags: + defines.append("PYTHON_WITH_PYDEBUG") + if "m" in sys.abiflags: + defines.append("PYTHON_WITH_PYMALLOC") + if "u" in sys.abiflags: + defines.append("PYTHON_WITH_WIDE_UNICODE") + + class_definition = """ +// Auto-generated by %s. +// DOT NOT MODIFIY BY HAND. +// ========================================================================== +// 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 (%s) +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Text; + +namespace Python.Runtime { + + [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; + } + + // Auto-generated from PyHeapTypeObject in Python.h +""" % (os.path.basename(__file__), " && ".join(defines)) + + # All the members are sizeof(void*) so we don't need to do any + # extra work to determine the size based on the type. + for name, tpy in members: + name = _typeoffset_member_renames.get(name, name) + class_definition += " public static int %s = 0;\n" % name + + class_definition += """ + /* here are optional user slots, followed by the members. */ + public static int members = 0; + } +} +#endif +""" + return class_definition + + +def main(): + # preprocess Python.h and build the AST + python_h = preprocess_python_headers() + parser = c_parser.CParser() + ast = parser.parse(python_h) + + # extract struct members from the AST + ast_parser = AstParser() + ast_parser.visit(ast) + + # generate the C# code + members = ast_parser.get_struct_members("PyHeapTypeObject") + interop_cs = gen_interop_code(members) + + if len(sys.argv) > 1: + with open(sys.argv[1], "wt") as fh: + fh.write(interop_cs) + else: + print(interop_cs) + + +if __name__ == "__main__": + sys.exit(main()) +