diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 30af44995..ac1d31324 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.3.0 +current_version = 2.4.0.dev0 parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\.(?P[a-z]+)(?P\d+))? serialize = {major}.{minor}.{patch}.{release}{dev} diff --git a/.mention-bot b/.mention-bot new file mode 100644 index 000000000..f07fa548e --- /dev/null +++ b/.mention-bot @@ -0,0 +1,35 @@ +{ + "maxReviewers": 5, + "numFilesToCheck": 10, + "message": "@pullRequester, thanks! @reviewers, please review this.", + "alwaysNotifyForPaths": [ + { + "name": "ghuser", + "files": ["src/js/**/*.js"], + "skipTeamPrs": false + } + ], + "fallbackNotifyForPaths": [ + { + "name": "ghuser", + "files": ["src/js/**/*.js"], + "skipTeamPrs": false + } + ], + "findPotentialReviewers": true, + "fileBlacklist": ["*.md"], + "userBlacklist": [], + "userBlacklistForPR": [], + "requiredOrgs": [], + "actions": ["opened"], + "skipAlreadyAssignedPR": false, + "skipAlreadyMentionedPR": false, + "assignToReviewer": false, + "createReviewRequest": false, + "createComment": true, + "skipTitle": "", + "withLabel": "", + "delayed": false, + "delayedUntil": "3d", + "skipCollaboratorPR": false +} diff --git a/.travis.yml b/.travis.yml index bf336dc9f..3ab0e900f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,73 @@ +dist: trusty sudo: false - language: python -python: - - 2.7 - - 3.3 - - 3.4 - - 3.5 - - 3.6 - - 3.7-dev - + matrix: + include: +# --------------------- XPLAT builds ------------------------ + - python: 2.7 + env: &xplat-env + - BUILD_OPTS=--xplat + - NUNIT_PATH=~/.nuget/packages/nunit.consolerunner/3.*/tools/nunit3-console.exe + addons: &xplat-addons + apt: + sources: + - sourceline: deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-trusty-prod trusty main + key_url: https://packages.microsoft.com/keys/microsoft.asc + - sourceline: deb http://download.mono-project.com/repo/debian trusty/snapshots/5.2.0.224 main + key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF + packages: + - mono-devel + - ca-certificates-mono + - dotnet-hostfxr-2.0.0 + - dotnet-runtime-2.0.0 + - dotnet-sdk-2.0.0 + - python: 3.3 + env: *xplat-env + addons: *xplat-addons + + - python: 3.4 + env: *xplat-env + addons: *xplat-addons + + - python: 3.5 + env: *xplat-env + addons: *xplat-addons + + - python: 3.6 + env: *xplat-env + addons: *xplat-addons + - python: "3.7-dev" + env: *xplat-env + addons: *xplat-addons + +# --------------------- Classic builds ------------------------ + - python: 2.7 + env: &classic-env + - BUILD_OPTS= + - NUNIT_PATH=./packages/NUnit.*/tools/nunit3-console.exe + + - python: 3.3 + env: *classic-env + + - python: 3.4 + env: *classic-env + + - python: 3.5 + env: *classic-env + + - python: 3.6 + env: *classic-env + + - python: "3.7-dev" + env: *classic-env + allow_failures: - - python: 3.7-dev + - python: "3.7-dev" + env: *xplat-env + + - python: "3.7-dev" + env: *classic-env env: global: @@ -23,8 +79,8 @@ env: addons: apt: sources: - - mono - - mono-libtiff-compat + - sourceline: deb http://download.mono-project.com/repo/debian trusty/snapshots/5.2.0.224 main + key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF packages: - mono-devel - ca-certificates-mono @@ -35,12 +91,14 @@ before_install: - export LD_LIBRARY_PATH=$PY_LIBDIR:$LD_LIBRARY_PATH install: + - pip install --upgrade setuptools # TEMP - due to setuptools 36.2.0 bug - pip install --upgrade -r requirements.txt - - coverage run setup.py install + - coverage run setup.py install $BUILD_OPTS script: - python -m pytest - - mono ./packages/NUnit.*/tools/nunit3-console.exe src/embed_tests/bin/Python.EmbeddingTest.dll + - mono $NUNIT_PATH src/embed_tests/bin/Python.EmbeddingTest.dll + - if [[ $BUILD_OPTS == --xplat ]]; then dotnet src/embed_tests/bin/netcoreapp2.0_publish/Python.EmbeddingTest.dll; fi after_script: # Uncomment if need to geninterop, ie. py37 final diff --git a/AUTHORS.md b/AUTHORS.md index 09358586e..78bb25f9e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -16,8 +16,10 @@ - Bradley Friedman ([@leith-bartrich](https://github.com/leith-bartrich)) - Christian Heimes ([@tiran](https://github.com/tiran)) - Christoph Gohlke ([@cgohlke](https://github.com/cgohlke)) +- Christopher Pow ([@christopherpow](https://github.com/christopherpow)) - Daniel Fernandez ([@fdanny](https://github.com/fdanny)) - Daniel Santana ([@dgsantana](https://github.com/dgsantana)) +- Dave Hirschfeld ([@dhirschfeld](https://github.com/dhirschfeld)) - David Lechner ([@dlech](https://github.com/dlech)) - Dmitriy Se ([@dmitriyse](https://github.com/dmitriyse)) - He-chien Tsai ([@t3476](https://github.com/t3476)) @@ -25,12 +27,15 @@ - Joe Frayne ([@jfrayne](https://github.com/jfrayne)) - John Burnett ([@johnburnett](https://github.com/johnburnett)) - Luke Stratman ([@lstratman](https://github.com/lstratman)) +- Konstantin Posudevskiy ([@konstantin-posudevskiy](https://github.com/konstantin-posudevskiy)) - Matthias Dittrich ([@matthid](https://github.com/matthid)) - Patrick Stewart ([@patstew](https://github.com/patstew)) - Raphael Nestler ([@rnestler](https://github.com/rnestler)) +- Rickard Holmberg ([@rickardraysearch](https://github.com/rickardraysearch)) - Sam Winstanley ([@swinstanley](https://github.com/swinstanley)) - Sean Freitag ([@cowboygneox](https://github.com/cowboygneox)) - Serge Weinstock ([@sweinst](https://github.com/sweinst)) +- Ville M. Vainio ([@vivainio](https://github.com/vivainio)) - Virgil Dupras ([@hsoft](https://github.com/hsoft)) - Wenguang Yang ([@yagweb](https://github.com/yagweb)) - Xavier Dupré ([@sdpython](https://github.com/sdpython)) @@ -40,3 +45,5 @@ - ([@rico-chet](https://github.com/rico-chet)) - ([@rmadsen-ks](https://github.com/rmadsen-ks)) - ([@stonebig](https://github.com/stonebig)) +- ([@testrunner123](https://github.com/testrunner123)) + diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c7de23c7..7d408a8d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,31 @@ This project adheres to [Semantic Versioning][]. This document follows the conventions laid out in [Keep a CHANGELOG][]. +## [unreleased][] + +### Added +- Added support for embedding python into dotnet core 2.0 (NetStandard 2.0) +- Added new build system (pythonnet.15.sln) based on dotnetcore-sdk/xplat(crossplatform msbuild). + Currently there two side-by-side build systems that produces the same output (net40) from the same sources. + After a some transition time, current (mono/ msbuild 14.0) build system will be removed. +- NUnit upgraded to 3.7 (eliminates travis-ci random bug) +- Added `clr.GetClrType` (#432, #433) +- Allowed passing `None` for nullable args (#460) +- Added keyword arguments based on C# syntax for calling CPython methods (#461) + +### Changed + +### Fixed + +- Fixed Visual Studio 2017 compat (#434) for setup.py +- Fixed crash on exit of the Python interpreter if a python class + derived from a .NET class has a `__namespace__` or `__assembly__` + attribute (#481) +- Fixed conversion of 'float' and 'double' values (#486) +- Fixed 'clrmethod' for python 2 (#492) +- Fixed double calling of constructor when deriving from .NET class (#495) + + ## [2.3.0][] - 2017-03-11 ### Added diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 000000000..719fbc83c --- /dev/null +++ b/NuGet.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 2794298a6..89dcf2206 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # pythonnet - Python for .NET +[![Join the chat at https://gitter.im/pythonnet/pythonnet](https://badges.gitter.im/pythonnet/pythonnet.svg)](https://gitter.im/pythonnet/pythonnet?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![appveyor shield][]](https://ci.appveyor.com/project/pythonnet/pythonnet/branch/master) [![travis shield][]](https://travis-ci.org/pythonnet/pythonnet) [![codecov shield][]](https://codecov.io/github/pythonnet/pythonnet) @@ -42,7 +44,7 @@ from System.Windows.Forms import Form 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))` +- Use `mod.func(args, Py.kw("keywordargname", keywordargvalue))` or `mod.func(args, keywordargname=keywordargvalue)` to apply keyword arguments. - All python objects should be declared as `dynamic` type. - Mathematical operations involving python and literal/managed types must @@ -63,15 +65,14 @@ static void Main(string[] args) double c = np.cos(5) + sin(5); Console.WriteLine(c); - /* this block is temporarily disabled due to regression #249 + dynamic a = np.array(new List { 1, 2, 3 }); Console.WriteLine(a.dtype); - dynamic b = np.array(new List { 6, 5, 4 }, Py.kw("dtype", np.int32)); + dynamic b = np.array(new List { 6, 5, 4 }, dtype: np.int32); Console.WriteLine(b.dtype); Console.WriteLine(a * b); - */ Console.ReadKey(); } } @@ -88,6 +89,10 @@ int32 [ 6. 10. 12.] ``` +Information on installation, FAQ, troubleshooting, debugging, and projects using pythonnet can be found in the Wiki: + +https://github.com/pythonnet/pythonnet/wiki + [appveyor shield]: https://img.shields.io/appveyor/ci/pythonnet/pythonnet/master.svg?label=AppVeyor [codecov shield]: https://img.shields.io/codecov/c/github/pythonnet/pythonnet/master.svg?label=Codecov diff --git a/appveyor.yml b/appveyor.yml index c108801e7..8178f173f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,9 @@ version: '{branch}-{build}' build: off +image: + - Visual Studio 2017 + platform: - x86 - x64 @@ -12,6 +15,16 @@ environment: CODECOV_ENV: PYTHON_VERSION, PLATFORM matrix: + - PYTHON_VERSION: 2.7 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.3 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.4 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.5 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.6 + BUILD_OPTS: --xplat - PYTHON_VERSION: 2.7 - PYTHON_VERSION: 3.3 - PYTHON_VERSION: 3.4 @@ -37,7 +50,7 @@ build_script: # Create clean `sdist`. Only used for releases - python setup.py --quiet sdist # Build `wheel` with coverage of `setup.py` - - coverage run setup.py bdist_wheel + - coverage run setup.py bdist_wheel %BUILD_OPTS% test_script: - pip install --no-index --find-links=.\dist\ pythonnet @@ -47,11 +60,11 @@ test_script: on_finish: # Temporary disable multiple upload due to codecov limit of 20 per commit. # https://docs.codecov.io/blog/week-8-2017 - # - coverage xml -i + - coverage xml -i # - codecov --file coverage.xml --flags setup_windows # - codecov --file py.coverage --flags python_tests # - codecov --file cs.coverage --flags embedded_tests - - codecov --flags setup_windows + - codecov --file py.coverage cs.coverage coverage.xml --flags setup_windows artifacts: - path: dist\* diff --git a/ci/appveyor_build_recipe.ps1 b/ci/appveyor_build_recipe.ps1 index 52520f4c2..84e0bc7c6 100644 --- a/ci/appveyor_build_recipe.ps1 +++ b/ci/appveyor_build_recipe.ps1 @@ -11,7 +11,7 @@ if ($env:PLATFORM -eq "x86"){ $env:CONDA_BLD = "$env:CONDA_BLD" + "-x64" } -if ($env:APPVEYOR_PULL_REQUEST_NUMBER -or $env:FORCE_CONDA_BUILD -eq "True") { +if ($env:APPVEYOR_PULL_REQUEST_NUMBER -or $env:APPVEYOR_REPO_TAG_NAME -or $env:FORCE_CONDA_BUILD -eq "True") { # Update PATH, and keep a copy to restore at end of this PowerShell script $old_path = $env:path $env:path = "$env:CONDA_BLD;$env:CONDA_BLD\Scripts;" + $env:path diff --git a/ci/appveyor_run_tests.ps1 b/ci/appveyor_run_tests.ps1 index 4245d1577..b45440fbe 100644 --- a/ci/appveyor_run_tests.ps1 +++ b/ci/appveyor_run_tests.ps1 @@ -11,7 +11,12 @@ if ($FALSE -and $env:PLATFORM -eq "x86"){ # Executable paths for OpenCover # Note if OpenCover fails, it won't affect the exit codes. $OPENCOVER = Resolve-Path .\packages\OpenCover.*\tools\OpenCover.Console.exe -$CS_RUNNER = Resolve-Path .\packages\NUnit.*\tools\"$CS_RUNNER".exe +if ($env:BUILD_OPTS -eq "--xplat"){ + $CS_RUNNER = Resolve-Path $env:USERPROFILE\.nuget\packages\nunit.consolerunner\*\tools\"$CS_RUNNER".exe +} +else{ + $CS_RUNNER = Resolve-Path .\packages\NUnit.*\tools\"$CS_RUNNER".exe +} $PY = Get-Command python # Can't use ".\build\*\Python.EmbeddingTest.dll". Missing framework files. @@ -39,6 +44,23 @@ if ($CS_STATUS -ne 0) { Write-Host "Embedded tests failed" -ForegroundColor "Red" } +if ($env:BUILD_OPTS -eq "--xplat"){ + if ($env:PLATFORM -eq "x64") { + $DOTNET_CMD = "dotnet" + } + else{ + $DOTNET_CMD = "c:\Program Files (x86)\dotnet\dotnet" + } + + # Run Embedded tests for netcoreapp2.0 (OpenCover currently does not supports dotnet core) + Write-Host ("Starting embedded tests for netcoreapp2.0") -ForegroundColor "Green" + &$DOTNET_CMD .\src\embed_tests\bin\netcoreapp2.0_publish\Python.EmbeddingTest.dll + $CS_STATUS = $LastExitCode + if ($CS_STATUS -ne 0) { + Write-Host "Embedded tests for netcoreapp2.0 failed" -ForegroundColor "Red" + } +} + # Set exit code to fail if either Python or Embedded tests failed if ($PYTHON_STATUS -ne 0 -or $CS_STATUS -ne 0) { Write-Host "Tests failed" -ForegroundColor "Red" diff --git a/conda.recipe/bld.bat b/conda.recipe/bld.bat index 1495f877d..27b55f2de 100644 --- a/conda.recipe/bld.bat +++ b/conda.recipe/bld.bat @@ -1,6 +1,6 @@ :: build it :: set path to modern MSBuild -set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH% +set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;C:\Program Files (x86)\MSBuild\15.0\Bin;%PATH% %PYTHON% setup.py install diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index 98602481f..3641185bb 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -1,6 +1,6 @@ package: name: pythonnet - version: "2.3.0" + version: "2.4.0.dev0" build: skip: True # [not win] diff --git a/demo/DynamicGrid.py b/demo/DynamicGrid.py new file mode 100644 index 000000000..951a6c248 --- /dev/null +++ b/demo/DynamicGrid.py @@ -0,0 +1,23 @@ +import clr +import sys +if sys.platform.lower() not in ['cli','win32']: + print("only windows is supported for wpf") +clr.AddReference(r"wpf\PresentationFramework") +from System.IO import StreamReader +from System.Windows.Markup import XamlReader +from System.Threading import Thread, ThreadStart, ApartmentState +from System.Windows import Application, Window + + +class MyWindow(Window): + def __init__(self): + stream = StreamReader("DynamicGrid.xaml") + window = XamlReader.Load(stream.BaseStream) + Application().Run(window) + + +if __name__ == '__main__': + thread = Thread(ThreadStart(MyWindow)) + thread.SetApartmentState(ApartmentState.STA) + thread.Start() + thread.Join() diff --git a/demo/DynamicGrid.xaml b/demo/DynamicGrid.xaml new file mode 100644 index 000000000..3c82eb16d --- /dev/null +++ b/demo/DynamicGrid.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/pythonnet.15.sln b/pythonnet.15.sln new file mode 100644 index 000000000..f2015e480 --- /dev/null +++ b/pythonnet.15.sln @@ -0,0 +1,194 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Runtime.15", "src/runtime/Python.Runtime.15.csproj", "{2759F4FF-716B-4828-916F-50FA86613DFC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.EmbeddingTest.15", "src/embed_tests/Python.EmbeddingTest.15.csproj", "{66B8D01A-9906-452A-B09E-BF75EA76468F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "clrmodule.15", "src/clrmodule/clrmodule.15.csproj", "{E08678D4-9A52-4AD5-B63D-8EBC7399981B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.15", "src/console/Console.15.csproj", "{CDAD305F-8E72-492C-A314-64CF58D472A0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Test.15", "src/testing/Python.Test.15.csproj", "{F94B547A-E97E-4500-8D53-B4D64D076E5F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + DebugMono|x64 = DebugMono|x64 + DebugMono|x86 = DebugMono|x86 + DebugMonoPY3|x64 = DebugMonoPY3|x64 + DebugMonoPY3|x86 = DebugMonoPY3|x86 + DebugWin|x64 = DebugWin|x64 + DebugWin|x86 = DebugWin|x86 + DebugWinPY3|x64 = DebugWinPY3|x64 + DebugWinPY3|x86 = DebugWinPY3|x86 + ReleaseMono|x64 = ReleaseMono|x64 + ReleaseMono|x86 = ReleaseMono|x86 + ReleaseMonoPY3|x64 = ReleaseMonoPY3|x64 + ReleaseMonoPY3|x86 = ReleaseMonoPY3|x86 + ReleaseWin|x64 = ReleaseWin|x64 + ReleaseWin|x86 = ReleaseWin|x86 + ReleaseWinPY3|x64 = ReleaseWinPY3|x64 + ReleaseWinPY3|x86 = ReleaseWinPY3|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x64.ActiveCfg = DebugMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x64.Build.0 = DebugMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x86.ActiveCfg = DebugMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x86.Build.0 = DebugMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x64.ActiveCfg = DebugWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x64.Build.0 = DebugWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x86.ActiveCfg = DebugWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x86.Build.0 = DebugWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x64.Build.0 = DebugWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x86.Build.0 = DebugWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x64.ActiveCfg = ReleaseMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x64.Build.0 = ReleaseMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x86.ActiveCfg = ReleaseMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x86.Build.0 = ReleaseMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x64.ActiveCfg = ReleaseWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x64.Build.0 = ReleaseWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x86.ActiveCfg = ReleaseWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x86.Build.0 = ReleaseWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|Any CPU + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x64.ActiveCfg = DebugMono|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x64.Build.0 = DebugMono|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x86.ActiveCfg = DebugMono|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x86.Build.0 = DebugMono|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x64.ActiveCfg = DebugWin|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x64.Build.0 = DebugWin|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x86.ActiveCfg = DebugWin|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x86.Build.0 = DebugWin|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMono|x64.ActiveCfg = DebugMono|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMono|x86.ActiveCfg = DebugMono|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x64.ActiveCfg = DebugWin|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x64.Build.0 = DebugWin|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x86.ActiveCfg = DebugWin|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x86.Build.0 = DebugWin|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x64.ActiveCfg = DebugMono|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x64.Build.0 = DebugMono|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x86.ActiveCfg = DebugMono|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x86.Build.0 = DebugMono|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x64.ActiveCfg = DebugWin|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x64.Build.0 = DebugWin|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x86.ActiveCfg = DebugWin|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x86.Build.0 = DebugWin|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x64.ActiveCfg = DebugMono|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x64.Build.0 = DebugMono|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x86.ActiveCfg = DebugMono|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x86.Build.0 = DebugMono|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x64.ActiveCfg = DebugWin|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x64.Build.0 = DebugWin|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x86.ActiveCfg = DebugWin|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x86.Build.0 = DebugWin|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A6347B90-BBE6-4E45-90BF-1BD8B76069E3} + EndGlobalSection +EndGlobal diff --git a/requirements.txt b/requirements.txt index bcceedf25..18f9bf902 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,11 +2,10 @@ pytest coverage +# Coverage upload +codecov + # Platform specific requirements pip; sys_platform == 'win32' wheel; sys_platform == 'win32' pycparser; sys_platform != 'win32' - -# Coverage upload -# codecov v2.0.6 isn't on PyPi -https://github.com/codecov/codecov-python/tarball/v2.0.6 diff --git a/setup.py b/setup.py index b85e2c8ad..c686f4ec2 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,8 @@ import sys import sysconfig from distutils import spawn -from distutils.command import build_ext, install_data, install_lib +from distutils.command import install, build, build_ext, install_data, install_lib +from wheel import bdist_wheel from setuptools import Extension, setup @@ -39,6 +40,12 @@ kits_suffix = os.path.join("bin", ARCH) WIN_SDK_KEYS = ( + RegKey(sdk_name="Windows Kit 10.0", key=kits_root, + value_name="KitsRoot10", suffix=os.path.join("bin", "10.0.16299.0", ARCH)), + + RegKey(sdk_name="Windows Kit 10.0", key=kits_root, + value_name="KitsRoot10", suffix=os.path.join("bin", "10.0.15063.0", ARCH)), + RegKey(sdk_name="Windows Kit 10.0", key=kits_root, value_name="KitsRoot10", suffix=kits_suffix), @@ -65,6 +72,9 @@ ) VS_KEYS = ( + RegKey(sdk_name="MSBuild 15", key=vs_root.format("15.0"), + value_name="MSBuildToolsPath", suffix=""), + RegKey(sdk_name="MSBuild 14", key=vs_root.format("14.0"), value_name="MSBuildToolsPath", suffix=""), @@ -128,9 +138,28 @@ def _get_long_description(): except ImportError: return '.Net and Mono integration for Python' +def _update_xlat_devtools(): + global DEVTOOLS + if DEVTOOLS == "MsDev": + DEVTOOLS = "MsDev15" + elif DEVTOOLS == "Mono": + DEVTOOLS = "dotnet" class BuildExtPythonnet(build_ext.build_ext): + user_options = build_ext.build_ext.user_options + [ + ('xplat', None, None) + ] + def initialize_options(self): + build_ext.build_ext.initialize_options(self) + self.xplat = None + + def finalize_options(self): + build_ext.build_ext.finalize_options(self) + def build_extension(self, ext): + if self.xplat: + _update_xlat_devtools() + """Builds the .pyd file using msbuild or xbuild""" if ext.name != "clr": return build_ext.build_ext.build_extension(self, ext) @@ -161,7 +190,7 @@ def build_extension(self, ext): if CONFIG == "Debug": defines.extend(["DEBUG", "TRACE"]) - if sys.platform != "win32" and DEVTOOLS == "Mono": + if sys.platform != "win32" and (DEVTOOLS == "Mono" or DEVTOOLS == "dotnet"): on_darwin = sys.platform == "darwin" defines.append("MONO_OSX" if on_darwin else "MONO_LINUX") @@ -193,20 +222,33 @@ def build_extension(self, ext): if DEVTOOLS == "MsDev": _xbuild = '"{0}"'.format(self._find_msbuild_tool("msbuild.exe")) _config = "{0}Win".format(CONFIG) - + _solution_file = 'pythonnet.sln' + _custom_define_constants = False + elif DEVTOOLS == "MsDev15": + _xbuild = '"{0}"'.format(self._find_msbuild_tool_15()) + _config = "{0}Win".format(CONFIG) + _solution_file = 'pythonnet.15.sln' + _custom_define_constants = True elif DEVTOOLS == "Mono": - _xbuild = "xbuild" + _xbuild = 'xbuild' + _config = "{0}Mono".format(CONFIG) + _solution_file = 'pythonnet.sln' + _custom_define_constants = False + elif DEVTOOLS == "dotnet": + _xbuild = 'dotnet msbuild' _config = "{0}Mono".format(CONFIG) + _solution_file = 'pythonnet.15.sln' + _custom_define_constants = True else: raise NotImplementedError( - "DevTool {0} not supported (use MsDev/Mono)".format(DEVTOOLS)) + "DevTool {0} not supported (use MsDev/MsDev15/Mono/dotnet)".format(DEVTOOLS)) cmd = [ _xbuild, - 'pythonnet.sln', + _solution_file, '/p:Configuration={}'.format(_config), '/p:Platform={}'.format(ARCH), - '/p:DefineConstants="{}"'.format(','.join(defines)), + '/p:{}DefineConstants="{}"'.format('Custom' if _custom_define_constants else '','%3B'.join(defines)), '/p:PythonBuildDir="{}"'.format(os.path.abspath(dest_dir)), '/p:PythonInteropFile="{}"'.format(os.path.basename(interop_file)), '/verbosity:{}'.format(VERBOSITY), @@ -217,15 +259,17 @@ def build_extension(self, ext): cmd.append('/p:PythonManifest="{0}"'.format(manifest)) self.debug_print("Building: {0}".format(" ".join(cmd))) - use_shell = True if DEVTOOLS == "Mono" else False + use_shell = True if DEVTOOLS == "Mono" or DEVTOOLS == "dotnet" else False + subprocess.check_call(" ".join(cmd + ["/t:Clean"]), shell=use_shell) subprocess.check_call(" ".join(cmd + ["/t:Build"]), shell=use_shell) - - if DEVTOOLS == "Mono": + if DEVTOOLS == "MsDev15" or DEVTOOLS == "dotnet": + subprocess.check_call(" ".join(cmd + ['"/t:Console_15:publish;Python_EmbeddingTest_15:publish"', "/p:TargetFramework=netcoreapp2.0"]), shell=use_shell) + if DEVTOOLS == "Mono" or DEVTOOLS == "dotnet": self._build_monoclr() def _get_manifest(self, build_dir): - if DEVTOOLS != "MsDev": + if DEVTOOLS != "MsDev" and DEVTOOLS != "MsDev15": return mt = self._find_msbuild_tool("mt.exe", use_windows_sdk=True) manifest = os.path.abspath(os.path.join(build_dir, "app.manifest")) @@ -258,22 +302,47 @@ def _build_monoclr(self): def _install_packages(self): """install packages using nuget""" - nuget = os.path.join("tools", "nuget", "nuget.exe") - use_shell = False - if DEVTOOLS == "Mono": - nuget = "mono {0}".format(nuget) - use_shell = True + use_shell = DEVTOOLS == "Mono" or DEVTOOLS == "dotnet" + + if DEVTOOLS == "MsDev15" or DEVTOOLS == "dotnet": + if DEVTOOLS == "MsDev15": + _config = "{0}Win".format(CONFIG) + elif DEVTOOLS == "dotnet": + _config = "{0}Mono".format(CONFIG) + + cmd = "dotnet msbuild /t:Restore pythonnet.15.sln /p:Configuration={0} /p:Platform={1}".format(_config, ARCH) + self.debug_print("Updating packages with xplat: {0}".format(cmd)) + subprocess.check_call(cmd, shell=use_shell) + else: + nuget = os.path.join("tools", "nuget", "nuget.exe") + + if DEVTOOLS == "Mono": + nuget = "mono {0}".format(nuget) - cmd = "{0} update -self".format(nuget) - self.debug_print("Updating NuGet: {0}".format(cmd)) - subprocess.check_call(cmd, shell=use_shell) + cmd = "{0} update -self".format(nuget) + self.debug_print("Updating NuGet: {0}".format(cmd)) + subprocess.check_call(cmd, shell=use_shell) - cmd = "{0} restore pythonnet.sln -o packages".format(nuget) - self.debug_print("Installing packages: {0}".format(cmd)) - subprocess.check_call(cmd, shell=use_shell) + cmd = "{0} restore pythonnet.sln -o packages".format(nuget) + self.debug_print("Installing packages: {0}".format(cmd)) + subprocess.check_call(cmd, shell=use_shell) def _find_msbuild_tool(self, tool="msbuild.exe", use_windows_sdk=False): """Return full path to one of the Microsoft build tools""" + + # trying to search path with help of vswhere when MSBuild 15.0 and higher installed. + if tool=="msbuild.exe" and use_windows_sdk==False: + try: + basePathes = subprocess.check_output( + ["tools\\vswhere\\vswhere.exe", "-latest", + "-version", "[15.0, 16.0)", + "-requires", "Microsoft.Component.MSBuild", + "-property", "InstallationPath"]).splitlines() + if len(basePathes): + return os.path.join(basePathes[0].decode(sys.stdout.encoding or "utf-8"), "MSBuild", "15.0", "Bin", "MSBuild.exe") + except: + pass # keep trying to search by old method. + # Search in PATH first path = spawn.find_executable(tool) if path: @@ -316,6 +385,20 @@ def _find_msbuild_tool(self, tool="msbuild.exe", use_windows_sdk=False): raise RuntimeError("{0} could not be found".format(tool)) + def _find_msbuild_tool_15(self): + """Return full path to one of the Microsoft build tools""" + try: + basePathes = subprocess.check_output( + ["tools\\vswhere\\vswhere.exe", "-latest", + "-version", "[15.0, 16.0)", + "-requires", "Microsoft.Component.MSBuild", + "-property", "InstallationPath"]).splitlines() + if len(basePathes): + return os.path.join(basePathes[0].decode(sys.stdout.encoding or "utf-8"), "MSBuild", "15.0", "Bin", "MSBuild.exe") + else: + raise RuntimeError("MSBuild >=15.0 could not be found.") + except subprocess.CalledProcessError as e: + raise RuntimeError("MSBuild >=15.0 could not be found. {0}".format(e.output)) class InstallLibPythonnet(install_lib.install_lib): def install(self): @@ -353,8 +436,39 @@ def run(self): return install_data.install_data.run(self) +class InstallPythonnet(install.install): + user_options = install.install.user_options + [ + ('xplat', None, None) + ] + def initialize_options(self): + install.install.initialize_options(self) + self.xplat = None -############################################################################### + def finalize_options(self): + install.install.finalize_options(self) + + def run(self): + if self.xplat: + _update_xlat_devtools() + return install.install.run(self) + +class BDistWheelPythonnet(bdist_wheel.bdist_wheel): + user_options = bdist_wheel.bdist_wheel.user_options + [ + ('xplat', None, None) + ] + def initialize_options(self): + bdist_wheel.bdist_wheel.initialize_options(self) + self.xplat = None + + def finalize_options(self): + bdist_wheel.bdist_wheel.finalize_options(self) + + def run(self): + if self.xplat: + _update_xlat_devtools() + return bdist_wheel.bdist_wheel.run(self) + + ############################################################################### setupdir = os.path.dirname(__file__) if setupdir: os.chdir(setupdir) @@ -365,7 +479,7 @@ def run(self): setup( name="pythonnet", - version="2.3.0", + version="2.4.0.dev0", description=".Net and Mono integration for Python", url='https://pythonnet.github.io/', license='MIT', @@ -382,9 +496,11 @@ def run(self): ]), ], cmdclass={ + "install": InstallPythonnet, "build_ext": BuildExtPythonnet, "install_lib": InstallLibPythonnet, "install_data": InstallDataPythonnet, + "bdist_wheel": BDistWheelPythonnet }, classifiers=[ 'Development Status :: 5 - Production/Stable', diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs index 24ba26862..c164e75d6 100644 --- a/src/SharedAssemblyInfo.cs +++ b/src/SharedAssemblyInfo.cs @@ -25,4 +25,4 @@ // Version Information. Keeping it simple. May need to revisit for Nuget // See: https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/ // AssemblyVersion can only be numeric -[assembly: AssemblyVersion("2.3.0")] +[assembly: AssemblyVersion("2.4.0")] diff --git a/src/clrmodule/ClrModule.cs b/src/clrmodule/ClrModule.cs index d24376a1f..3bb3a533b 100644 --- a/src/clrmodule/ClrModule.cs +++ b/src/clrmodule/ClrModule.cs @@ -53,7 +53,7 @@ public static void initclr() { #if USE_PYTHON_RUNTIME_VERSION // Has no effect until SNK works. Keep updated anyways. - Version = new Version("2.3.0"), + Version = new Version("2.4.0"), #endif CultureInfo = CultureInfo.InvariantCulture }; diff --git a/src/clrmodule/clrmodule.15.csproj b/src/clrmodule/clrmodule.15.csproj new file mode 100644 index 000000000..2585ffdd2 --- /dev/null +++ b/src/clrmodule/clrmodule.15.csproj @@ -0,0 +1,95 @@ + + + + + + net40 + x64;x86 + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + clrmodule + clrmodule + clrmodule + 2.4.0 + false + false + false + false + false + false + bin\clrmodule.xml + bin\ + false + 1591 + ..\..\ + $(SolutionDir)\bin\ + $(PythonBuildDir)\$(TargetFramework)\ + 6 + prompt + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);TRACE;DEBUG + + + + x86 + + + x64 + + + + false + full + + + true + pdbonly + + + + $(DefineConstants);PYTHON2;TRACE;DEBUG + + + $(DefineConstants);PYTHON2 + + + $(DefineConstants);PYTHON2;TRACE;DEBUG + + + $(DefineConstants);PYTHON2 + + + $(DefineConstants);PYTHON3;TRACE;DEBUG + + + $(DefineConstants);PYTHON3 + + + $(DefineConstants);PYTHON3;TRACE;DEBUG + + + $(DefineConstants);PYTHON3 + + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + + diff --git a/src/console/Console.15.csproj b/src/console/Console.15.csproj new file mode 100644 index 000000000..ec5008036 --- /dev/null +++ b/src/console/Console.15.csproj @@ -0,0 +1,94 @@ + + + + net40;netcoreapp2.0 + x64;x86 + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + Exe + nPython + Python.Runtime + nPython + 2.4.0 + false + false + false + false + false + false + bin\ + false + $(OutputPath)\$(AssemblyName).xml + $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml + 1591 + ..\..\ + $(SolutionDir)\bin\ + $(PythonBuildDir)\$(TargetFramework)\ + 6 + python-clear.ico + prompt + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);TRACE;DEBUG + $(NuGetPackageRoot)\microsoft.targetingpack.netframework.v4.5\1.0.1\lib\net45\ + + + x86 + + + x64 + + + + false + full + + + true + pdbonly + + + true + false + full + + + true + true + portable + + + + $(DefineConstants);DEBUG;TRACE + + + $(DefineConstants) + + + + $(PythonManifest) + + + + + + + Properties\SharedAssemblyInfo.cs + + + + + + Python.Runtime.dll + + + + + + + + + + + + diff --git a/src/console/pythonconsole.cs b/src/console/pythonconsole.cs index e9bb31e69..912e9bb0d 100644 --- a/src/console/pythonconsole.cs +++ b/src/console/pythonconsole.cs @@ -16,8 +16,9 @@ namespace Python.Runtime /// public sealed class PythonConsole { +#if NET40 private static AssemblyLoader assemblyLoader = new AssemblyLoader(); - +#endif private PythonConsole() { } @@ -25,9 +26,11 @@ private PythonConsole() [STAThread] public static int Main(string[] args) { + // Only net40 is capable to safely inject python.runtime.dll into resources. +#if NET40 // reference the static assemblyLoader to stop it being optimized away AssemblyLoader a = assemblyLoader; - +#endif string[] cmd = Environment.GetCommandLineArgs(); PythonEngine.Initialize(); @@ -37,6 +40,7 @@ public static int Main(string[] args) return i; } +#if NET40 // Register a callback function to load embedded assemblies. // (Python.Runtime.dll is included as a resource) private sealed class AssemblyLoader @@ -73,5 +77,6 @@ public AssemblyLoader() }; } } +#endif } } diff --git a/src/embed_tests/Program.cs b/src/embed_tests/Program.cs new file mode 100644 index 000000000..b4439e3e4 --- /dev/null +++ b/src/embed_tests/Program.cs @@ -0,0 +1,19 @@ +using System; + +using NUnit.Common; + +using NUnitLite; + +namespace Python.EmbeddingTest +{ + public class Program + { + public static int Main(string[] args) + { + return new AutoRun(typeof(Program).Assembly).Execute( + args, + new ExtendedTextWrapper(Console.Out), + Console.In); + } + } +} diff --git a/src/embed_tests/Python.EmbeddingTest.15.csproj b/src/embed_tests/Python.EmbeddingTest.15.csproj new file mode 100644 index 000000000..92d55a7e0 --- /dev/null +++ b/src/embed_tests/Python.EmbeddingTest.15.csproj @@ -0,0 +1,115 @@ + + + + + net40;netcoreapp2.0 + x64;x86 + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + Exe + false + Python.EmbeddingTest + Python.EmbeddingTest + Python.EmbeddingTest + 2.4.0 + false + false + false + false + bin\ + false + $(OutputPath)\$(AssemblyName).xml + $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml + 1591 + ..\..\ + $(SolutionDir)\bin\ + $(OutputPath)\$(TargetFramework)_publish + 6 + prompt + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);NETCOREAPP + $(DefineConstants);TRACE;DEBUG + $(NuGetPackageRoot)\microsoft.targetingpack.netframework.v4.5\1.0.1\lib\net45\ + + + x86 + + + x64 + + + + false + full + + + true + pdbonly + + + true + false + full + + + true + true + portable + + + + $(DefineConstants);DEBUG;TRACE + + + $(DefineConstants) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + + diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj index 9c417cd27..fe02b0526 100644 --- a/src/embed_tests/Python.EmbeddingTest.csproj +++ b/src/embed_tests/Python.EmbeddingTest.csproj @@ -1,4 +1,4 @@ - + Debug @@ -70,8 +70,8 @@ - - ..\..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll + + ..\..\packages\NUnit.3.7.1\lib\net40\nunit.framework.dll @@ -84,7 +84,9 @@ + + @@ -96,7 +98,10 @@ + + + @@ -114,6 +119,6 @@ - + - + \ No newline at end of file diff --git a/src/embed_tests/TestConverter.cs b/src/embed_tests/TestConverter.cs new file mode 100644 index 000000000..346c8afdc --- /dev/null +++ b/src/embed_tests/TestConverter.cs @@ -0,0 +1,48 @@ +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class TestConverter + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + [Test] + public void TestConvertSingleToManaged( + [Values(float.PositiveInfinity, float.NegativeInfinity, float.MinValue, float.MaxValue, float.NaN, + float.Epsilon)] float testValue) + { + var pyFloat = new PyFloat(testValue); + + object convertedValue; + var converted = Converter.ToManaged(pyFloat.Handle, typeof(float), out convertedValue, false); + + Assert.IsTrue(converted); + Assert.IsTrue(((float) convertedValue).Equals(testValue)); + } + + [Test] + public void TestConvertDoubleToManaged( + [Values(double.PositiveInfinity, double.NegativeInfinity, double.MinValue, double.MaxValue, double.NaN, + double.Epsilon)] double testValue) + { + var pyFloat = new PyFloat(testValue); + + object convertedValue; + var converted = Converter.ToManaged(pyFloat.Handle, typeof(double), out convertedValue, false); + + Assert.IsTrue(converted); + Assert.IsTrue(((double) convertedValue).Equals(testValue)); + } + } +} diff --git a/src/embed_tests/TestExample.cs b/src/embed_tests/TestExample.cs new file mode 100644 index 000000000..0cf795f5d --- /dev/null +++ b/src/embed_tests/TestExample.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class TestExample + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + [Test] + public void TestReadme() + { + dynamic np; + try + { + np = Py.Import("numpy"); + } + catch (PythonException) + { + Assert.Inconclusive("Numpy or dependency not installed"); + return; + } + + Assert.AreEqual("1.0", np.cos(np.pi * 2).ToString()); + + dynamic sin = np.sin; + StringAssert.StartsWith("-0.95892", sin(5).ToString()); + + double c = np.cos(5) + sin(5); + Assert.AreEqual(-0.675262, c, 0.01); + + dynamic a = np.array(new List { 1, 2, 3 }); + Assert.AreEqual("float64", a.dtype.ToString()); + + dynamic b = np.array(new List { 6, 5, 4 }, Py.kw("dtype", np.int32)); + Assert.AreEqual("int32", b.dtype.ToString()); + + Assert.AreEqual("[ 6. 10. 12.]", (a * b).ToString()); + } + } +} diff --git a/src/embed_tests/TestNamedArguments.cs b/src/embed_tests/TestNamedArguments.cs new file mode 100644 index 000000000..1d7076956 --- /dev/null +++ b/src/embed_tests/TestNamedArguments.cs @@ -0,0 +1,64 @@ +using System; +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class TestNamedArguments + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + /// + /// Test named arguments support through Py.kw method + /// + [Test] + public void TestKeywordArgs() + { + dynamic a = CreateTestClass(); + var result = (int)a.Test3(2, Py.kw("a4", 8)); + + Assert.AreEqual(12, result); + } + + + /// + /// Test keyword arguments with .net named arguments + /// + [Test] + public void TestNamedArgs() + { + dynamic a = CreateTestClass(); + var result = (int)a.Test3(2, a4: 8); + + Assert.AreEqual(12, result); + } + + + + private static PyObject CreateTestClass() + { + var locals = new PyDict(); + + PythonEngine.Exec(@" +class cmTest3: + def Test3(self, a1 = 1, a2 = 1, a3 = 1, a4 = 1): + return a1 + a2 + a3 + a4 + +a = cmTest3() +", null, locals.Handle); + + return locals.GetItem("a"); + } + + } +} diff --git a/src/embed_tests/TestPyScope.cs b/src/embed_tests/TestPyScope.cs new file mode 100644 index 000000000..49c15a3a1 --- /dev/null +++ b/src/embed_tests/TestPyScope.cs @@ -0,0 +1,372 @@ +using System; +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class PyScopeTest + { + private PyScope ps; + + [SetUp] + public void SetUp() + { + using (Py.GIL()) + { + ps = Py.CreateScope("test"); + } + } + + [TearDown] + public void Dispose() + { + using (Py.GIL()) + { + ps.Dispose(); + ps = null; + } + } + + /// + /// Eval a Python expression and obtain its return value. + /// + [Test] + public void TestEval() + { + using (Py.GIL()) + { + ps.Set("a", 1); + var result = ps.Eval("a + 2"); + Assert.AreEqual(3, result); + } + } + + /// + /// Exec Python statements and obtain the variables created. + /// + [Test] + public void TestExec() + { + using (Py.GIL()) + { + ps.Set("bb", 100); //declare a global variable + ps.Set("cc", 10); //declare a local variable + ps.Exec("aa = bb + cc + 3"); + var result = ps.Get("aa"); + Assert.AreEqual(113, result); + } + } + + /// + /// Compile an expression into an ast object; + /// Execute the ast and obtain its return value. + /// + [Test] + public void TestCompileExpression() + { + using (Py.GIL()) + { + ps.Set("bb", 100); //declare a global variable + ps.Set("cc", 10); //declare a local variable + PyObject script = PythonEngine.Compile("bb + cc + 3", "", RunFlagType.Eval); + var result = ps.Execute(script); + Assert.AreEqual(113, result); + } + } + + /// + /// Compile Python statements into an ast object; + /// Execute the ast; + /// Obtain the local variables created. + /// + [Test] + public void TestCompileStatements() + { + using (Py.GIL()) + { + ps.Set("bb", 100); //declare a global variable + ps.Set("cc", 10); //declare a local variable + PyObject script = PythonEngine.Compile("aa = bb + cc + 3", "", RunFlagType.File); + ps.Execute(script); + var result = ps.Get("aa"); + Assert.AreEqual(113, result); + } + } + + /// + /// Create a function in the scope, then the function can read variables in the scope. + /// It cannot write the variables unless it uses the 'global' keyword. + /// + [Test] + public void TestScopeFunction() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + ps.Set("cc", 10); + ps.Exec( + "def func1():\n" + + " bb = cc + 10\n"); + dynamic func1 = ps.Get("func1"); + func1(); //call the function, it can be called any times + var result = ps.Get("bb"); + Assert.AreEqual(100, result); + + ps.Set("bb", 100); + ps.Set("cc", 10); + ps.Exec( + "def func2():\n" + + " global bb\n" + + " bb = cc + 10\n"); + dynamic func2 = ps.Get("func2"); + func2(); + result = ps.Get("bb"); + Assert.AreEqual(20, result); + } + } + + /// + /// Create a class in the scope, the class can read variables in the scope. + /// Its methods can write the variables with the help of 'global' keyword. + /// + [Test] + public void TestScopeClass() + { + using (Py.GIL()) + { + dynamic _ps = ps; + _ps.bb = 100; + ps.Exec( + "class Class1():\n" + + " def __init__(self, value):\n" + + " self.value = value\n" + + " def call(self, arg):\n" + + " return self.value + bb + arg\n" + //use scope variables + " def update(self, arg):\n" + + " global bb\n" + + " bb = self.value + arg\n" //update scope variable + ); + dynamic obj1 = _ps.Class1(20); + var result = obj1.call(10).As(); + Assert.AreEqual(130, result); + + obj1.update(10); + result = ps.Get("bb"); + Assert.AreEqual(30, result); + } + } + + /// + /// Import a python module into the session. + /// Equivalent to the Python "import" statement. + /// + [Test] + public void TestImportModule() + { + using (Py.GIL()) + { + dynamic sys = ps.Import("sys"); + Assert.IsTrue(ps.Contains("sys")); + + ps.Exec("sys.attr1 = 2"); + var value1 = ps.Eval("sys.attr1"); + var value2 = sys.attr1.As(); + Assert.AreEqual(2, value1); + Assert.AreEqual(2, value2); + + //import as + ps.Import("sys", "sys1"); + Assert.IsTrue(ps.Contains("sys1")); + } + } + + /// + /// Create a scope and import variables from a scope, + /// exec Python statements in the scope then discard it. + /// + [Test] + public void TestImportScope() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + ps.Set("cc", 10); + + using (var scope = Py.CreateScope()) + { + scope.Import(ps, "ps"); + scope.Exec("aa = ps.bb + ps.cc + 3"); + var result = scope.Get("aa"); + Assert.AreEqual(113, result); + } + + Assert.IsFalse(ps.Contains("aa")); + } + } + + /// + /// Create a scope and import variables from a scope, + /// exec Python statements in the scope then discard it. + /// + [Test] + public void TestImportAllFromScope() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + ps.Set("cc", 10); + + using (var scope = ps.NewScope()) + { + scope.Exec("aa = bb + cc + 3"); + var result = scope.Get("aa"); + Assert.AreEqual(113, result); + } + + Assert.IsFalse(ps.Contains("aa")); + } + } + + /// + /// Create a scope and import variables from a scope, + /// call the function imported. + /// + [Test] + public void TestImportScopeFunction() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + ps.Set("cc", 10); + ps.Exec( + "def func1():\n" + + " return cc + bb\n"); + + using (PyScope scope = ps.NewScope()) + { + //'func1' is imported from the origion scope + scope.Exec( + "def func2():\n" + + " return func1() - cc - bb\n"); + dynamic func2 = scope.Get("func2"); + + var result1 = func2().As(); + Assert.AreEqual(0, result1); + + scope.Set("cc", 20);//it has no effect on the globals of 'func1' + var result2 = func2().As(); + Assert.AreEqual(-10, result2); + scope.Set("cc", 10); //rollback + + ps.Set("cc", 20); + var result3 = func2().As(); + Assert.AreEqual(10, result3); + ps.Set("cc", 10); //rollback + } + } + } + + /// + /// Import a python module into the session with a new name. + /// Equivalent to the Python "import .. as .." statement. + /// + [Test] + public void TestImportScopeByName() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + + using (var scope = Py.CreateScope()) + { + scope.ImportAll("test"); + //scope.ImportModule("test"); + + Assert.IsTrue(scope.Contains("bb")); + } + } + } + + /// + /// Use the locals() and globals() method just like in python module + /// + [Test] + public void TestVariables() + { + (ps.Variables() as dynamic)["ee"] = new PyInt(200); + var a0 = ps.Get("ee"); + Assert.AreEqual(200, a0); + + ps.Exec("locals()['ee'] = 210"); + var a1 = ps.Get("ee"); + Assert.AreEqual(210, a1); + + ps.Exec("globals()['ee'] = 220"); + var a2 = ps.Get("ee"); + Assert.AreEqual(220, a2); + + using (var item = ps.Variables()) + { + item["ee"] = new PyInt(230); + } + var a3 = ps.Get("ee"); + Assert.AreEqual(230, a3); + } + + /// + /// Share a pyscope by multiple threads. + /// + [Test] + public void TestThread() + { + //After the proposal here https://github.com/pythonnet/pythonnet/pull/419 complished, + //the BeginAllowThreads statement blow and the last EndAllowThreads statement + //should be removed. + dynamic _ps = ps; + var ts = PythonEngine.BeginAllowThreads(); + using (Py.GIL()) + { + _ps.res = 0; + _ps.bb = 100; + _ps.th_cnt = 0; + //add function to the scope + //can be call many times, more efficient than ast + ps.Exec( + "def update():\n" + + " global res, th_cnt\n" + + " res += bb + 1\n" + + " th_cnt += 1\n" + ); + } + int th_cnt = 3; + for (int i =0; i< th_cnt; i++) + { + System.Threading.Thread th = new System.Threading.Thread(()=> + { + using (Py.GIL()) + { + //ps.GetVariable("update")(); //call the scope function dynamicly + _ps.update(); + } + }); + th.Start(); + } + //equivalent to Thread.Join, make the main thread join the GIL competition + int cnt = 0; + while(cnt != th_cnt) + { + using (Py.GIL()) + { + cnt = ps.Get("th_cnt"); + } + System.Threading.Thread.Sleep(10); + } + using (Py.GIL()) + { + var result = ps.Get("res"); + Assert.AreEqual(101* th_cnt, result); + } + PythonEngine.EndAllowThreads(ts); + } + } +} diff --git a/src/embed_tests/TestPySequence.cs b/src/embed_tests/TestPySequence.cs index 7c175b1ce..1e3ebf144 100644 --- a/src/embed_tests/TestPySequence.cs +++ b/src/embed_tests/TestPySequence.cs @@ -69,8 +69,10 @@ public void TestRepeat() PyObject actual = t1.Repeat(3); Assert.AreEqual("FooFooFoo", actual.ToString()); - actual = t1.Repeat(-3); - Assert.AreEqual("", actual.ToString()); + // On 32 bit system this argument should be int, but on the 64 bit system this should be long value. + // This works on the Framework 4.0 accidentally, it should produce out of memory! + // actual = t1.Repeat(-3); + // Assert.AreEqual("", actual.ToString()); } [Test] diff --git a/src/embed_tests/TestPyWith.cs b/src/embed_tests/TestPyWith.cs new file mode 100644 index 000000000..fd3f8e662 --- /dev/null +++ b/src/embed_tests/TestPyWith.cs @@ -0,0 +1,88 @@ +using System; +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class TestPyWith + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + /// + /// Test that exception is raised in context manager that ignores it. + /// + [Test] + public void TestWithPositive() + { + var locals = new PyDict(); + + PythonEngine.Exec(@" +class CmTest: + def __enter__(self): + print('Enter') + return self + def __exit__(self, t, v, tb): + # Exception not handled, return will be False + print('Exit') + def fail(self): + return 5 / 0 + +a = CmTest() +", null, locals.Handle); + + var a = locals.GetItem("a"); + + try + { + Py.With(a, cmTest => + { + cmTest.fail(); + }); + } + catch (PythonException e) + { + Assert.IsTrue(e.Message.Contains("ZeroDivisionError")); + } + } + + + /// + /// Test that exception is not raised in context manager that handles it + /// + [Test] + public void TestWithNegative() + { + var locals = new PyDict(); + + PythonEngine.Exec(@" +class CmTest: + def __enter__(self): + print('Enter') + return self + def __exit__(self, t, v, tb): + # Signal exception is handled by returning true + return True + def fail(self): + return 5 / 0 + +a = CmTest() +", null, locals.Handle); + + var a = locals.GetItem("a"); + Py.With(a, cmTest => + { + cmTest.fail(); + }); + } + } +} diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs index 22e6db0a9..2e0598da7 100644 --- a/src/embed_tests/TestRuntime.cs +++ b/src/embed_tests/TestRuntime.cs @@ -1,4 +1,4 @@ -using System; +using System; using NUnit.Framework; using Python.Runtime; @@ -47,5 +47,46 @@ public static void RefCountTest() Runtime.Runtime.Py_Finalize(); } + + [Test] + public static void PyCheck_Iter_PyObject_IsIterable_Test() + { + Runtime.Runtime.Py_Initialize(); + + // Tests that a python list is an iterable, but not an iterator + var pyList = Runtime.Runtime.PyList_New(0); + Assert.IsFalse(Runtime.Runtime.PyIter_Check(pyList)); + Assert.IsTrue(Runtime.Runtime.PyObject_IsIterable(pyList)); + + // Tests that a python list iterator is both an iterable and an iterator + var pyListIter = Runtime.Runtime.PyObject_GetIter(pyList); + Assert.IsTrue(Runtime.Runtime.PyObject_IsIterable(pyListIter)); + Assert.IsTrue(Runtime.Runtime.PyIter_Check(pyListIter)); + + // Tests that a python float is neither an iterable nor an iterator + var pyFloat = Runtime.Runtime.PyFloat_FromDouble(2.73); + Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(pyFloat)); + Assert.IsFalse(Runtime.Runtime.PyIter_Check(pyFloat)); + + Runtime.Runtime.Py_Finalize(); + } + + [Test] + public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test() + { + Runtime.Runtime.Py_Initialize(); + + // Create an instance of threading.Lock, which is one of the very few types that does not have the + // TypeFlags.HaveIter set in Python 2. This tests a different code path in PyObject_IsIterable and PyIter_Check. + var threading = Runtime.Runtime.PyImport_ImportModule("threading"); + var threadingDict = Runtime.Runtime.PyModule_GetDict(threading); + var lockType = Runtime.Runtime.PyDict_GetItemString(threadingDict, "Lock"); + var lockInstance = Runtime.Runtime.PyObject_CallObject(lockType, Runtime.Runtime.PyTuple_New(0)); + + Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(lockInstance)); + Assert.IsFalse(Runtime.Runtime.PyIter_Check(lockInstance)); + + Runtime.Runtime.Py_Finalize(); + } } } diff --git a/src/embed_tests/packages.config b/src/embed_tests/packages.config index 4cb01d3be..8c175f441 100644 --- a/src/embed_tests/packages.config +++ b/src/embed_tests/packages.config @@ -1,5 +1,5 @@ - - + + diff --git a/src/embed_tests/pyimport.cs b/src/embed_tests/pyimport.cs index 3bb9a34d6..acb3433de 100644 --- a/src/embed_tests/pyimport.cs +++ b/src/embed_tests/pyimport.cs @@ -30,7 +30,11 @@ public void SetUp() /* Append the tests directory to sys.path * using reflection to circumvent the private * modifiers placed on most Runtime methods. */ +#if NETCOREAPP + const string s = "../../fixtures"; +#else const string s = "../fixtures"; +#endif string testPath = Path.Combine(TestContext.CurrentContext.TestDirectory, s); IntPtr str = Runtime.Runtime.PyString_FromString(testPath); diff --git a/src/runtime/CustomMarshaler.cs b/src/runtime/CustomMarshaler.cs index 90bb77a71..b51911816 100644 --- a/src/runtime/CustomMarshaler.cs +++ b/src/runtime/CustomMarshaler.cs @@ -91,13 +91,13 @@ public static int GetUnicodeByteLength(IntPtr p) var len = 0; while (true) { - int c = Runtime.UCS == 2 + int c = Runtime._UCS == 2 ? Marshal.ReadInt16(p, len * 2) : Marshal.ReadInt32(p, len * 4); if (c == 0) { - return len * Runtime.UCS; + return len * Runtime._UCS; } checked { @@ -163,7 +163,7 @@ public override IntPtr MarshalManagedToNative(object managedObj) } int totalStrLength = argv.Sum(arg => arg.Length + 1); - int memSize = argv.Length * IntPtr.Size + totalStrLength * Runtime.UCS; + int memSize = argv.Length * IntPtr.Size + totalStrLength * Runtime._UCS; IntPtr mem = Marshal.AllocHGlobal(memSize); try diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj new file mode 100644 index 000000000..cfde0a127 --- /dev/null +++ b/src/runtime/Python.Runtime.15.csproj @@ -0,0 +1,140 @@ + + + + net40;netstandard2.0 + AnyCPU + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + net45 + Python.Runtime + Python.Runtime + Python.Runtime + 2.4.0 + false + false + false + false + false + false + bin\ + false + $(OutputPath)\$(AssemblyName).xml + $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml + 1591;NU1701 + ..\..\ + $(SolutionDir)\bin\ + $(PythonBuildDir)\$(TargetFramework)\ + 6 + True + ..\pythonnet.snk + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);NETSTANDARD + $(DefineConstants);TRACE;DEBUG + $(NuGetPackageRoot)\microsoft.targetingpack.netframework.v4.5\1.0.1\lib\net45\ + $(PYTHONNET_PY2_VERSION) + PYTHON27 + $(PYTHONNET_PY3_VERSION) + PYTHON36 + $(PYTHONNET_WIN_DEFINE_CONSTANTS) + UCS2 + $(PYTHONNET_MONO_DEFINE_CONSTANTS) + UCS4;MONO_LINUX;PYTHON_WITH_PYMALLOC + $(PYTHONNET_INTEROP_FILE) + + + false + full + + + true + pdbonly + + + true + false + full + + + true + true + portable + + + + $(DefineConstants);PYTHON2;$(Python2Version);$(PythonMonoDefineConstants) + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonMonoDefineConstants) + + + $(DefineConstants);PYTHON2;$(Python2Version);$(PythonMonoDefineConstants);TRACE;DEBUG + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonMonoDefineConstants);TRACE;DEBUG + + + $(DefineConstants);PYTHON2;$(Python2Version);$(PythonWinDefineConstants) + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonWinDefineConstants) + + + $(DefineConstants);PYTHON2;$(Python2Version);$(PythonWinDefineConstants);TRACE;DEBUG + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonWinDefineConstants);TRACE;DEBUG + + + + + + + + + + + + + + + + + + + + + + clr.py + + + + + + + + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + + + + + diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 8580b7f61..82825a626 100644 --- a/src/runtime/Python.Runtime.csproj +++ b/src/runtime/Python.Runtime.csproj @@ -1,4 +1,4 @@ - + Debug @@ -20,12 +20,14 @@ false ..\pythonnet.snk - + + + PYTHON2;PYTHON27;UCS4 true @@ -127,6 +129,7 @@ + @@ -135,6 +138,7 @@ + @@ -161,6 +165,6 @@ - + - + \ No newline at end of file diff --git a/src/runtime/Util.cs b/src/runtime/Util.cs new file mode 100644 index 000000000..dd4418cc9 --- /dev/null +++ b/src/runtime/Util.cs @@ -0,0 +1,33 @@ +using System; +using System.Runtime.InteropServices; + +namespace Python.Runtime +{ + internal class Util + { + internal static Int64 ReadCLong(IntPtr tp, int offset) + { + // On Windows, a C long is always 32 bits. + if (Runtime.IsWindows || Runtime.Is32Bit) + { + return Marshal.ReadInt32(tp, offset); + } + else + { + return Marshal.ReadInt64(tp, offset); + } + } + + internal static void WriteCLong(IntPtr type, int offset, Int64 flags) + { + if (Runtime.IsWindows || Runtime.Is32Bit) + { + Marshal.WriteInt32(type, offset, (Int32)(flags & 0xffffffffL)); + } + else + { + Marshal.WriteInt64(type, offset, flags); + } + } + } +} \ No newline at end of file diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs index c180f9acc..16d3b99db 100644 --- a/src/runtime/classderived.cs +++ b/src/runtime/classderived.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -808,7 +808,6 @@ public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, objec obj, args); - var disposeList = new List(); CLRObject self = null; IntPtr gs = Runtime.PyGILState_Ensure(); try @@ -821,42 +820,9 @@ public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, objec // object to be collected. FieldInfo fi = obj.GetType().GetField("__pyobj__"); fi.SetValue(obj, self); - - Runtime.XIncref(self.pyHandle); - var pyself = new PyObject(self.pyHandle); - disposeList.Add(pyself); - - Runtime.XIncref(Runtime.PyNone); - var 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 overridden then it will be a managed object - ManagedType managedMethod = ManagedType.GetManagedObject(init.Handle); - if (null == managedMethod) - { - var pyargs = new PyObject[args.Length]; - for (var 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) - { - 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. diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs index 472e5dcbb..fb3d0e0d7 100644 --- a/src/runtime/clrobject.cs +++ b/src/runtime/clrobject.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.InteropServices; namespace Python.Runtime @@ -11,7 +11,7 @@ internal CLRObject(object ob, IntPtr tp) { IntPtr py = Runtime.PyType_GenericAlloc(tp, 0); - var flags = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_flags); + long flags = Util.ReadCLong(tp, TypeOffset.tp_flags); if ((flags & TypeFlags.Subclass) != 0) { IntPtr dict = Marshal.ReadIntPtr(py, ObjectOffset.DictOffset(tp)); diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index b66150a29..5179c849b 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; @@ -133,6 +134,22 @@ internal static IntPtr ToPython(object value, Type type) return result; } + if (value is IList && value.GetType().IsGenericType) + { + using (var resultlist = new PyList()) + { + foreach (object o in (IEnumerable)value) + { + using (var p = new PyObject(ToPython(o, o?.GetType()))) + { + resultlist.Append(p); + } + } + Runtime.XIncref(resultlist.Handle); + return resultlist.Handle; + } + } + // it the type is a python subclass of a managed type then return the // underlying python object rather than construct a new wrapper object. var pyderived = value as IPythonDerivedType; @@ -301,6 +318,17 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return true; } + if (obType.IsGenericType && obType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + if( value == Runtime.PyNone ) + { + result = null; + return true; + } + // Set type to underlying type + obType = obType.GetGenericArguments()[0]; + } + if (obType.IsArray) { return ToArray(value, obType, out result, setError); @@ -742,10 +770,14 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo goto type_error; } double dd = Runtime.PyFloat_AsDouble(op); + Runtime.CheckExceptionOccurred(); Runtime.XDecref(op); if (dd > Single.MaxValue || dd < Single.MinValue) { - goto overflow; + if (!double.IsInfinity(dd)) + { + goto overflow; + } } result = (float)dd; return true; @@ -757,11 +789,8 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo goto type_error; } double d = Runtime.PyFloat_AsDouble(op); + Runtime.CheckExceptionOccurred(); Runtime.XDecref(op); - if (d > Double.MaxValue || d < Double.MinValue) - { - goto overflow; - } result = d; return true; } diff --git a/src/runtime/delegatemanager.cs b/src/runtime/delegatemanager.cs index df5eec427..7632816d1 100644 --- a/src/runtime/delegatemanager.cs +++ b/src/runtime/delegatemanager.cs @@ -195,6 +195,10 @@ public Dispatcher(IntPtr target, Type dtype) ~Dispatcher() { + // We needs to disable Finalizers until it's valid implementation. + // Current implementation can produce low probability floating bugs. + return; + // Note: the managed GC thread can run and try to free one of // these *after* the Python runtime has been finalized! if (Runtime.Py_IsInitialized() > 0) diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs index 9ee8d223b..562b2e5f8 100644 --- a/src/runtime/managedtype.cs +++ b/src/runtime/managedtype.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.InteropServices; namespace Python.Runtime @@ -28,7 +28,7 @@ internal static ManagedType GetManagedObject(IntPtr ob) tp = ob; } - var flags = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_flags); + var flags = Util.ReadCLong(tp, TypeOffset.tp_flags); if ((flags & TypeFlags.Managed) != 0) { IntPtr op = tp == ob @@ -63,7 +63,7 @@ internal static bool IsManagedType(IntPtr ob) tp = ob; } - var flags = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_flags); + var flags = Util.ReadCLong(tp, TypeOffset.tp_flags); if ((flags & TypeFlags.Managed) != 0) { return true; diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs index bfb71e26d..3295ab110 100644 --- a/src/runtime/metatype.cs +++ b/src/runtime/metatype.cs @@ -105,7 +105,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) flags |= TypeFlags.BaseType; flags |= TypeFlags.Subclass; flags |= TypeFlags.HaveGC; - Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + Util.WriteCLong(type, TypeOffset.tp_flags, flags); TypeManager.CopySlot(base_type, type, TypeOffset.tp_dealloc); @@ -157,23 +157,13 @@ public static IntPtr tp_call(IntPtr tp, IntPtr args, IntPtr kw) return IntPtr.Zero; } - IntPtr py__init__ = Runtime.PyString_FromString("__init__"); - IntPtr type = Runtime.PyObject_TYPE(obj); - IntPtr init = Runtime._PyType_Lookup(type, py__init__); - Runtime.XDecref(py__init__); + var init = Runtime.PyObject_GetAttrString(obj, "__init__"); Runtime.PyErr_Clear(); if (init != IntPtr.Zero) { - IntPtr bound = Runtime.GetBoundArgTuple(obj, args); - if (bound == IntPtr.Zero) - { - Runtime.XDecref(obj); - return IntPtr.Zero; - } - - IntPtr result = Runtime.PyObject_Call(init, bound, kw); - Runtime.XDecref(bound); + IntPtr result = Runtime.PyObject_Call(init, args, kw); + Runtime.XDecref(init); if (result == IntPtr.Zero) { @@ -211,7 +201,7 @@ public static int tp_setattro(IntPtr tp, IntPtr name, IntPtr value) IntPtr fp = Marshal.ReadIntPtr(dt, TypeOffset.tp_descr_set); if (fp != IntPtr.Zero) { - return NativeCall.Impl.Int_Call_3(fp, descr, name, value); + return NativeCall.Int_Call_3(fp, descr, name, value); } Exceptions.SetError(Exceptions.AttributeError, "attribute is read-only"); return -1; @@ -247,7 +237,7 @@ public static void tp_dealloc(IntPtr tp) { // Fix this when we dont cheat on the handle for subclasses! - var flags = (int)Marshal.ReadIntPtr(tp, TypeOffset.tp_flags); + var flags = Util.ReadCLong(tp, TypeOffset.tp_flags); if ((flags & TypeFlags.Subclass) == 0) { IntPtr gc = Marshal.ReadIntPtr(tp, TypeOffset.magic()); diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs index 258d77bac..e683026f9 100644 --- a/src/runtime/moduleobject.cs +++ b/src/runtime/moduleobject.cs @@ -403,6 +403,23 @@ public static Assembly AddReference(string name) return assembly; } + /// + /// Get a Type instance for a class object. + /// clr.GetClrType(IComparable) gives you the Type for IComparable, + /// that you can e.g. perform reflection on. Similar to typeof(IComparable) in C# + /// or clr.GetClrType(IComparable) in IronPython. + /// + /// + /// + /// The Type object + + [ModuleFunction] + [ForbidPythonThreads] + public static Type GetClrType(Type type) + { + return type; + } + [ModuleFunction] [ForbidPythonThreads] public static string FindAssembly(string name) diff --git a/src/runtime/nativecall.cs b/src/runtime/nativecall.cs index 9d1b0f41c..c64306958 100644 --- a/src/runtime/nativecall.cs +++ b/src/runtime/nativecall.cs @@ -23,6 +23,32 @@ namespace Python.Runtime /// internal class NativeCall { +#if NETSTANDARD + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void Void_1_Delegate(IntPtr a1); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate IntPtr IntPtr_3_Delegate(IntPtr a1, IntPtr a2, IntPtr a3); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int Int_3_Delegate(IntPtr a1, IntPtr a2, IntPtr a3); + + public static void Void_Call_1(IntPtr fp, IntPtr a1) + { + ((Void_1_Delegate)Marshal.GetDelegateForFunctionPointer(fp, typeof(Void_1_Delegate)))(a1); + } + + public static IntPtr Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3) + { + return ((IntPtr_3_Delegate)Marshal.GetDelegateForFunctionPointer(fp, typeof(IntPtr_3_Delegate)))(a1, a2, a3); + } + + + public static int Int_Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3) + { + return ((Int_3_Delegate)Marshal.GetDelegateForFunctionPointer(fp, typeof(Int_3_Delegate)))(a1, a2, a3); + } +#else private static AssemblyBuilder aBuilder; private static ModuleBuilder mBuilder; @@ -132,9 +158,10 @@ public static int Int_Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3) { return Impl.Int_Call_3(fp, a1, a2, a3); } +#endif } - +#if !NETSTANDARD /// /// Defines native call signatures to be generated by NativeCall. /// @@ -148,4 +175,5 @@ public interface INativeCall IntPtr Call_3(IntPtr funcPtr, IntPtr a1, IntPtr a2, IntPtr a3); } +#endif } diff --git a/src/runtime/polyfill/ReflectionPolifills.cs b/src/runtime/polyfill/ReflectionPolifills.cs new file mode 100644 index 000000000..a7e9c879a --- /dev/null +++ b/src/runtime/polyfill/ReflectionPolifills.cs @@ -0,0 +1,21 @@ +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace Python.Runtime +{ +#if NETSTANDARD + public static class ReflectionPolifills + { + public static AssemblyBuilder DefineDynamicAssembly(this AppDomain appDomain, AssemblyName assemblyName, AssemblyBuilderAccess assemblyBuilderAccess) + { + return AssemblyBuilder.DefineDynamicAssembly(assemblyName, assemblyBuilderAccess); + } + + public static Type CreateType(this TypeBuilder typeBuilder) + { + return typeBuilder.GetTypeInfo().GetType(); + } + } +#endif +} diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs index 47f413409..5900e80b7 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -43,6 +43,10 @@ protected PyObject() ~PyObject() { + // We needs to disable Finalizers until it's valid implementation. + // Current implementation can produce low probability floating bugs. + return; + Dispose(); } @@ -96,6 +100,27 @@ public object AsManagedObject(Type t) } return result; } + + /// + /// As Method + /// + /// + /// Return a managed object of the given type, based on the + /// value of the Python object. + /// + public T As() + { + if (typeof(T) == typeof(PyObject) || typeof(T) == typeof(object)) + { + return (T)(this as object); + } + object result; + if (!Converter.ToManaged(obj, typeof(T), out result, false)) + { + throw new InvalidCastException("cannot convert object to target type"); + } + return (T)result; + } /// @@ -781,7 +806,7 @@ public bool IsCallable() /// public bool IsIterable() { - return Runtime.PyIter_Check(obj); + return Runtime.PyObject_IsIterable(obj); } @@ -915,6 +940,34 @@ public override bool TrySetMember(SetMemberBinder binder, object value) return true; } + private void GetArgs(object[] inargs, CallInfo callInfo, out PyTuple args, out PyDict kwargs) + { + if (callInfo == null || callInfo.ArgumentNames.Count == 0) + { + GetArgs(inargs, out args, out kwargs); + return; + } + + // Support for .net named arguments + var namedArgumentCount = callInfo.ArgumentNames.Count; + var regularArgumentCount = callInfo.ArgumentCount - namedArgumentCount; + + var argTuple = Runtime.PyTuple_New(regularArgumentCount); + for (int i = 0; i < regularArgumentCount; ++i) + { + AddArgument(argTuple, i, inargs[i]); + } + args = new PyTuple(argTuple); + + var namedArgs = new object[namedArgumentCount * 2]; + for (int i = 0; i < namedArgumentCount; ++i) + { + namedArgs[i * 2] = callInfo.ArgumentNames[i]; + namedArgs[i * 2 + 1] = inargs[regularArgumentCount + i]; + } + kwargs = Py.kw(namedArgs); + } + private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs) { int arg_count; @@ -925,22 +978,10 @@ private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs) IntPtr argtuple = Runtime.PyTuple_New(arg_count); for (var i = 0; i < arg_count; i++) { - IntPtr ptr; - if (inargs[i] is PyObject) - { - ptr = ((PyObject)inargs[i]).Handle; - Runtime.XIncref(ptr); - } - else - { - ptr = Converter.ToPython(inargs[i], inargs[i]?.GetType()); - } - if (Runtime.PyTuple_SetItem(argtuple, i, ptr) < 0) - { - throw new PythonException(); - } + AddArgument(argtuple, i, inargs[i]); } args = new PyTuple(argtuple); + kwargs = null; for (int i = arg_count; i < inargs.Length; i++) { @@ -959,6 +1000,32 @@ private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs) } } + private static void AddArgument(IntPtr argtuple, int i, object target) + { + IntPtr ptr = GetPythonObject(target); + + if (Runtime.PyTuple_SetItem(argtuple, i, ptr) < 0) + { + throw new PythonException(); + } + } + + private static IntPtr GetPythonObject(object target) + { + IntPtr ptr; + if (target is PyObject) + { + ptr = ((PyObject)target).Handle; + Runtime.XIncref(ptr); + } + else + { + ptr = Converter.ToPython(target, target?.GetType()); + } + + return ptr; + } + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (this.HasAttr(binder.Name) && this.GetAttr(binder.Name).IsCallable()) @@ -967,7 +1034,7 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, o PyDict kwargs = null; try { - GetArgs(args, out pyargs, out kwargs); + GetArgs(args, binder.CallInfo, out pyargs, out kwargs); result = CheckNone(InvokeMethod(binder.Name, pyargs, kwargs)); } finally @@ -997,7 +1064,7 @@ public override bool TryInvoke(InvokeBinder binder, object[] args, out object re PyDict kwargs = null; try { - GetArgs(args, out pyargs, out kwargs); + GetArgs(args, binder.CallInfo, out pyargs, out kwargs); result = CheckNone(Invoke(pyargs, kwargs)); } finally diff --git a/src/runtime/pyscope.cs b/src/runtime/pyscope.cs new file mode 100644 index 000000000..67f93c6e2 --- /dev/null +++ b/src/runtime/pyscope.cs @@ -0,0 +1,657 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Dynamic; + +namespace Python.Runtime +{ + public class PyScopeException : Exception + { + public PyScopeException(string message) + : base(message) + { + + } + } + + /// + /// Classes/methods have this attribute must be used with GIL obtained. + /// + public class PyGILAttribute : Attribute + { + } + + [PyGIL] + public class PyScope : DynamicObject, IDisposable + { + public readonly string Name; + + /// + /// the python Module object the scope associated with. + /// + internal readonly IntPtr obj; + + /// + /// the variable dict of the scope. + /// + internal readonly IntPtr variables; + + private bool _isDisposed; + + /// + /// The Manager this scope associated with. + /// It provides scopes this scope can import. + /// + internal readonly PyScopeManager Manager; + + /// + /// event which will be triggered after the scope disposed. + /// + public event Action OnDispose; + + /// + /// Constructor + /// + /// + /// Create a scope based on a Python Module. + /// + internal PyScope(IntPtr ptr, PyScopeManager manager) + { + if (!Runtime.PyType_IsSubtype(Runtime.PyObject_TYPE(ptr), Runtime.PyModuleType)) + { + throw new PyScopeException("object is not a module"); + } + Manager = manager ?? PyScopeManager.Global; + obj = ptr; + //Refcount of the variables not increase + variables = Runtime.PyModule_GetDict(obj); + Runtime.CheckExceptionOccurred(); + + Runtime.PyDict_SetItemString( + variables, "__builtins__", + Runtime.PyEval_GetBuiltins() + ); + this.Name = this.Get("__name__"); + } + + /// + /// return the variable dict of the scope. + /// + /// + public PyDict Variables() + { + Runtime.XIncref(variables); + return new PyDict(variables); + } + + /// + /// Create a scope, and import all from this scope + /// + /// + public PyScope NewScope() + { + var scope = Manager.Create(); + scope.ImportAll(this); + return scope; + } + + /// + /// Import method + /// + /// + /// Import a scope or a module of given name, + /// scope will be looked up first. + /// + public dynamic Import(string name, string asname = null) + { + Check(); + if (String.IsNullOrEmpty(asname)) + { + asname = name; + } + PyScope scope; + Manager.TryGet(name, out scope); + if (scope != null) + { + Import(scope, asname); + return scope; + } + else + { + PyObject module = PythonEngine.ImportModule(name); + Import(module, asname); + return module; + } + } + + /// + /// Import method + /// + /// + /// Import a scope as a variable of given name. + /// + public void Import(PyScope scope, string asname) + { + this.Set(asname, scope.obj); + } + + /// + /// Import Method + /// + /// + /// The 'import .. as ..' statement in Python. + /// Import a module as a variable into the scope. + /// + public void Import(PyObject module, string asname = null) + { + if (String.IsNullOrEmpty(asname)) + { + asname = module.GetAttr("__name__").As(); + } + Set(asname, module); + } + + /// + /// ImportAll Method + /// + /// + /// The 'import * from ..' statement in Python. + /// Import all content of a scope/module of given name into the scope, scope will be looked up first. + /// + public void ImportAll(string name) + { + PyScope scope; + Manager.TryGet(name, out scope); + if (scope != null) + { + ImportAll(scope); + return; + } + else + { + PyObject module = PythonEngine.ImportModule(name); + ImportAll(module); + } + } + + /// + /// ImportAll Method + /// + /// + /// Import all variables of the scope into this scope. + /// + public void ImportAll(PyScope scope) + { + int result = Runtime.PyDict_Update(variables, scope.variables); + if (result < 0) + { + throw new PythonException(); + } + } + + /// + /// ImportAll Method + /// + /// + /// Import all variables of the module into this scope. + /// + public void ImportAll(PyObject module) + { + if (Runtime.PyObject_Type(module.obj) != Runtime.PyModuleType) + { + throw new PyScopeException("object is not a module"); + } + var module_dict = Runtime.PyModule_GetDict(module.obj); + int result = Runtime.PyDict_Update(variables, module_dict); + if (result < 0) + { + throw new PythonException(); + } + } + + /// + /// ImportAll Method + /// + /// + /// Import all variables in the dictionary into this scope. + /// + public void ImportAll(PyDict dict) + { + int result = Runtime.PyDict_Update(variables, dict.obj); + if (result < 0) + { + throw new PythonException(); + } + } + + /// + /// Execute method + /// + /// + /// Execute a Python ast and return the result as a PyObject. + /// The ast can be either an expression or stmts. + /// + public PyObject Execute(PyObject script, PyDict locals = null) + { + Check(); + IntPtr _locals = locals == null ? variables : locals.obj; + IntPtr ptr = Runtime.PyEval_EvalCode(script.Handle, variables, _locals); + Runtime.CheckExceptionOccurred(); + if (ptr == Runtime.PyNone) + { + Runtime.XDecref(ptr); + return null; + } + return new PyObject(ptr); + } + + /// + /// Execute method + /// + /// + /// Execute a Python ast and return the result as a PyObject, + /// and convert the result to a Managed Object of given type. + /// The ast can be either an expression or stmts. + /// + public T Execute(PyObject script, PyDict locals = null) + { + Check(); + PyObject pyObj = Execute(script, locals); + if (pyObj == null) + { + return default(T); + } + var obj = pyObj.As(); + return obj; + } + + /// + /// Eval method + /// + /// + /// Evaluate a Python expression and return the result as a PyObject + /// or null if an exception is raised. + /// + public PyObject Eval(string code, PyDict locals = null) + { + Check(); + IntPtr _locals = locals == null ? variables : locals.obj; + var flag = (IntPtr)Runtime.Py_eval_input; + IntPtr ptr = Runtime.PyRun_String( + code, flag, variables, _locals + ); + Runtime.CheckExceptionOccurred(); + return new PyObject(ptr); + } + + /// + /// Evaluate a Python expression + /// + /// + /// Evaluate a Python expression + /// and convert the result to a Managed Object of given type. + /// + public T Eval(string code, PyDict locals = null) + { + Check(); + PyObject pyObj = Eval(code, locals); + var obj = pyObj.As(); + return obj; + } + + /// + /// Exec Method + /// + /// + /// Exec a Python script and save its local variables in the current local variable dict. + /// + public void Exec(string code, PyDict locals = null) + { + Check(); + IntPtr _locals = locals == null ? variables : locals.obj; + Exec(code, variables, _locals); + } + + private void Exec(string code, IntPtr _globals, IntPtr _locals) + { + var flag = (IntPtr)Runtime.Py_file_input; + IntPtr ptr = Runtime.PyRun_String( + code, flag, _globals, _locals + ); + Runtime.CheckExceptionOccurred(); + if (ptr != Runtime.PyNone) + { + throw new PythonException(); + } + Runtime.XDecref(ptr); + } + + /// + /// Set Variable Method + /// + /// + /// Add a new variable to the variables dict if it not exist + /// or update its value if the variable exists. + /// + public void Set(string name, object value) + { + IntPtr _value = Converter.ToPython(value, value?.GetType()); + Set(name, _value); + Runtime.XDecref(_value); + } + + private void Set(string name, IntPtr value) + { + Check(); + using (var pyKey = new PyString(name)) + { + int r = Runtime.PyObject_SetItem(variables, pyKey.obj, value); + if (r < 0) + { + throw new PythonException(); + } + } + } + + /// + /// Remove Method + /// + /// + /// Remove a variable from the variables dict. + /// + public void Remove(string name) + { + Check(); + using (var pyKey = new PyString(name)) + { + int r = Runtime.PyObject_DelItem(variables, pyKey.obj); + if (r < 0) + { + throw new PythonException(); + } + } + } + + /// + /// Contains Method + /// + /// + /// Returns true if the variable exists in the scope. + /// + public bool Contains(string name) + { + Check(); + using (var pyKey = new PyString(name)) + { + return Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0; + } + } + + /// + /// Get Method + /// + /// + /// Returns the value of the variable of given name. + /// If the variable does not exist, throw an Exception. + /// + public PyObject Get(string name) + { + PyObject scope; + var state = TryGet(name, out scope); + if (!state) + { + throw new PyScopeException($"The scope of name '{Name}' has no attribute '{name}'"); + } + return scope; + } + + /// + /// TryGet Method + /// + /// + /// Returns the value of the variable, local variable first. + /// If the variable does not exist, return null. + /// + public bool TryGet(string name, out PyObject value) + { + Check(); + using (var pyKey = new PyString(name)) + { + if (Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0) + { + IntPtr op = Runtime.PyObject_GetItem(variables, pyKey.obj); + if (op == IntPtr.Zero) + { + throw new PythonException(); + } + if (op == Runtime.PyNone) + { + Runtime.XDecref(op); + value = null; + return true; + } + value = new PyObject(op); + return true; + } + else + { + value = null; + return false; + } + } + } + + /// + /// Get Method + /// + /// + /// Obtain the value of the variable of given name, + /// and convert the result to a Managed Object of given type. + /// If the variable does not exist, throw an Exception. + /// + public T Get(string name) + { + Check(); + PyObject pyObj = Get(name); + if (pyObj == null) + { + return default(T); + } + return pyObj.As(); + } + + /// + /// TryGet Method + /// + /// + /// Obtain the value of the variable of given name, + /// and convert the result to a Managed Object of given type. + /// If the variable does not exist, return false. + /// + public bool TryGet(string name, out T value) + { + Check(); + PyObject pyObj; + var result = TryGet(name, out pyObj); + if (!result) + { + value = default(T); + return false; + } + if (pyObj == null) + { + if (typeof(T).IsValueType) + { + throw new PyScopeException($"The value of the attribute '{name}' is None which cannot be convert to '{typeof(T).ToString()}'"); + } + else + { + value = default(T); + return true; + } + } + value = pyObj.As(); + return true; + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + result = this.Get(binder.Name); + return true; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + this.Set(binder.Name, value); + return true; + } + + private void Check() + { + if (_isDisposed) + { + throw new PyScopeException($"The scope of name '{Name}' object has been disposed"); + } + } + + public void Dispose() + { + if (_isDisposed) + { + return; + } + _isDisposed = true; + Runtime.XDecref(obj); + this.OnDispose?.Invoke(this); + } + + ~PyScope() + { + // We needs to disable Finalizers until it's valid implementation. + // Current implementation can produce low probability floating bugs. + return; + + Dispose(); + } + } + + public class PyScopeManager + { + public readonly static PyScopeManager Global = new PyScopeManager(); + + private Dictionary NamedScopes = new Dictionary(); + + internal PyScope NewScope(string name) + { + if (name == null) + { + name = ""; + } + var module = Runtime.PyModule_New(name); + if (module == IntPtr.Zero) + { + throw new PythonException(); + } + return new PyScope(module, this); + } + + /// + /// Create Method + /// + /// + /// Create an anonymous scope. + /// + [PyGIL] + public PyScope Create() + { + var scope = this.NewScope(null); + return scope; + } + + /// + /// Create Method + /// + /// + /// Create an named scope of given name. + /// + [PyGIL] + public PyScope Create(string name) + { + if (String.IsNullOrEmpty(name)) + { + throw new ArgumentNullException(nameof(name)); + } + if (name != null && Contains(name)) + { + throw new PyScopeException($"A scope of name '{name}' does already exist"); + } + var scope = this.NewScope(name); + scope.OnDispose += Remove; + NamedScopes[name] = scope; + return scope; + } + + /// + /// Contains Method + /// + /// + /// return true if the scope exists in this manager. + /// + public bool Contains(string name) + { + return NamedScopes.ContainsKey(name); + } + + /// + /// Get Method + /// + /// + /// Find the scope in this manager. + /// If the scope not exist, an Exception will be thrown. + /// + public PyScope Get(string name) + { + if (String.IsNullOrEmpty(name)) + { + throw new ArgumentNullException(nameof(name)); + } + if (NamedScopes.ContainsKey(name)) + { + return NamedScopes[name]; + } + throw new PyScopeException($"There is no scope named '{name}' registered in this manager"); + } + + /// + /// Get Method + /// + /// + /// Try to find the scope in this manager. + /// + public bool TryGet(string name, out PyScope scope) + { + return NamedScopes.TryGetValue(name, out scope); + } + + /// + /// Remove Method + /// + /// + /// remove the scope from this manager. + /// + public void Remove(PyScope scope) + { + NamedScopes.Remove(scope.Name); + } + + [PyGIL] + public void Clear() + { + var scopes = NamedScopes.Values.ToList(); + foreach (var scope in scopes) + { + scope.Dispose(); + } + } + } +} diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 1fd3b239a..556da698f 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -294,6 +294,7 @@ public static void Shutdown() { if (initialized) { + PyScopeManager.Global.Clear(); Marshal.FreeHGlobal(_pythonHome); _pythonHome = IntPtr.Zero; Marshal.FreeHGlobal(_programName); @@ -421,6 +422,13 @@ public static PyObject ModuleFromString(string name, string code) return new PyObject(m); } + public static PyObject Compile(string code, string filename = "", RunFlagType mode = RunFlagType.File) + { + var flag = (IntPtr)mode; + IntPtr ptr = Runtime.Py_CompileString(code, filename, flag); + Runtime.CheckExceptionOccurred(); + return new PyObject(ptr); + } /// /// Eval Method @@ -539,6 +547,18 @@ public static GILState GIL() return new GILState(); } + public static PyScope CreateScope() + { + var scope = PyScopeManager.Global.Create(); + return scope; + } + + public static PyScope CreateScope(string name) + { + var scope = PyScopeManager.Global.Create(name); + return scope; + } + public class GILState : IDisposable { private IntPtr state; @@ -632,5 +652,34 @@ public static void SetArgv(IEnumerable argv) Runtime.CheckExceptionOccurred(); } } + + public static void With(PyObject obj, Action Body) + { + // Behavior described here: + // https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers + + IntPtr type = Runtime.PyNone; + IntPtr val = Runtime.PyNone; + IntPtr traceBack = Runtime.PyNone; + PythonException ex = null; + + try + { + PyObject enterResult = obj.InvokeMethod("__enter__"); + + Body(enterResult); + } + catch (PythonException e) + { + ex = e; + type = ex.PyType; + val = ex.PyValue; + traceBack = ex.PyTB; + } + + var exitResult = obj.InvokeMethod("__exit__", new PyObject(type), new PyObject(val), new PyObject(traceBack)); + + if (ex != null && !exitResult.IsTrue()) throw ex; + } } } diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs index 4fe07f3cf..4031b0526 100644 --- a/src/runtime/pythonexception.cs +++ b/src/runtime/pythonexception.cs @@ -57,6 +57,10 @@ public PythonException() ~PythonException() { + // We needs to disable Finalizers until it's valid implementation. + // Current implementation can produce low probability floating bugs. + return; + Dispose(); } diff --git a/src/runtime/resources/clr.py b/src/runtime/resources/clr.py index 91f1f6b15..e708a54ac 100644 --- a/src/runtime/resources/clr.py +++ b/src/runtime/resources/clr.py @@ -2,7 +2,7 @@ Code in this module gets loaded into the main clr module. """ -__version__ = "2.3.0" +__version__ = "2.4.0.dev0" class clrproperty(object): diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 346ea745f..abd0661a4 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -9,6 +9,27 @@ namespace Python.Runtime internal static class NativeMethods { #if MONO_LINUX || MONO_OSX +#if NETSTANDARD + private static int RTLD_NOW = 0x2; +#if MONO_LINUX + private static int RTLD_GLOBAL = 0x100; + private static IntPtr RTLD_DEFAULT = IntPtr.Zero; + private const string NativeDll = "libdl.so"; + public static IntPtr LoadLibrary(string fileName) + { + return dlopen($"lib{fileName}.so", RTLD_NOW | RTLD_GLOBAL); + } +#elif MONO_OSX + private static int RTLD_GLOBAL = 0x8; + private const string NativeDll = "/usr/lib/libSystem.dylib" + private static IntPtr RTLD_DEFAULT = new IntPtr(-2); + + public static IntPtr LoadLibrary(string fileName) + { + return dlopen($"lib{fileName}.dylib", RTLD_NOW | RTLD_GLOBAL); + } +#endif +#else private static int RTLD_NOW = 0x2; private static int RTLD_SHARED = 0x20; #if MONO_OSX @@ -23,6 +44,8 @@ public static IntPtr LoadLibrary(string fileName) { return dlopen(fileName, RTLD_NOW | RTLD_SHARED); } +#endif + public static void FreeLibrary(IntPtr handle) { @@ -48,16 +71,16 @@ public static IntPtr GetProcAddress(IntPtr dllHandle, string name) return res; } - [DllImport(NativeDll)] - private static extern IntPtr dlopen(String fileName, int flags); + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public static extern IntPtr dlopen(String fileName, int flags); - [DllImport(NativeDll)] + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private static extern IntPtr dlsym(IntPtr handle, String symbol); - [DllImport(NativeDll)] + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] private static extern int dlclose(IntPtr handle); - [DllImport(NativeDll)] + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr dlerror(); #else // Windows private const string NativeDll = "kernel32.dll"; @@ -80,8 +103,14 @@ public static IntPtr GetProcAddress(IntPtr dllHandle, string name) /// public class Runtime { + // C# compiler copies constants to the assemblies that references this library. + // We needs to replace all public constants to static readonly fields to allow + // binary substitution of different Python.Runtime.dll builds in a target application. + + public static int UCS => _UCS; + #if UCS4 - public const int UCS = 4; + internal const int _UCS = 4; /// /// EntryPoint to be used in DllImport to map to correct Unicode @@ -89,7 +118,7 @@ public class Runtime /// private const string PyUnicodeEntryPoint = "PyUnicodeUCS4_"; #elif UCS2 - public const int UCS = 2; + internal const int _UCS = 2; /// /// EntryPoint to be used in DllImport to map to correct Unicode @@ -100,32 +129,39 @@ public class Runtime #error You must define either UCS2 or UCS4! #endif + // C# compiler copies constants to the assemblies that references this library. + // We needs to replace all public constants to static readonly fields to allow + // binary substitution of different Python.Runtime.dll builds in a target application. + + public static string pyversion => _pyversion; + public static string pyver => _pyver; + #if PYTHON27 - public const string pyversion = "2.7"; - public const string pyver = "27"; + internal const string _pyversion = "2.7"; + internal const string _pyver = "27"; #elif PYTHON33 - public const string pyversion = "3.3"; - public const string pyver = "33"; + internal const string _pyversion = "3.3"; + internal const string _pyver = "33"; #elif PYTHON34 - public const string pyversion = "3.4"; - public const string pyver = "34"; + internal const string _pyversion = "3.4"; + internal const string _pyver = "34"; #elif PYTHON35 - public const string pyversion = "3.5"; - public const string pyver = "35"; + internal const string _pyversion = "3.5"; + internal const string _pyver = "35"; #elif PYTHON36 - public const string pyversion = "3.6"; - public const string pyver = "36"; + internal const string _pyversion = "3.6"; + internal const string _pyver = "36"; #elif PYTHON37 // TODO: Add `interop37.cs` after PY37 is released - public const string pyversion = "3.7"; - public const string pyver = "37"; + internal const string _pyversion = "3.7"; + internal const string _pyver = "37"; #else #error You must define one of PYTHON33 to PYTHON37 or PYTHON27 #endif #if MONO_LINUX || MONO_OSX // Linux/macOS use dotted version string - internal const string dllBase = "python" + pyversion; + internal const string dllBase = "python" + _pyversion; #else // Windows - internal const string dllBase = "python" + pyver; + internal const string dllBase = "python" + _pyver; #endif #if PYTHON_WITH_PYDEBUG @@ -139,26 +175,36 @@ public class Runtime internal const string dllWithPyMalloc = ""; #endif -#if PYTHON_WITHOUT_ENABLE_SHARED - public const string PythonDll = "__Internal"; + // C# compiler copies constants to the assemblies that references this library. + // We needs to replace all public constants to static readonly fields to allow + // binary substitution of different Python.Runtime.dll builds in a target application. + + public static readonly string PythonDLL = _PythonDll; + +#if PYTHON_WITHOUT_ENABLE_SHARED && !NETSTANDARD + internal const string _PythonDll = "__Internal"; #else - public const string PythonDll = dllBase + dllWithPyDebug + dllWithPyMalloc; + internal const string _PythonDll = dllBase + dllWithPyDebug + dllWithPyMalloc; #endif - public static readonly int pyversionnumber = Convert.ToInt32(pyver); + public static readonly int pyversionnumber = Convert.ToInt32(_pyver); // set to true when python is finalizing internal static object IsFinalizingLock = new object(); internal static bool IsFinalizing; internal static bool Is32Bit = IntPtr.Size == 4; + + // .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; + internal static bool IsPython2 = pyversionnumber < 30; internal static bool IsPython3 = pyversionnumber >= 30; /// /// Encoding to use to convert Unicode to/from Managed to Native /// - internal static readonly Encoding PyEncoding = UCS == 2 ? Encoding.Unicode : Encoding.UTF32; + internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32; /// /// Initialize the runtime... @@ -274,19 +320,19 @@ internal static void Initialize() Error = new IntPtr(-1); -#if PYTHON3 IntPtr dllLocal = IntPtr.Zero; - if (PythonDll != "__Internal") + + if (_PythonDll != "__Internal") { - NativeMethods.LoadLibrary(PythonDll); + dllLocal = NativeMethods.LoadLibrary(_PythonDll); } _PyObject_NextNotImplemented = NativeMethods.GetProcAddress(dllLocal, "_PyObject_NextNotImplemented"); + #if !(MONO_LINUX || MONO_OSX) if (dllLocal != IntPtr.Zero) { NativeMethods.FreeLibrary(dllLocal); } -#endif #endif // Initialize modules that depend on the runtime class. @@ -349,8 +395,8 @@ internal static int AtExit() #if PYTHON3 internal static IntPtr PyBytesType; - internal static IntPtr _PyObject_NextNotImplemented; #endif + internal static IntPtr _PyObject_NextNotImplemented; internal static IntPtr PyNotImplemented; internal const int Py_LT = 0; @@ -380,29 +426,6 @@ internal static void CheckExceptionOccurred() } } - internal static IntPtr GetBoundArgTuple(IntPtr obj, IntPtr args) - { - if (PyObject_TYPE(args) != PyTupleType) - { - Exceptions.SetError(Exceptions.TypeError, "tuple expected"); - return IntPtr.Zero; - } - int size = PyTuple_Size(args); - IntPtr items = PyTuple_New(size + 1); - PyTuple_SetItem(items, 0, obj); - XIncref(obj); - - for (var i = 0; i < size; i++) - { - IntPtr item = PyTuple_GetItem(args, i); - XIncref(item); - PyTuple_SetItem(items, i + 1, item); - } - - return items; - } - - internal static IntPtr ExtendTuple(IntPtr t, params IntPtr[] args) { int size = PyTuple_Size(t); @@ -499,7 +522,7 @@ internal static Type[] PythonArgsToTypeArray(IntPtr arg, bool mangleObjects) /// internal static unsafe void XIncref(IntPtr op) { -#if PYTHON_WITH_PYDEBUG +#if PYTHON_WITH_PYDEBUG || NETSTANDARD Py_IncRef(op); return; #else @@ -520,7 +543,7 @@ internal static unsafe void XIncref(IntPtr op) internal static unsafe void XDecref(IntPtr op) { -#if PYTHON_WITH_PYDEBUG +#if PYTHON_WITH_PYDEBUG || NETSTANDARD Py_DecRef(op); return; #else @@ -570,7 +593,7 @@ internal static unsafe long Refcount(IntPtr op) /// Limit this function usage for Testing and Py_Debug builds /// /// PyObject Ptr - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_IncRef(IntPtr ob); /// @@ -578,157 +601,160 @@ internal static unsafe long Refcount(IntPtr op) /// Limit this function usage for Testing and Py_Debug builds /// /// PyObject Ptr - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_DecRef(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_Initialize(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int Py_IsInitialized(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_Finalize(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_NewInterpreter(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_EndInterpreter(IntPtr threadState); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyThreadState_New(IntPtr istate); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyThreadState_Get(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyThread_get_key_value(IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyThread_get_thread_ident(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyThread_set_key_value(IntPtr key, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyThreadState_Swap(IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyGILState_Ensure(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyGILState_Release(IntPtr gs); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyGILState_GetThisThreadState(); #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] public static extern int Py_Main( int argc, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StrArrayMarshaler))] string[] argv ); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] public static extern int Py_Main(int argc, string[] argv); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_InitThreads(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyEval_ThreadsInitialized(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_AcquireLock(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_ReleaseLock(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_AcquireThread(IntPtr tstate); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_ReleaseThread(IntPtr tstate); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyEval_SaveThread(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_RestoreThread(IntPtr tstate); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyEval_GetBuiltins(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyEval_GetGlobals(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyEval_GetLocals(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetProgramName(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_SetProgramName(IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetPythonHome(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_SetPythonHome(IntPtr home); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetPath(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_SetPath(IntPtr home); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetVersion(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetPlatform(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetCopyright(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetCompiler(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetBuildInfo(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyRun_SimpleString(string code); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_CompileString(string code, string file, IntPtr tok); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_ExecCodeModule(string name, IntPtr code); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyCFunction_NewEx(IntPtr ml, IntPtr self, IntPtr mod); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyCFunction_Call(IntPtr func, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyClass_New(IntPtr bases, IntPtr dict, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyInstance_New(IntPtr cls, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyInstance_NewRaw(IntPtr cls, IntPtr dict); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyMethod_New(IntPtr func, IntPtr self, IntPtr cls); @@ -777,44 +803,59 @@ internal static string PyObject_GetTypeName(IntPtr op) return Marshal.PtrToStringAnsi(ppName); } - [DllImport(PythonDll)] + /// + /// Test whether the Python object is an iterable. + /// + internal static bool PyObject_IsIterable(IntPtr pointer) + { + var ob_type = Marshal.ReadIntPtr(pointer, ObjectOffset.ob_type); +#if PYTHON2 + long tp_flags = Util.ReadCLong(ob_type, TypeOffset.tp_flags); + if ((tp_flags & TypeFlags.HaveIter) == 0) + return false; +#endif + IntPtr tp_iter = Marshal.ReadIntPtr(ob_type, TypeOffset.tp_iter); + return tp_iter != IntPtr.Zero; + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_HasAttrString(IntPtr pointer, string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GetAttrString(IntPtr pointer, string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_SetAttrString(IntPtr pointer, string name, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_HasAttr(IntPtr pointer, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GetAttr(IntPtr pointer, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_SetAttr(IntPtr pointer, IntPtr name, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GetItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_SetItem(IntPtr pointer, IntPtr key, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_DelItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GetIter(IntPtr op); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Call(IntPtr pointer, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_CallObject(IntPtr pointer, IntPtr args); #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_RichCompareBool(IntPtr value1, IntPtr value2, int opid); internal static int PyObject_Compare(IntPtr value1, IntPtr value2) @@ -842,46 +883,47 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2) return -1; } #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_Compare(IntPtr value1, IntPtr value2); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_IsInstance(IntPtr ob, IntPtr type); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_IsSubclass(IntPtr ob, IntPtr type); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyCallable_Check(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_IsTrue(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_Not(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_Size(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Hash(IntPtr op); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Repr(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Str(IntPtr pointer); #if PYTHON3 - [DllImport(PythonDll, EntryPoint = "PyObject_Str")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyObject_Str")] internal static extern IntPtr PyObject_Unicode(IntPtr pointer); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Unicode(IntPtr pointer); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Dir(IntPtr pointer); @@ -890,20 +932,21 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2) //==================================================================== #if PYTHON3 - [DllImport(PythonDll, EntryPoint = "PyNumber_Long")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyNumber_Long")] internal static extern IntPtr PyNumber_Int(IntPtr ob); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Int(IntPtr ob); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Long(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Float(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern bool PyNumber_Check(IntPtr ob); internal static bool PyInt_Check(IntPtr ob) @@ -929,28 +972,32 @@ internal static IntPtr PyInt_FromInt64(long value) } #if PYTHON3 - [DllImport(PythonDll, EntryPoint = "PyLong_FromLong")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_FromLong")] private static extern IntPtr PyInt_FromLong(IntPtr value); - [DllImport(PythonDll, EntryPoint = "PyLong_AsLong")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_AsLong")] internal static extern int PyInt_AsLong(IntPtr value); - [DllImport(PythonDll, EntryPoint = "PyLong_FromString")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_FromString")] internal static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); - [DllImport(PythonDll, EntryPoint = "PyLong_GetMax")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_GetMax")] internal static extern int PyInt_GetMax(); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr PyInt_FromLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyInt_AsLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyInt_GetMax(); #endif @@ -959,34 +1006,34 @@ internal static bool PyLong_Check(IntPtr ob) return PyObject_TYPE(ob) == PyLongType; } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromLong(long value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromUnsignedLong(uint value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromDouble(double value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromLongLong(long value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromUnsignedLongLong(ulong value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromString(string value, IntPtr end, int radix); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyLong_AsLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern uint PyLong_AsUnsignedLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern long PyLong_AsLongLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern ulong PyLong_AsUnsignedLongLong(IntPtr value); internal static bool PyFloat_Check(IntPtr ob) @@ -994,88 +1041,88 @@ internal static bool PyFloat_Check(IntPtr ob) return PyObject_TYPE(ob) == PyFloatType; } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyFloat_FromDouble(double value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyFloat_FromString(IntPtr value, IntPtr junk); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern double PyFloat_AsDouble(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Add(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Subtract(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Multiply(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Divide(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_And(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Xor(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Or(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Lshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Rshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Power(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Remainder(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceAdd(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceSubtract(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceMultiply(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceDivide(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceAnd(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceXor(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceOr(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceLshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceRshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlacePower(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceRemainder(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Negative(IntPtr o1); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Positive(IntPtr o1); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Invert(IntPtr o1); @@ -1083,49 +1130,49 @@ internal static bool PyFloat_Check(IntPtr ob) // Python sequence API //==================================================================== - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern bool PySequence_Check(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySequence_GetItem(IntPtr pointer, int index); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySequence_SetItem(IntPtr pointer, int index, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySequence_DelItem(IntPtr pointer, int index); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySequence_GetSlice(IntPtr pointer, int i1, int i2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySequence_SetSlice(IntPtr pointer, int i1, int i2, IntPtr v); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySequence_DelSlice(IntPtr pointer, int i1, int i2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySequence_Size(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySequence_Contains(IntPtr pointer, IntPtr item); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySequence_Concat(IntPtr pointer, IntPtr other); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySequence_Repeat(IntPtr pointer, int count); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySequence_Index(IntPtr pointer, IntPtr item); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySequence_Count(IntPtr pointer, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySequence_Tuple(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySequence_List(IntPtr pointer); @@ -1150,10 +1197,10 @@ internal static IntPtr PyString_FromString(string value) } #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyBytes_FromString(string op); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyBytes_Size(IntPtr op); internal static IntPtr PyBytes_AS_STRING(IntPtr ob) @@ -1161,22 +1208,23 @@ internal static IntPtr PyBytes_AS_STRING(IntPtr ob) return ob + BytesOffset.ob_sval; } - [DllImport(PythonDll, EntryPoint = "PyUnicode_FromStringAndSize")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicode_FromStringAndSize")] internal static extern IntPtr PyString_FromStringAndSize( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string value, int size ); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_FromStringAndSize(IntPtr value, int size); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyString_FromStringAndSize(string value, int size); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyString_AsString(IntPtr op); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyString_Size(IntPtr pointer); #endif @@ -1186,13 +1234,13 @@ internal static bool PyUnicode_Check(IntPtr ob) } #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_FromObject(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_FromKindAndData( int kind, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UcsMarshaler))] string s, @@ -1201,37 +1249,43 @@ int size internal static IntPtr PyUnicode_FromUnicode(string s, int size) { - return PyUnicode_FromKindAndData(UCS, s, size); + return PyUnicode_FromKindAndData(_UCS, s, size); } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyUnicode_GetSize(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_AsUnicode(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_FromOrdinal(int c); #elif PYTHON2 - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromObject")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromObject")] internal static extern IntPtr PyUnicode_FromObject(IntPtr ob); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromEncodedObject")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromEncodedObject")] internal static extern IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromUnicode")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromUnicode")] internal static extern IntPtr PyUnicode_FromUnicode( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UcsMarshaler))] string s, int size ); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "GetSize")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "GetSize")] internal static extern int PyUnicode_GetSize(IntPtr ob); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "AsUnicode")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "AsUnicode")] internal static extern IntPtr PyUnicode_AsUnicode(IntPtr ob); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromOrdinal")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromOrdinal")] internal static extern IntPtr PyUnicode_FromOrdinal(int c); #endif @@ -1269,7 +1323,7 @@ internal static string GetManagedString(IntPtr op) IntPtr p = PyUnicode_AsUnicode(op); int length = PyUnicode_GetSize(op); - int size = length * UCS; + int size = length * _UCS; var buffer = new byte[size]; Marshal.Copy(p, buffer, 0, size); return PyEncoding.GetString(buffer, 0, size); @@ -1288,52 +1342,52 @@ internal static bool PyDict_Check(IntPtr ob) return PyObject_TYPE(ob) == PyDictType; } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_New(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDictProxy_New(IntPtr dict); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_GetItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_GetItemString(IntPtr pointer, string key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_SetItem(IntPtr pointer, IntPtr key, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_SetItemString(IntPtr pointer, string key, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_DelItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_DelItemString(IntPtr pointer, string key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyMapping_HasKey(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_Keys(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_Values(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_Items(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_Copy(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_Update(IntPtr pointer, IntPtr other); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyDict_Clear(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_Size(IntPtr pointer); @@ -1346,37 +1400,37 @@ internal static bool PyList_Check(IntPtr ob) return PyObject_TYPE(ob) == PyListType; } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyList_New(int size); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyList_AsTuple(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyList_GetItem(IntPtr pointer, int index); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_SetItem(IntPtr pointer, int index, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_Insert(IntPtr pointer, int index, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_Append(IntPtr pointer, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_Reverse(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_Sort(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyList_GetSlice(IntPtr pointer, int start, int end); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_SetSlice(IntPtr pointer, int start, int end, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_Size(IntPtr pointer); @@ -1389,19 +1443,19 @@ internal static bool PyTuple_Check(IntPtr ob) return PyObject_TYPE(ob) == PyTupleType; } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyTuple_New(int size); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyTuple_GetItem(IntPtr pointer, int index); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyTuple_SetItem(IntPtr pointer, int index, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyTuple_GetSlice(IntPtr pointer, int start, int end); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyTuple_Size(IntPtr pointer); @@ -1409,19 +1463,19 @@ internal static bool PyTuple_Check(IntPtr ob) // Python iterator API //==================================================================== -#if PYTHON2 - [DllImport(PythonDll)] - internal static extern bool PyIter_Check(IntPtr pointer); -#elif PYTHON3 internal static bool PyIter_Check(IntPtr pointer) { - var 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; - } + var ob_type = Marshal.ReadIntPtr(pointer, ObjectOffset.ob_type); +#if PYTHON2 + long tp_flags = Util.ReadCLong(ob_type, TypeOffset.tp_flags); + if ((tp_flags & TypeFlags.HaveIter) == 0) + return false; #endif + IntPtr tp_iternext = Marshal.ReadIntPtr(ob_type, TypeOffset.tp_iternext); + return tp_iternext != IntPtr.Zero && tp_iternext != _PyObject_NextNotImplemented; + } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyIter_Next(IntPtr pointer); @@ -1429,47 +1483,47 @@ internal static bool PyIter_Check(IntPtr pointer) // Python module API //==================================================================== - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyModule_New(string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern string PyModule_GetName(IntPtr module); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyModule_GetDict(IntPtr module); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern string PyModule_GetFilename(IntPtr module); #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyModule_Create2(IntPtr module, int apiver); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_Import(IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_ImportModule(string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_ReloadModule(IntPtr module); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_AddModule(string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_GetModuleDict(); #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PySys_SetArgvEx( int argc, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StrArrayMarshaler))] string[] argv, int updatepath ); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PySys_SetArgvEx( int argc, string[] argv, @@ -1477,10 +1531,10 @@ int updatepath ); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySys_GetObject(string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySys_SetObject(string name, IntPtr ob); @@ -1493,10 +1547,10 @@ internal static bool PyType_Check(IntPtr ob) return PyObject_TypeCheck(ob, PyTypeType); } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyType_Modified(IntPtr type); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern bool PyType_IsSubtype(IntPtr t1, IntPtr t2); internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) @@ -1505,37 +1559,37 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) return (t == tp) || PyType_IsSubtype(t, tp); } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyType_GenericAlloc(IntPtr type, int n); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyType_Ready(IntPtr type); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr _PyType_Lookup(IntPtr type, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GenericGetAttr(IntPtr obj, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_GenericSetAttr(IntPtr obj, IntPtr name, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr _PyObject_GetDictPtr(IntPtr obj); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GC_New(IntPtr tp); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyObject_GC_Del(IntPtr tp); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyObject_GC_Track(IntPtr tp); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyObject_GC_UnTrack(IntPtr tp); @@ -1543,13 +1597,13 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) // Python memory API //==================================================================== - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyMem_Malloc(int size); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyMem_Realloc(IntPtr ptr, int size); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyMem_Free(IntPtr ptr); @@ -1557,40 +1611,40 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) // Python exception API //==================================================================== - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_SetString(IntPtr ob, string message); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_SetObject(IntPtr ob, IntPtr message); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyErr_SetFromErrno(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_SetNone(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyErr_ExceptionMatches(IntPtr exception); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyErr_GivenExceptionMatches(IntPtr ob, IntPtr val); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_NormalizeException(IntPtr ob, IntPtr val, IntPtr tb); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyErr_Occurred(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_Fetch(ref IntPtr ob, ref IntPtr val, ref IntPtr tb); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_Restore(IntPtr ob, IntPtr val, IntPtr tb); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_Clear(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_Print(); @@ -1598,10 +1652,10 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) // Miscellaneous //==================================================================== - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyMethod_Self(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyMethod_Function(IntPtr ob); } } diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index 6f373f036..6570ee083 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Reflection; @@ -86,7 +86,7 @@ internal static IntPtr CreateType(Type impl) int flags = TypeFlags.Default | TypeFlags.Managed | TypeFlags.HeapType | TypeFlags.HaveGC; - Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + Util.WriteCLong(type, TypeOffset.tp_flags, flags); Runtime.PyType_Ready(type); @@ -160,7 +160,7 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) flags |= TypeFlags.HeapType; flags |= TypeFlags.BaseType; flags |= TypeFlags.HaveGC; - Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + Util.WriteCLong(type, TypeOffset.tp_flags, flags); // Leverage followup initialization from the Python runtime. Note // that the type of the new type must PyType_Type at the time we @@ -206,6 +206,7 @@ internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr if (0 != Runtime.PyMapping_HasKey(py_dict, assemblyKey.Handle)) { var pyAssembly = new PyObject(Runtime.PyDict_GetItem(py_dict, assemblyKey.Handle)); + Runtime.XIncref(pyAssembly.Handle); disposeList.Add(pyAssembly); if (!Converter.ToManagedValue(pyAssembly.Handle, typeof(string), out assembly, false)) { @@ -218,6 +219,7 @@ internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr if (0 != Runtime.PyMapping_HasKey(py_dict, namespaceKey.Handle)) { var pyNamespace = new PyObject(Runtime.PyDict_GetItem(py_dict, namespaceKey.Handle)); + Runtime.XIncref(pyNamespace.Handle); disposeList.Add(pyNamespace); if (!Converter.ToManagedValue(pyNamespace.Handle, typeof(string), out namespaceStr, false)) { @@ -321,7 +323,7 @@ internal static IntPtr CreateMetaType(Type impl) flags |= TypeFlags.Managed; flags |= TypeFlags.HeapType; flags |= TypeFlags.HaveGC; - Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + Util.WriteCLong(type, TypeOffset.tp_flags, flags); // We need space for 3 PyMethodDef structs, each of them // 4 int-ptrs in size. @@ -378,7 +380,7 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl) flags |= TypeFlags.Managed; flags |= TypeFlags.HeapType; flags |= TypeFlags.HaveGC; - Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); + Util.WriteCLong(type, TypeOffset.tp_flags, flags); CopySlot(base_, type, TypeOffset.tp_traverse); CopySlot(base_, type, TypeOffset.tp_clear); diff --git a/src/testing/Python.Test.15.csproj b/src/testing/Python.Test.15.csproj new file mode 100644 index 000000000..da20ed2ef --- /dev/null +++ b/src/testing/Python.Test.15.csproj @@ -0,0 +1,86 @@ + + + + net40;netstandard2.0 + x64;x86 + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + Python.Test + Python.Test + Python.Test + 2.4.0 + bin\ + false + $(OutputPath)\$(AssemblyName).xml + $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml + 1591,0067 + ..\..\ + $(SolutionDir)\bin\ + $(PythonBuildDir)\$(TargetFramework)\ + 6 + false + ..\pythonnet.snk + prompt + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);TRACE;DEBUG + $(NuGetPackageRoot)\microsoft.targetingpack.netframework.v4.5\1.0.1\lib\net45\ + + + x86 + + + x64 + + + + false + full + + + true + pdbonly + + + true + false + full + + + true + true + portable + + + + $(DefineConstants);DEBUG;TRACE + + + $(DefineConstants) + + + + + + + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + diff --git a/src/testing/Python.Test.csproj b/src/testing/Python.Test.csproj index ce8dca10d..8a8d9ed2b 100644 --- a/src/testing/Python.Test.csproj +++ b/src/testing/Python.Test.csproj @@ -108,6 +108,6 @@ - + diff --git a/src/testing/conversiontest.cs b/src/testing/conversiontest.cs index 7179c7607..36294594c 100644 --- a/src/testing/conversiontest.cs +++ b/src/testing/conversiontest.cs @@ -32,8 +32,14 @@ public ConversionTest() public byte[] ByteArrayField; public sbyte[] SByteArrayField; + + public T? Echo(T? arg) where T: struct { + return arg; + } + } + public interface ISpam { diff --git a/src/tests/fixtures/netstandard2.0/.gitkeep b/src/tests/fixtures/netstandard2.0/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/tests/test_clrmethod.py b/src/tests/test_clrmethod.py new file mode 100644 index 000000000..a6078bece --- /dev/null +++ b/src/tests/test_clrmethod.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +"""Test clrmethod and clrproperty support for calling methods and getting/setting python properties from CLR.""" + +import Python.Test as Test +import System +import pytest +import clr + +class ExampleClrClass(System.Object): + __namespace__ = "PyTest" + def __init__(self): + self._x = 3 + @clr.clrmethod(int, [int]) + def test(self, x): + return x*2 + + def get_X(self): + return self._x + def set_X(self, value): + self._x = value + X = clr.clrproperty(int, get_X, set_X) + + @clr.clrproperty(int) + def Y(self): + return self._x * 2 + +def test_set_and_get_property_from_py(): + """Test setting and getting clr-accessible properties from python.""" + t = ExampleClrClass() + assert t.X == 3 + assert t.Y == 3 * 2 + t.X = 4 + assert t.X == 4 + assert t.Y == 4 * 2 + +def test_set_and_get_property_from_clr(): + """Test setting and getting clr-accessible properties from the clr.""" + t = ExampleClrClass() + assert t.GetType().GetProperty("X").GetValue(t) == 3 + assert t.GetType().GetProperty("Y").GetValue(t) == 3 * 2 + t.GetType().GetProperty("X").SetValue(t, 4) + assert t.GetType().GetProperty("X").GetValue(t) == 4 + assert t.GetType().GetProperty("Y").GetValue(t) == 4 * 2 + + +def test_set_and_get_property_from_clr_and_py(): + """Test setting and getting clr-accessible properties alternatingly from the clr and from python.""" + t = ExampleClrClass() + assert t.GetType().GetProperty("X").GetValue(t) == 3 + assert t.GetType().GetProperty("Y").GetValue(t) == 3 * 2 + assert t.X == 3 + assert t.Y == 3 * 2 + t.GetType().GetProperty("X").SetValue(t, 4) + assert t.GetType().GetProperty("X").GetValue(t) == 4 + assert t.GetType().GetProperty("Y").GetValue(t) == 4 * 2 + assert t.X == 4 + assert t.Y == 4 * 2 + t.X = 5 + assert t.GetType().GetProperty("X").GetValue(t) == 5 + assert t.GetType().GetProperty("Y").GetValue(t) == 5 * 2 + assert t.X == 5 + assert t.Y == 5 * 2 + +def test_method_invocation_from_py(): + """Test calling a clr-accessible method from python.""" + t = ExampleClrClass() + assert t.test(41) == 41*2 + +def test_method_invocation_from_clr(): + """Test calling a clr-accessible method from the clr.""" + t = ExampleClrClass() + assert t.GetType().GetMethod("test").Invoke(t, [37]) == 37*2 diff --git a/src/tests/test_conversion.py b/src/tests/test_conversion.py index ac263ef5d..53e5d8051 100644 --- a/src/tests/test_conversion.py +++ b/src/tests/test_conversion.py @@ -466,17 +466,6 @@ def test_double_conversion(): with pytest.raises(TypeError): ConversionTest().DoubleField = None - with pytest.raises(OverflowError): - ConversionTest().DoubleField = 1.7976931348623159e308 - - with pytest.raises(OverflowError): - ConversionTest().DoubleField = -1.7976931348623159e308 - - with pytest.raises(OverflowError): - _ = System.Double(1.7976931348623159e308) - - with pytest.raises(OverflowError): - _ = System.Double(-1.7976931348623159e308) def test_decimal_conversion(): @@ -641,6 +630,8 @@ def test_enum_conversion(): def test_null_conversion(): """Test null conversion.""" + import System + ob = ConversionTest() ob.StringField = None @@ -652,6 +643,10 @@ def test_null_conversion(): ob.SpamField = None assert ob.SpamField is None + pi = 22/7 + assert ob.Echo[System.Double](pi) == pi + assert ob.Echo[System.DateTime](None) is None + # Primitive types and enums should not be set to null. with pytest.raises(TypeError): diff --git a/src/tests/test_module.py b/src/tests/test_module.py index 2255ea411..e02aa6e01 100644 --- a/src/tests/test_module.py +++ b/src/tests/test_module.py @@ -352,6 +352,26 @@ def test_clr_add_reference(): with pytest.raises(FileNotFoundException): AddReference("somethingtotallysilly") +def test_clr_get_clr_type(): + """Test clr.GetClrType().""" + from clr import GetClrType + import System + from System import IComparable + from System import ArgumentException + assert GetClrType(System.String).FullName == "System.String" + comparable = GetClrType(IComparable) + assert comparable.FullName == "System.IComparable" + assert comparable.IsInterface + assert GetClrType(int).FullName == "System.Int32" + assert GetClrType(str).FullName == "System.String" + assert GetClrType(float).FullName == "System.Double" + dblarr = System.Array[System.Double] + assert GetClrType(dblarr).FullName == "System.Double[]" + + with pytest.raises(TypeError): + GetClrType(1) + with pytest.raises(TypeError): + GetClrType("thiswillfail") def test_assembly_load_thread_safety(): from Python.Test import ModuleTest diff --git a/src/tests/test_subclass.py b/src/tests/test_subclass.py index 8e862a56d..43d013c7c 100644 --- a/src/tests/test_subclass.py +++ b/src/tests/test_subclass.py @@ -15,12 +15,12 @@ from ._compat import range -def interface_test_class_fixture(): +def interface_test_class_fixture(subnamespace): """Delay creation of class until test starts.""" class InterfaceTestClass(IInterfaceTest): """class that implements the test interface""" - __namespace__ = "Python.Test" + __namespace__ = "Python.Test." + subnamespace def foo(self): return "InterfaceTestClass" @@ -31,12 +31,12 @@ def bar(self, x, i): return InterfaceTestClass -def derived_class_fixture(): +def derived_class_fixture(subnamespace): """Delay creation of class until test starts.""" class DerivedClass(SubClassTest): """class that derives from a class deriving from IInterfaceTest""" - __namespace__ = "Python.Test" + __namespace__ = "Python.Test." + subnamespace def foo(self): return "DerivedClass" @@ -60,12 +60,12 @@ def return_list(self): return DerivedClass -def derived_event_test_class_fixture(): +def derived_event_test_class_fixture(subnamespace): """Delay creation of class until test starts.""" class DerivedEventTest(IInterfaceTest): """class that implements IInterfaceTest.TestEvent""" - __namespace__ = "Python.Test" + __namespace__ = "Python.Test." + subnamespace def __init__(self): self.event_handlers = [] @@ -85,7 +85,6 @@ def OnTestEvent(self, value): return DerivedEventTest -@pytest.mark.skip(reason="FIXME: test randomly pass/fails") def test_base_class(): """Test base class managed type""" ob = SubClassTest() @@ -98,10 +97,9 @@ def test_base_class(): assert list(SubClassTest.test_list(ob)) == ["a", "b", "c"] -@pytest.mark.skip(reason="FIXME: test randomly pass/fails") def test_interface(): """Test python classes can derive from C# interfaces""" - InterfaceTestClass = interface_test_class_fixture() + InterfaceTestClass = interface_test_class_fixture(test_interface.__name__) ob = InterfaceTestClass() assert ob.foo() == "InterfaceTestClass" assert FunctionsTest.test_foo(ob) == "InterfaceTestClass" @@ -112,10 +110,9 @@ def test_interface(): assert id(x) == id(ob) -@pytest.mark.skip(reason="FIXME: test randomly pass/fails") def test_derived_class(): """Test python class derived from managed type""" - DerivedClass = derived_class_fixture() + DerivedClass = derived_class_fixture(test_derived_class.__name__) ob = DerivedClass() assert ob.foo() == "DerivedClass" assert ob.base_foo() == "foo" @@ -131,10 +128,9 @@ def test_derived_class(): assert id(x) == id(ob) -@pytest.mark.skip(reason="FIXME: test randomly pass/fails") def test_create_instance(): """Test derived instances can be created from managed code""" - DerivedClass = derived_class_fixture() + DerivedClass = derived_class_fixture(test_create_instance.__name__) ob = FunctionsTest.create_instance(DerivedClass) assert ob.foo() == "DerivedClass" assert FunctionsTest.test_foo(ob) == "DerivedClass" @@ -145,7 +141,7 @@ def test_create_instance(): x = FunctionsTest.pass_through(ob) assert id(x) == id(ob) - InterfaceTestClass = interface_test_class_fixture() + InterfaceTestClass = interface_test_class_fixture(test_create_instance.__name__) ob2 = FunctionsTest.create_instance(InterfaceTestClass) assert ob2.foo() == "InterfaceTestClass" assert FunctionsTest.test_foo(ob2) == "InterfaceTestClass" @@ -156,7 +152,6 @@ def test_create_instance(): assert id(y) == id(ob2) -@pytest.mark.skip(reason="FIXME: test randomly pass/fails") def test_events(): class EventHandler(object): def handler(self, x, args): @@ -169,12 +164,12 @@ def handler(self, x, args): assert FunctionsTest.test_event(x, 1) == 1 assert event_handler.value == 1 - InterfaceTestClass = interface_test_class_fixture() + InterfaceTestClass = interface_test_class_fixture(test_events.__name__) i = InterfaceTestClass() with pytest.raises(System.NotImplementedException): FunctionsTest.test_event(i, 2) - DerivedEventTest = derived_event_test_class_fixture() + DerivedEventTest = derived_event_test_class_fixture(test_events.__name__) d = DerivedEventTest() d.add_TestEvent(event_handler.handler) assert FunctionsTest.test_event(d, 3) == 3 @@ -193,3 +188,59 @@ def test_isinstance_check(): for x in b: assert isinstance(x, System.Object) assert isinstance(x, System.String) + +def test_namespace_and_init(): + calls = [] + class TestX(System.Object): + __namespace__ = "test_clr_subclass_with_init_args" + def __init__(self, *args, **kwargs): + calls.append((args, kwargs)) + t = TestX(1,2,3,foo="bar") + assert len(calls) == 1 + assert calls[0][0] == (1,2,3) + assert calls[0][1] == {"foo":"bar"} + +def test_namespace_and_argless_init(): + calls = [] + class TestX(System.Object): + __namespace__ = "test_clr_subclass_without_init_args" + def __init__(self): + calls.append(True) + t = TestX() + assert len(calls) == 1 + assert calls[0] == True + + +def test_namespace_and_no_init(): + class TestX(System.Object): + __namespace__ = "test_clr_subclass_without_init" + q = 1 + t = TestX() + assert t.q == 1 + +def test_construction_from_clr(): + import clr + calls = [] + class TestX(System.Object): + __namespace__ = "test_clr_subclass_init_from_clr" + @clr.clrmethod(None, [int, str]) + def __init__(self, i, s): + calls.append((i, s)) + + # Construct a TestX from Python + t = TestX(1, "foo") + assert len(calls) == 1 + assert calls[0][0] == 1 + assert calls[0][1] == "foo" + + # Reset calls and construct a TestX from CLR + calls = [] + tp = t.GetType() + t2 = tp.GetConstructors()[0].Invoke(None) + assert len(calls) == 0 + + # The object has only been constructed, now it needs to be initialized as well + tp.GetMethod("__init__").Invoke(t2, [1, "foo"]) + assert len(calls) == 1 + assert calls[0][0] == 1 + assert calls[0][1] == "foo" diff --git a/tools/nuget/nuget.exe b/tools/nuget/nuget.exe index e42e6d827..463f8e137 100644 Binary files a/tools/nuget/nuget.exe and b/tools/nuget/nuget.exe differ diff --git a/tools/vswhere/vswhere.exe b/tools/vswhere/vswhere.exe new file mode 100644 index 000000000..3eb2df009 Binary files /dev/null and b/tools/vswhere/vswhere.exe differ