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/.gitignore b/.gitignore index 6f813dcb0..1e494b12d 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,10 @@ UpgradeLog*.htm # Coverity cov-int/ + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json 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..1dadbad1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,78 @@ +dist: trusty sudo: false - language: python -python: - - 2.7 - - 3.3 - - 3.4 - - 3.5 - - 3.6 - - 3.7-dev - + matrix: - allow_failures: - - python: 3.7-dev + 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/ubuntu trusty 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.5 + env: *xplat-env + addons: *xplat-addons + + - python: 3.6 + env: *xplat-env + addons: *xplat-addons + + - python: 3.7 + env: *xplat-env + dist: xenial + sudo: true + addons: &xplat-addons-xenial + apt: + sources: + - sourceline: deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main + key_url: https://packages.microsoft.com/keys/microsoft.asc + - sourceline: deb https://download.mono-project.com/repo/ubuntu stable-xenial 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 + +# --------------------- Classic builds ------------------------ + - python: 2.7 + env: &classic-env + - BUILD_OPTS= + - NUNIT_PATH=./packages/NUnit.*/tools/nunit3-console.exe + + - python: 3.5 + env: *classic-env + + - python: 3.6 + env: *classic-env + + - python: 3.7 + env: *classic-env + dist: xenial + sudo: true + addons: + apt: + sources: + - sourceline: deb http://download.mono-project.com/repo/ubuntu xenial main + key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF + packages: + - mono-devel + - ca-certificates-mono env: global: @@ -23,8 +84,8 @@ env: addons: apt: sources: - - mono - - mono-libtiff-compat + - sourceline: deb http://download.mono-project.com/repo/ubuntu trusty main + key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF packages: - mono-devel - ca-certificates-mono @@ -35,12 +96,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..ba954b47d 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -12,31 +12,53 @@ ## Contributors +- Alexandre Catarino([@AlexCatarino](https://github.com/AlexCatarino)) - Arvid JB ([@ArvidJB](https://github.com/ArvidJB)) +- Benoît Hudson ([@benoithudson](https://github.com/benoithudson)) - Bradley Friedman ([@leith-bartrich](https://github.com/leith-bartrich)) +- Callum Noble ([@callumnoble](https://github.com/callumnoble)) - 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 Lassonde ([@lassond](https://github.com/lassond)) - David Lechner ([@dlech](https://github.com/dlech)) - Dmitriy Se ([@dmitriyse](https://github.com/dmitriyse)) - He-chien Tsai ([@t3476](https://github.com/t3476)) +- Inna Wiesel ([@inna-w](https://github.com/inna-w)) +- Ivan Cronyn ([@cronan](https://github.com/cronan)) +- Jan Krivanek ([@jakrivan](https://github.com/jakrivan)) - Jeff Reback ([@jreback](https://github.com/jreback)) - Joe Frayne ([@jfrayne](https://github.com/jfrayne)) - John Burnett ([@johnburnett](https://github.com/johnburnett)) +- John Wilkes ([@jbw3](https://github.com/jbw3)) - 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)) +- Simon Mourier ([@smourier](https://github.com/smourier)) +- Victor Milovanov ([@lostmsu](https://github.com/lostmsu)) +- Viktoria Kovescses ([@vkovec](https://github.com/vkovec)) +- Ville M. Vainio ([@vivainio](https://github.com/vivainio)) - Virgil Dupras ([@hsoft](https://github.com/hsoft)) - Wenguang Yang ([@yagweb](https://github.com/yagweb)) +- William Sardar ([@williamsardar])(https://github.com/williamsardar) - Xavier Dupré ([@sdpython](https://github.com/sdpython)) - Zane Purvis ([@zanedp](https://github.com/zanedp)) - ([@bltribble](https://github.com/bltribble)) +- ([@civilx64](https://github.com/civilx64)) +- ([@GSPP](https://github.com/GSPP)) - ([@omnicognate](https://github.com/omnicognate)) +- ([@OneBlue](https://github.com/OneBlue)) - ([@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..b5531bf47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,74 +5,136 @@ This project adheres to [Semantic Versioning][]. This document follows the conventions laid out in [Keep a CHANGELOG][]. +## [unreleased][] + +### Added + +- Added automatic NuGet package generation in appveyor and local builds + +### Changed + +### Fixed + +## [2.4.0][] + +### 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 C# `PythonEngine.AddShutdownHandler` to help client code clean up on shutdown. +- Added `clr.GetClrType` ([#432][i432])([#433][p433]) +- Allowed passing `None` for nullable args ([#460][p460]) +- Added keyword arguments based on C# syntax for calling CPython methods ([#461][p461]) +- Catches exceptions thrown in C# iterators (yield returns) and rethrows them in python ([#475][i475])([#693][p693]) +- Implemented GetDynamicMemberNames() for PyObject to allow dynamic object members to be visible in the debugger ([#443][i443])([#690][p690]) +- Incorporated reference-style links to issues and pull requests in the CHANGELOG ([#608][i608]) +- Added PyObject finalizer support, Python objects referred by C# can be auto collect now ([#692][p692]). +- Added detailed comments about aproaches and dangers to handle multi-app-domains ([#625][p625]) +- Python 3.7 support, builds and testing added. Defaults changed from Python 3.6 to 3.7 ([#698][p698]) + +### Changed + +- PythonException included C# call stack +- Reattach python exception traceback information (#545) +- PythonEngine.Intialize will now call `Py_InitializeEx` with a default value of 0, so signals will not be configured by default on embedding. This is different from the previous behaviour, where `Py_Initialize` was called instead, which sets initSigs to 1. ([#449][i449]) +- Refactored MethodBinder.Bind in preparation to make it extensible (#829) +- Look for installed Windows 10 sdk's during installation instead of relying on specific versions. + +### Fixed + +- Fixed secondary PythonEngine.Initialize call, all sensitive static variables now reseted. + This is a hidden bug. Once python cleaning up enough memory, objects from previous engine run becomes corrupted. ([#534][p534]) +- Fixed Visual Studio 2017 compat ([#434][i434]) for setup.py +- Fixed crashes when integrating pythonnet in Unity3d ([#714][i714]), + related to unloading the Application Domain +- Fixed interop methods with Py_ssize_t. NetCoreApp 2.0 is more sensitive than net40 and requires this fix. ([#531][p531]) +- Fixed crash on exit of the Python interpreter if a python class + derived from a .NET class has a `__namespace__` or `__assembly__` + attribute ([#481][i481]) +- Fixed conversion of 'float' and 'double' values ([#486][i486]) +- Fixed 'clrmethod' for python 2 ([#492][i492]) +- Fixed double calling of constructor when deriving from .NET class ([#495][i495]) +- Fixed `clr.GetClrType` when iterating over `System` members ([#607][p607]) +- Fixed `LockRecursionException` when loading assemblies ([#627][i627]) +- Fixed errors breaking .NET Remoting on method invoke ([#276][i276]) +- Fixed PyObject.GetHashCode ([#676][i676]) +- Fix memory leaks due to spurious handle incrementation ([#691][i691]) +- Fix spurious assembly loading exceptions from private types ([#703][i703]) +- Fix inheritance of non-abstract base methods ([#755][i755]) + + ## [2.3.0][] - 2017-03-11 ### Added -- Added Code Coverage (#345) -- Added `PySys_SetArgvEx` (#347) -- Added XML Documentation (#349) -- Added `Embedded_Tests` on AppVeyor (#224)(#353) -- Added `Embedded_Tests` on Travis (#224)(#391) -- Added PY3 settings to solution configuration-manager (#346) -- Added `Slack` (#384)(#383)(#386) +- Added Code Coverage ([#345][p345]) +- Added `PySys_SetArgvEx` ([#347][p347]) +- Added XML Documentation ([#349][p349]) +- Added `Embedded_Tests` on AppVeyor ([#224][i224])([#353][p353]) +- Added `Embedded_Tests` on Travis ([#224][i224])([#391][p391]) +- Added PY3 settings to solution configuration-manager ([#346][p346]) +- Added `Slack` ([#384][p384])([#383][i383])([#386][p386]) - Added function of passing an arbitrary .NET object as the value - of an attribute of `PyObject` (#370)(#373) -- Added `Coverity scan` (#390) -- Added `bumpversion` for version control (#319)(#398) -- Added `tox` for local testing (#345) + of an attribute of `PyObject` ([#370][i370])([#373][p373]) +- Added `Coverity scan` ([#390][i390]) +- Added `bumpversion` for version control ([#319][i319])([#398][p398]) +- Added `tox` for local testing ([#345][p345]) - Added `requirements.txt` -- Added to `PythonEngine` methods `Eval` and `Exec` (#389) -- Added implementations of `ICustomMarshal` (#407) -- Added docker images (#322) -- Added hooks in `pyinstaller` and `cx_freeze` for `pythonnet` (#66) +- Added to `PythonEngine` methods `Eval` and `Exec` ([#389][p389]) +- Added implementations of `ICustomMarshal` ([#407][p407]) +- Added docker images ([#322][i322]) +- Added hooks in `pyinstaller` and `cx_freeze` for `pythonnet` ([#66][i66]) ### Changed -- Refactored python `unittests` (#329) -- Refactored python `setup.py` (#337) -- Refactored remaining of Build Directives on `runtime.cs` (#339) -- Refactored `Embedded_Tests` to make easier to write tests (#369) -- Changed `unittests` to `pytest` (#368) -- Upgraded NUnit framework from `2.6.3` to `3.5.0` (#341) -- Downgraded NUnit framework from `3.5.0` to `2.6.4` (#353) -- Upgraded NUnit framework from `2.6.4` to `3.6.0` (#371) -- Unfroze Mono version on Travis (#345) -- Changed `conda.recipe` build to only pull-requests (#345) -- Combine `Py_DEBUG` and `PYTHON_WITH_PYDEBUG` flags (#362) +- Refactored python `unittests` ([#329][p329]) +- Refactored python `setup.py` ([#337][p337]) +- Refactored remaining of Build Directives on `runtime.cs` ([#339][p339]) +- Refactored `Embedded_Tests` to make easier to write tests ([#369][p369]) +- Changed `unittests` to `pytest` ([#368][p368]) +- Upgraded NUnit framework from `2.6.3` to `3.5.0` ([#341][p341]) +- Downgraded NUnit framework from `3.5.0` to `2.6.4` ([#353][p353]) +- Upgraded NUnit framework from `2.6.4` to `3.6.0` ([#371][p371]) +- Unfroze Mono version on Travis ([#345][p345]) +- Changed `conda.recipe` build to only pull-requests ([#345][p345]) +- Combine `Py_DEBUG` and `PYTHON_WITH_PYDEBUG` flags ([#362][i362]) ### Deprecated -- Deprecated `RunString` (#401) +- Deprecated `RunString` ([#401][i401]) ### Fixed -- Fixed crash during Initialization (#262)(#343) -- Fixed crash during Shutdown (#365) +- Fixed crash during Initialization ([#262][i262])([#343][p343]) +- Fixed crash during Shutdown ([#365][p365]) - Fixed multiple build warnings -- Fixed method signature match for Object Type (#203)(#377) -- Fixed outdated version number in AssemblyInfo (#398) -- Fixed wrong version number in `conda.recipe` (#398) +- Fixed method signature match for Object Type ([#203][i203])([#377][p377]) +- Fixed outdated version number in AssemblyInfo ([#398][p398]) +- Fixed wrong version number in `conda.recipe` ([#398][p398]) - Fixed fixture location for Python tests and `Embedded_Tests` -- Fixed `PythonException` crash during Shutdown (#400) -- Fixed `AppDomain` unload during GC (#397)(#400) -- Fixed `Py_Main` & `PySys_SetArgvEx` `no mem error` on `UCS4/PY3` (#399) -- Fixed `Python.Runtime.dll.config` on macOS (#120) -- Fixed crash on `PythonEngine.Version` (#413) -- Fixed `PythonEngine.PythonPath` issues (#179)(#414)(#415) +- Fixed `PythonException` crash during Shutdown ([#400][p400]) +- Fixed `AppDomain` unload during GC ([#397][i397])([#400][p400]) +- Fixed `Py_Main` & `PySys_SetArgvEx` `no mem error` on `UCS4/PY3` ([#399][p399]) +- Fixed `Python.Runtime.dll.config` on macOS ([#120][i120]) +- Fixed crash on `PythonEngine.Version` ([#413][i413]) +- Fixed `PythonEngine.PythonPath` issues ([#179][i179])([#414][i414])([#415][p415]) +- Fixed missing information on 'No method matches given arguments' by adding the method name ### Removed -- Removed `six` dependency for `unittests` (#329) -- Removed `Mono.Unix` dependency for `UCS4` (#360) +- Removed `six` dependency for `unittests` ([#329][p329]) +- Removed `Mono.Unix` dependency for `UCS4` ([#360][p360]) - Removed need for `Python.Runtime.dll.config` -- Removed PY32 build option `PYTHON_WITH_WIDE_UNICODE` (#417) +- Removed PY32 build option `PYTHON_WITH_WIDE_UNICODE` ([#417][i417]) ## [2.2.2][] - 2017-01-29 ### Fixed -- Missing files from packaging (#336) +- Missing files from packaging ([#336][i336]) ## [2.2.1][] - 2017-01-26 @@ -80,65 +142,65 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. ### Added -- Python 3.6 support (#310) -- Added `__version__` to module (#312) -- Added `conda` recipe (#281) -- Nuget update on build (#268) -- Added `__cause__` attribute on exception (#287) +- Python 3.6 support ([#310][p310]) +- Added `__version__` to module ([#312][p312]) +- Added `conda` recipe ([#281][p281]) +- Nuget update on build ([#268][p268]) +- Added `__cause__` attribute on exception ([#287][p287]) ### Changed -- License to MIT (#314) -- Project clean-up (#320) +- License to MIT ([#314][p314]) +- Project clean-up ([#320][p320]) - Refactor `#if` directives -- Rename Decref/Incref to XDecref/XIncre (#275) -- Remove printing if Decref is called with NULL (#275) +- Rename Decref/Incref to XDecref/XIncre ([#275][p275]) +- Remove printing if Decref is called with NULL ([#275][p275]) ### Removed -- Python 2.6 support (#270) -- Python 3.2 support (#270) +- Python 2.6 support ([#270][i270]) +- Python 3.2 support ([#270][i270]) ### Fixed -- Fixed `isinstance` refcount_leak (#273) -- Comparison Operators (#294) -- Improved Linux support (#300) -- Exception pickling (#286) +- Fixed `isinstance` refcount_leak ([#273][p273]) +- Comparison Operators ([#294][p294]) +- Improved Linux support ([#300][p300]) +- Exception pickling ([#286][p286]) ## [2.2.0-dev1][] - 2016-09-19 ### Changed -- Switch to C# 6.0 (#219) -- `setup.py` improvements for locating build tools (#208) -- unmanaged exports updated (#206) -- Mono update pinned to 4.2.4.4 (#233) +- Switch to C# 6.0 ([#219][p219]) +- `setup.py` improvements for locating build tools ([#208][p208]) +- unmanaged exports updated ([#206][p206]) +- Mono update pinned to 4.2.4.4 ([#233][p233]) ### Fixed -- Fixed relative imports (#219) -- Fixed recursive types (#250) -- Demo fix - stream reading (#225) +- Fixed relative imports ([#219][p219]) +- Fixed recursive types ([#250][p250]) +- Demo fix - stream reading ([#225][p225]) ## [2.1.0][] - 2016-04-12 ### Added -- Added Python 3.2 support. (#78) -- Added Python 3.3 support. (#78) -- Added Python 3.4 support. (#78) -- Added Python 3.5 support. (#163) -- Managed types can be sub-classed in Python (#78) -- Uses dynamic objects for cleaner code when embedding Python (#78) +- Added Python 3.2 support. ([#78][p78]) +- Added Python 3.3 support. ([#78][p78]) +- Added Python 3.4 support. ([#78][p78]) +- Added Python 3.5 support. ([#163][p163]) +- Managed types can be sub-classed in Python ([#78][p78]) +- Uses dynamic objects for cleaner code when embedding Python ([#78][p78]) ### Changed -- Better Linux support (with or without --enable-shared option) (#78) +- Better Linux support (with or without --enable-shared option) ([#78][p78]) ### Removed -- Implicit Type Casting (#131) +- Implicit Type Casting ([#131][i131]) ## [2.0.0][] - 2015-06-26 @@ -558,3 +620,101 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. [2.0.0]: ../../compare/1.0...v2.0.0 [1.0.0]: https://github.com/pythonnet/pythonnet/releases/tag/1.0 + +[i714]: https://github.com/pythonnet/pythonnet/issues/714 +[i608]: https://github.com/pythonnet/pythonnet/issues/608 +[i443]: https://github.com/pythonnet/pythonnet/issues/443 +[p690]: https://github.com/pythonnet/pythonnet/pull/690 +[i475]: https://github.com/pythonnet/pythonnet/issues/475 +[p693]: https://github.com/pythonnet/pythonnet/pull/693 +[i432]: https://github.com/pythonnet/pythonnet/issues/432 +[p433]: https://github.com/pythonnet/pythonnet/pull/433 +[p460]: https://github.com/pythonnet/pythonnet/pull/460 +[p461]: https://github.com/pythonnet/pythonnet/pull/461 +[p433]: https://github.com/pythonnet/pythonnet/pull/433 +[i434]: https://github.com/pythonnet/pythonnet/issues/434 +[i481]: https://github.com/pythonnet/pythonnet/issues/481 +[i486]: https://github.com/pythonnet/pythonnet/issues/486 +[i492]: https://github.com/pythonnet/pythonnet/issues/492 +[i495]: https://github.com/pythonnet/pythonnet/issues/495 +[p607]: https://github.com/pythonnet/pythonnet/pull/607 +[i627]: https://github.com/pythonnet/pythonnet/issues/627 +[i276]: https://github.com/pythonnet/pythonnet/issues/276 +[i676]: https://github.com/pythonnet/pythonnet/issues/676 +[p345]: https://github.com/pythonnet/pythonnet/pull/345 +[p347]: https://github.com/pythonnet/pythonnet/pull/347 +[p349]: https://github.com/pythonnet/pythonnet/pull/349 +[i224]: https://github.com/pythonnet/pythonnet/issues/224 +[p353]: https://github.com/pythonnet/pythonnet/pull/353 +[p391]: https://github.com/pythonnet/pythonnet/pull/391 +[p346]: https://github.com/pythonnet/pythonnet/pull/346 +[p384]: https://github.com/pythonnet/pythonnet/pull/384 +[i383]: https://github.com/pythonnet/pythonnet/issues/383 +[p386]: https://github.com/pythonnet/pythonnet/pull/386 +[i370]: https://github.com/pythonnet/pythonnet/issues/370 +[p373]: https://github.com/pythonnet/pythonnet/pull/373 +[i390]: https://github.com/pythonnet/pythonnet/issues/390 +[i319]: https://github.com/pythonnet/pythonnet/issues/319 +[p398]: https://github.com/pythonnet/pythonnet/pull/398 +[p345]: https://github.com/pythonnet/pythonnet/pull/345 +[p389]: https://github.com/pythonnet/pythonnet/pull/389 +[p407]: https://github.com/pythonnet/pythonnet/pull/407 +[i322]: https://github.com/pythonnet/pythonnet/issues/322 +[i66]: https://github.com/pythonnet/pythonnet/issues/66 +[p329]: https://github.com/pythonnet/pythonnet/pull/329 +[p337]: https://github.com/pythonnet/pythonnet/pull/337 +[p339]: https://github.com/pythonnet/pythonnet/pull/339 +[p369]: https://github.com/pythonnet/pythonnet/pull/369 +[p368]: https://github.com/pythonnet/pythonnet/pull/368 +[p341]: https://github.com/pythonnet/pythonnet/pull/341 +[p353]: https://github.com/pythonnet/pythonnet/pull/353 +[p371]: https://github.com/pythonnet/pythonnet/pull/371 +[p345]: https://github.com/pythonnet/pythonnet/pull/345 +[i362]: https://github.com/pythonnet/pythonnet/issues/362 +[i401]: https://github.com/pythonnet/pythonnet/issues/401 +[i262]: https://github.com/pythonnet/pythonnet/issues/262 +[p343]: https://github.com/pythonnet/pythonnet/pull/343 +[p365]: https://github.com/pythonnet/pythonnet/pull/365 +[i203]: https://github.com/pythonnet/pythonnet/issues/203 +[p377]: https://github.com/pythonnet/pythonnet/pull/377 +[p398]: https://github.com/pythonnet/pythonnet/pull/398 +[p400]: https://github.com/pythonnet/pythonnet/pull/400 +[i397]: https://github.com/pythonnet/pythonnet/issues/397 +[p399]: https://github.com/pythonnet/pythonnet/pull/399 +[i120]: https://github.com/pythonnet/pythonnet/issues/120 +[i413]: https://github.com/pythonnet/pythonnet/issues/413 +[i179]: https://github.com/pythonnet/pythonnet/issues/179 +[i414]: https://github.com/pythonnet/pythonnet/issues/414 +[p415]: https://github.com/pythonnet/pythonnet/pull/415 +[p329]: https://github.com/pythonnet/pythonnet/pull/329 +[p360]: https://github.com/pythonnet/pythonnet/pull/360 +[i417]: https://github.com/pythonnet/pythonnet/issues/417 +[i336]: https://github.com/pythonnet/pythonnet/issues/336 +[p310]: https://github.com/pythonnet/pythonnet/pull/310 +[p312]: https://github.com/pythonnet/pythonnet/pull/312 +[p281]: https://github.com/pythonnet/pythonnet/pull/281 +[p268]: https://github.com/pythonnet/pythonnet/pull/268 +[p287]: https://github.com/pythonnet/pythonnet/pull/287 +[p314]: https://github.com/pythonnet/pythonnet/pull/314 +[p320]: https://github.com/pythonnet/pythonnet/pull/320 +[p275]: https://github.com/pythonnet/pythonnet/pull/275 +[i270]: https://github.com/pythonnet/pythonnet/issues/270 +[p273]: https://github.com/pythonnet/pythonnet/pull/273 +[p294]: https://github.com/pythonnet/pythonnet/pull/294 +[p300]: https://github.com/pythonnet/pythonnet/pull/300 +[p286]: https://github.com/pythonnet/pythonnet/pull/286 +[p219]: https://github.com/pythonnet/pythonnet/pull/219 +[p208]: https://github.com/pythonnet/pythonnet/pull/208 +[p206]: https://github.com/pythonnet/pythonnet/pull/206 +[p233]: https://github.com/pythonnet/pythonnet/pull/233 +[p219]: https://github.com/pythonnet/pythonnet/pull/219 +[p250]: https://github.com/pythonnet/pythonnet/pull/250 +[p225]: https://github.com/pythonnet/pythonnet/pull/225 +[p78]: https://github.com/pythonnet/pythonnet/pull/78 +[p163]: https://github.com/pythonnet/pythonnet/pull/163 +[p625]: https://github.com/pythonnet/pythonnet/pull/625 +[i131]: https://github.com/pythonnet/pythonnet/issues/131 +[p531]: https://github.com/pythonnet/pythonnet/pull/531 +[i755]: https://github.com/pythonnet/pythonnet/pull/755 +[p534]: https://github.com/pythonnet/pythonnet/pull/534 +[i449]: https://github.com/pythonnet/pythonnet/issues/449 diff --git a/LICENSE b/LICENSE index e344a0795..59abd9c81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2006-2017 the contributors of the "Python for .NET" project +Copyright (c) 2006-2019 the contributors of the "Python for .NET" project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 000000000..5210cd6c9 --- /dev/null +++ b/NuGet.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 2794298a6..000000000 --- a/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# pythonnet - Python for .NET - -[![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) -[![coverity shield][]](https://scan.coverity.com/projects/pythonnet) - -[![license shield][]](./LICENSE) -[![pypi package version][]](https://pypi.python.org/pypi/pythonnet) -[![python supported shield][]](https://pypi.python.org/pypi/pythonnet) -[![stackexchange shield][]](http://stackoverflow.com/questions/tagged/python.net) -[![slack][]](https://pythonnet.slack.com) - -Python for .NET is a package that gives Python programmers nearly -seamless integration with the .NET Common Language Runtime (CLR) and -provides a powerful application scripting tool for .NET developers. -It allows Python code to interact with the CLR, and may also be used to -embed Python into a .NET application. - -## Calling .NET code from Python - -Python for .NET allows CLR namespaces to be treated essentially -as Python packages. - -```python -import clr -from System import String -from System.Collections import * -``` - -To load an assembly, use the `AddReference` function in the `clr` module: - -```python -import clr -clr.AddReference("System.Windows.Forms") -from System.Windows.Forms import Form -``` - -## Embedding Python in .NET - -- All calls to python should be inside - a `using (Py.GIL()) {/* Your code here */}` block. -- Import python modules using `dynamic mod = Py.Import("mod")`, - then you can call functions as normal, eg `mod.func(args)`. -- Use `mod.func(args, Py.kw("keywordargname", keywordargvalue))` - to apply keyword arguments. -- All python objects should be declared as `dynamic` type. -- Mathematical operations involving python and literal/managed types must - have the python object first, eg. `np.pi * 2` works, `2 * np.pi` doesn't. - -### Example - -```csharp -static void Main(string[] args) -{ - using (Py.GIL()) - { - dynamic np = Py.Import("numpy"); - Console.WriteLine(np.cos(np.pi * 2)); - - dynamic sin = np.sin; - Console.WriteLine(sin(5)); - - 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)); - Console.WriteLine(b.dtype); - - Console.WriteLine(a * b); - */ - Console.ReadKey(); - } -} -``` - -Output: - -```c -1.0 --0.958924274663 --0.6752620892 -float64 -int32 -[ 6. 10. 12.] -``` - -[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 - -[coverity shield]: https://img.shields.io/coverity/scan/7830.svg - -[license shield]: https://img.shields.io/badge/license-MIT-blue.svg?maxAge=3600 - -[pypi package version]: https://img.shields.io/pypi/v/pythonnet.svg - -[python supported shield]: https://img.shields.io/pypi/pyversions/pythonnet.svg - -[slack]: https://img.shields.io/badge/chat-slack-color.svg?style=social - -[stackexchange shield]: https://img.shields.io/badge/StackOverflow-python.net-blue.svg - -[travis shield]: https://img.shields.io/travis/pythonnet/pythonnet/master.svg?label=Travis diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..5366649ae --- /dev/null +++ b/README.rst @@ -0,0 +1,115 @@ +pythonnet - Python for .NET +=========================== + +|Join the chat at https://gitter.im/pythonnet/pythonnet| + +|appveyor shield| |travis shield| |codecov shield| + +|license shield| |pypi package version| |conda-forge version| |python supported shield| +|stackexchange shield| + +Python for .NET is a package that gives Python programmers nearly +seamless integration with the .NET Common Language Runtime (CLR) and +provides a powerful application scripting tool for .NET developers. It +allows Python code to interact with the CLR, and may also be used to +embed Python into a .NET application. + +Calling .NET code from Python +----------------------------- + +Python for .NET allows CLR namespaces to be treated essentially as +Python packages. + +.. code-block:: + + import clr + from System import String + from System.Collections import * + +To load an assembly, use the ``AddReference`` function in the ``clr`` +module: + +.. code-block:: + + import clr + clr.AddReference("System.Windows.Forms") + from System.Windows.Forms import Form + +Embedding Python in .NET +------------------------ + +- All calls to python should be inside a + ``using (Py.GIL()) {/* Your code here */}`` block. +- Import python modules using ``dynamic mod = Py.Import("mod")``, then + you can call functions as normal, eg ``mod.func(args)``. +- Use ``mod.func(args, Py.kw("keywordargname", keywordargvalue))`` 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 have the python object first, eg. ``np.pi * 2`` works, + ``2 * np.pi`` doesn't. + +Example +~~~~~~~ + +.. code-block:: csharp + + static void Main(string[] args) + { + using (Py.GIL()) + { + dynamic np = Py.Import("numpy"); + Console.WriteLine(np.cos(np.pi * 2)); + + dynamic sin = np.sin; + Console.WriteLine(sin(5)); + + double c = np.cos(5) + sin(5); + Console.WriteLine(c); + + dynamic a = np.array(new List { 1, 2, 3 }); + Console.WriteLine(a.dtype); + + dynamic b = np.array(new List { 6, 5, 4 }, dtype: np.int32); + Console.WriteLine(b.dtype); + + Console.WriteLine(a * b); + Console.ReadKey(); + } + } + +Output: + +.. code:: + + 1.0 + -0.958924274663 + -0.6752620892 + float64 + 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 + +.. |Join the chat at https://gitter.im/pythonnet/pythonnet| image:: https://badges.gitter.im/pythonnet/pythonnet.svg + :target: https://gitter.im/pythonnet/pythonnet?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge +.. |appveyor shield| image:: https://img.shields.io/appveyor/ci/pythonnet/pythonnet/master.svg?label=AppVeyor + :target: https://ci.appveyor.com/project/pythonnet/pythonnet/branch/master +.. |travis shield| image:: https://img.shields.io/travis/pythonnet/pythonnet/master.svg?label=Travis + :target: https://travis-ci.org/pythonnet/pythonnet +.. |codecov shield| image:: https://img.shields.io/codecov/c/github/pythonnet/pythonnet/master.svg?label=Codecov + :target: https://codecov.io/github/pythonnet/pythonnet +.. |license shield| image:: https://img.shields.io/badge/license-MIT-blue.svg?maxAge=3600 + :target: ./LICENSE +.. |pypi package version| image:: https://img.shields.io/pypi/v/pythonnet.svg + :target: https://pypi.python.org/pypi/pythonnet +.. |python supported shield| image:: https://img.shields.io/pypi/pyversions/pythonnet.svg + :target: https://pypi.python.org/pypi/pythonnet +.. |stackexchange shield| image:: https://img.shields.io/badge/StackOverflow-python.net-blue.svg + :target: http://stackoverflow.com/questions/tagged/python.net +.. |conda-forge version| image:: https://img.shields.io/conda/vn/conda-forge/pythonnet.svg + :target: https://anaconda.org/conda-forge/pythonnet diff --git a/appveyor.yml b/appveyor.yml index c108801e7..445f9bb5a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,9 @@ version: '{branch}-{build}' build: off +image: + - Visual Studio 2017 + platform: - x86 - x64 @@ -12,11 +15,24 @@ environment: CODECOV_ENV: PYTHON_VERSION, PLATFORM matrix: + - PYTHON_VERSION: 2.7 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.5 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.6 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.7 + BUILD_OPTS: --xplat - PYTHON_VERSION: 2.7 - - PYTHON_VERSION: 3.3 - - PYTHON_VERSION: 3.4 - PYTHON_VERSION: 3.5 - PYTHON_VERSION: 3.6 + - PYTHON_VERSION: 3.7 + +matrix: + allow_failures: + - PYTHON_VERSION: 3.4 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.4 init: # Update Environment Variables based on matrix/platform @@ -28,6 +44,7 @@ init: - set PATH=%PYTHON%;%PYTHON%\Scripts;%PATH% install: + - python -m pip install -U pip - pip install --upgrade -r requirements.txt --quiet # Install OpenCover. Can't put on `packages.config`, not Mono compatible @@ -37,7 +54,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,14 +64,15 @@ 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\* + - path: '.\src\runtime\bin\*.nupkg' notifications: - provider: Slack 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..545a01268 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -1,10 +1,10 @@ package: name: pythonnet - version: "2.3.0" + version: {{ GIT_DESCRIBE_VERSION }} build: skip: True # [not win] - number: {{ environ.get('GIT_DESCRIBE_NUMBER', 0) }} + number: {{ GIT_DESCRIBE_NUMBER }} {% if environ.get('GIT_DESCRIBE_NUMBER', '0') == '0' %}string: py{{ environ.get('PY_VER').replace('.', '') }}_0 {% else %}string: py{{ environ.get('PY_VER').replace('.', '') }}_{{ environ.get('GIT_BUILD_STR', 'GIT_STUB') }}{% endif %} 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.sln b/pythonnet.sln index c5afd66c3..7f8185d25 100644 --- a/pythonnet.sln +++ b/pythonnet.sln @@ -1,202 +1,367 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python.Runtime", "src\runtime\Python.Runtime.csproj", "{097B4AC0-74E9-4C58-BCF8-C69746EC8271}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python.Test", "src\testing\Python.Test.csproj", "{6F401A34-273B-450F-9A4C-13550BE0767B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python.EmbeddingTest", "src\embed_tests\Python.EmbeddingTest.csproj", "{4165C59D-2822-499F-A6DB-EACA4C331EB5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "src\console\Console.csproj", "{E29DCF0A-5114-4A98-B1DD-71264B6EA349}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "clrmodule", "src\clrmodule\clrmodule.csproj", "{86E834DE-1139-4511-96CC-69636A56E7AC}" -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 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x64.Build.0 = DebugMono|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x86.Build.0 = DebugMono|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x64.Build.0 = DebugWin|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x86.Build.0 = DebugWin|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMono|x64.Build.0 = DebugMono|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMono|x86.Build.0 = DebugMono|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWin|x64.Build.0 = DebugWin|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWin|x86.Build.0 = DebugWin|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x64.Build.0 = DebugMono|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x86.Build.0 = DebugMono|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x64.Build.0 = DebugWin|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x86.Build.0 = DebugWin|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMono|x64.Build.0 = DebugMono|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMono|x86.Build.0 = DebugMono|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWin|x64.Build.0 = DebugWin|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWin|x86.Build.0 = DebugWin|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWin|x64.Build.0 = DebugWin|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWin|x86.Build.0 = DebugWin|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = src\console\Console.csproj - Policies = $0 - $0.VersionControlPolicy = $1 - $1.inheritsSet = Mono - $0.ChangeLogPolicy = $2 - $2.UpdateMode = None - $2.MessageStyle = $3 - $3.LineAlign = 0 - $2.inheritsSet = Mono - EndGlobalSection -EndGlobal +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28917.181 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Runtime", "src\runtime\Python.Runtime.csproj", "{097B4AC0-74E9-4C58-BCF8-C69746EC8271}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.EmbeddingTest", "src\embed_tests\Python.EmbeddingTest.csproj", "{4165C59D-2822-499F-A6DB-EACA4C331EB5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console", "src\console\Console.csproj", "{6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Test", "src\testing\Python.Test.csproj", "{BA04A0A4-706F-4469-BE07-2E9F3E6141DD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "clrmodule", "src\clrmodule\clrmodule.csproj", "{39FC5D2C-EFD3-487D-9B9C-6F5464F27764}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + DebugMono|Any CPU = DebugMono|Any CPU + DebugMono|x64 = DebugMono|x64 + DebugMono|x86 = DebugMono|x86 + DebugMonoPY3|Any CPU = DebugMonoPY3|Any CPU + DebugMonoPY3|x64 = DebugMonoPY3|x64 + DebugMonoPY3|x86 = DebugMonoPY3|x86 + DebugWin|Any CPU = DebugWin|Any CPU + DebugWin|x64 = DebugWin|x64 + DebugWin|x86 = DebugWin|x86 + DebugWinPY3|Any CPU = DebugWinPY3|Any CPU + DebugWinPY3|x64 = DebugWinPY3|x64 + DebugWinPY3|x86 = DebugWinPY3|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + ReleaseMono|Any CPU = ReleaseMono|Any CPU + ReleaseMono|x64 = ReleaseMono|x64 + ReleaseMono|x86 = ReleaseMono|x86 + ReleaseMonoPY3|Any CPU = ReleaseMonoPY3|Any CPU + ReleaseMonoPY3|x64 = ReleaseMonoPY3|x64 + ReleaseMonoPY3|x86 = ReleaseMonoPY3|x86 + ReleaseWin|Any CPU = ReleaseWin|Any CPU + ReleaseWin|x64 = ReleaseWin|x64 + ReleaseWin|x86 = ReleaseWin|x86 + ReleaseWinPY3|Any CPU = ReleaseWinPY3|Any CPU + ReleaseWinPY3|x64 = ReleaseWinPY3|x64 + ReleaseWinPY3|x86 = ReleaseWinPY3|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Debug|Any CPU.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Debug|x64.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Debug|x64.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Debug|x86.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Debug|x86.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|Any CPU.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|Any CPU.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x64.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x64.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x86.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x86.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|Any CPU.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|Any CPU.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x64.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x64.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x86.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x86.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|Any CPU.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|Any CPU.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x64.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x64.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x86.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x86.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|Any CPU.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|Any CPU.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x64.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x64.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x86.ActiveCfg = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x86.Build.0 = Debug|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Release|Any CPU.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Release|Any CPU.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Release|x64.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Release|x64.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Release|x86.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.Release|x86.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|Any CPU.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|Any CPU.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x64.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x64.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x86.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x86.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|Any CPU.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|Any CPU.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x64.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x64.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x86.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x86.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|Any CPU.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|Any CPU.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x64.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x64.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x86.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x86.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|Any CPU.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|Any CPU.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x64.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x64.Build.0 = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x86.ActiveCfg = Release|Any CPU + {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x86.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Debug|x64.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Debug|x64.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Debug|x86.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Debug|x86.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|Any CPU.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|Any CPU.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x64.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x64.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x86.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x86.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|Any CPU.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|Any CPU.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x64.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x64.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x86.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x86.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|Any CPU.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|Any CPU.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x64.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x64.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x86.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x86.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|Any CPU.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|Any CPU.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x64.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x64.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x86.ActiveCfg = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x86.Build.0 = Debug|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Release|Any CPU.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Release|x64.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Release|x64.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Release|x86.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.Release|x86.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|Any CPU.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|Any CPU.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x64.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x64.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x86.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x86.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|Any CPU.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|Any CPU.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x64.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x64.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x86.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x86.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|Any CPU.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|Any CPU.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x64.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x64.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x86.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x86.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|Any CPU.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|Any CPU.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x64.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x64.Build.0 = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x86.ActiveCfg = Release|Any CPU + {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x86.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Debug|x64.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Debug|x64.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Debug|x86.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Debug|x86.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMono|Any CPU.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMono|Any CPU.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMono|x64.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMono|x64.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMono|x86.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMono|x86.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMonoPY3|Any CPU.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMonoPY3|Any CPU.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMonoPY3|x64.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMonoPY3|x64.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMonoPY3|x86.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugMonoPY3|x86.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWin|Any CPU.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWin|Any CPU.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWin|x64.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWin|x64.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWin|x86.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWin|x86.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWinPY3|Any CPU.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWinPY3|Any CPU.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWinPY3|x64.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWinPY3|x64.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWinPY3|x86.ActiveCfg = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.DebugWinPY3|x86.Build.0 = Debug|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Release|Any CPU.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Release|x64.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Release|x64.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Release|x86.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.Release|x86.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMono|Any CPU.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMono|Any CPU.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMono|x64.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMono|x64.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMono|x86.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMono|x86.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMonoPY3|Any CPU.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMonoPY3|Any CPU.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMonoPY3|x64.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMonoPY3|x64.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMonoPY3|x86.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseMonoPY3|x86.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWin|Any CPU.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWin|Any CPU.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWin|x64.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWin|x64.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWin|x86.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWin|x86.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWinPY3|Any CPU.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWinPY3|Any CPU.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWinPY3|x64.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWinPY3|x64.Build.0 = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWinPY3|x86.ActiveCfg = Release|Any CPU + {6C429F44-22EB-4FDB-9DD9-EA55B0497E7D}.ReleaseWinPY3|x86.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Debug|x64.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Debug|x64.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Debug|x86.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Debug|x86.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMono|Any CPU.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMono|Any CPU.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMono|x64.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMono|x64.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMono|x86.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMono|x86.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMonoPY3|Any CPU.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMonoPY3|Any CPU.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMonoPY3|x64.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMonoPY3|x64.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMonoPY3|x86.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugMonoPY3|x86.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWin|Any CPU.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWin|Any CPU.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWin|x64.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWin|x64.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWin|x86.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWin|x86.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWinPY3|Any CPU.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWinPY3|Any CPU.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWinPY3|x64.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWinPY3|x64.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWinPY3|x86.ActiveCfg = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.DebugWinPY3|x86.Build.0 = Debug|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Release|Any CPU.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Release|x64.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Release|x64.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Release|x86.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.Release|x86.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMono|Any CPU.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMono|Any CPU.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMono|x64.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMono|x64.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMono|x86.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMono|x86.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMonoPY3|Any CPU.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMonoPY3|Any CPU.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMonoPY3|x64.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMonoPY3|x64.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMonoPY3|x86.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseMonoPY3|x86.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWin|Any CPU.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWin|Any CPU.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWin|x64.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWin|x64.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWin|x86.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWin|x86.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWinPY3|Any CPU.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWinPY3|Any CPU.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWinPY3|x64.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWinPY3|x64.Build.0 = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWinPY3|x86.ActiveCfg = Release|Any CPU + {BA04A0A4-706F-4469-BE07-2E9F3E6141DD}.ReleaseWinPY3|x86.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Debug|x64.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Debug|x64.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Debug|x86.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Debug|x86.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMono|Any CPU.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMono|Any CPU.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMono|x64.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMono|x64.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMono|x86.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMono|x86.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMonoPY3|Any CPU.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMonoPY3|Any CPU.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMonoPY3|x64.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMonoPY3|x64.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMonoPY3|x86.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugMonoPY3|x86.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWin|Any CPU.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWin|Any CPU.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWin|x64.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWin|x64.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWin|x86.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWin|x86.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWinPY3|Any CPU.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWinPY3|Any CPU.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWinPY3|x64.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWinPY3|x64.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWinPY3|x86.ActiveCfg = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.DebugWinPY3|x86.Build.0 = Debug|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Release|Any CPU.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Release|x64.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Release|x64.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Release|x86.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.Release|x86.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMono|Any CPU.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMono|Any CPU.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMono|x64.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMono|x64.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMono|x86.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMono|x86.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMonoPY3|Any CPU.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMonoPY3|Any CPU.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMonoPY3|x64.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMonoPY3|x64.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMonoPY3|x86.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseMonoPY3|x86.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWin|Any CPU.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWin|Any CPU.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWin|x64.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWin|x64.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWin|x86.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWin|x86.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWinPY3|Any CPU.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWinPY3|Any CPU.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWinPY3|x64.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWinPY3|x64.Build.0 = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWinPY3|x86.ActiveCfg = Release|Any CPU + {39FC5D2C-EFD3-487D-9B9C-6F5464F27764}.ReleaseWinPY3|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6EBD48BE-33F5-44AA-9006-850A23CAE8BF} + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = src\console\Console.csproj + Policies = $0 + $0.VersionControlPolicy = $1 + $1.inheritsSet = Mono + $0.ChangeLogPolicy = $2 + $2.UpdateMode = None + $2.MessageStyle = $3 + $3.LineAlign = 0 + $2.inheritsSet = Mono + EndGlobalSection +EndGlobal diff --git a/requirements.txt b/requirements.txt index bcceedf25..29c2e4566 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ # Requirements for both Travis and AppVeyor -pytest +pytest==3.2.5 coverage +psutil + +# Coverage upload +codecov # Platform specific requirements -pip; sys_platform == 'win32' +# 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..53c7f3f67 100644 --- a/setup.py +++ b/setup.py @@ -14,16 +14,17 @@ 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 # Allow config/verbosity to be set from cli # http://stackoverflow.com/a/4792601/5208670 CONFIG = "Release" # Release or Debug -VERBOSITY = "minimal" # quiet, minimal, normal, detailed, diagnostic +VERBOSITY = "normal" # quiet, minimal, normal, detailed, diagnostic -is_64bits = sys.maxsize > 2**32 +is_64bits = sys.maxsize > 2 ** 32 DEVTOOLS = "MsDev" if sys.platform == "win32" else "Mono" ARCH = "x64" if is_64bits else "x86" PY_MAJOR = sys.version_info[0] @@ -31,54 +32,113 @@ ############################################################################### # Windows Keys Constants for MSBUILD tools -RegKey = collections.namedtuple('RegKey', 'sdk_name key value_name suffix') +RegKey = collections.namedtuple("RegKey", "sdk_name key value_name suffix") vs_python = "Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\WinSDK" vs_root = "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{0}" sdks_root = "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v{0}Win32Tools" kits_root = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots" kits_suffix = os.path.join("bin", ARCH) -WIN_SDK_KEYS = ( - RegKey(sdk_name="Windows Kit 10.0", key=kits_root, - value_name="KitsRoot10", suffix=kits_suffix), - - RegKey(sdk_name="Windows Kit 8.1", key=kits_root, - value_name="KitsRoot81", suffix=kits_suffix), - - RegKey(sdk_name="Windows Kit 8.0", key=kits_root, - value_name="KitsRoot", suffix=kits_suffix), - - RegKey(sdk_name="Windows SDK 7.1A", key=sdks_root.format("7.1A\\WinSDK-"), - value_name="InstallationFolder", suffix=""), - - RegKey(sdk_name="Windows SDK 7.1", key=sdks_root.format("7.1\\WinSDK"), - value_name="InstallationFolder", suffix=""), - - RegKey(sdk_name="Windows SDK 7.0A", key=sdks_root.format("7.0A\\WinSDK-"), - value_name="InstallationFolder", suffix=""), - - RegKey(sdk_name="Windows SDK 7.0", key=sdks_root.format("7.0\\WinSDK"), - value_name="InstallationFolder", suffix=""), - - RegKey(sdk_name="Windows SDK 6.0A", key=sdks_root.format("6.0A\\WinSDK"), - value_name="InstallationFolder", suffix=""), -) +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, + ), + RegKey( + sdk_name="Windows Kit 8.1", + key=kits_root, + value_name="KitsRoot81", + suffix=kits_suffix, + ), + RegKey( + sdk_name="Windows Kit 8.0", + key=kits_root, + value_name="KitsRoot", + suffix=kits_suffix, + ), + RegKey( + sdk_name="Windows SDK 7.1A", + key=sdks_root.format("7.1A\\WinSDK-"), + value_name="InstallationFolder", + suffix="", + ), + RegKey( + sdk_name="Windows SDK 7.1", + key=sdks_root.format("7.1\\WinSDK"), + value_name="InstallationFolder", + suffix="", + ), + RegKey( + sdk_name="Windows SDK 7.0A", + key=sdks_root.format("7.0A\\WinSDK-"), + value_name="InstallationFolder", + suffix="", + ), + RegKey( + sdk_name="Windows SDK 7.0", + key=sdks_root.format("7.0\\WinSDK"), + value_name="InstallationFolder", + suffix="", + ), + RegKey( + sdk_name="Windows SDK 6.0A", + key=sdks_root.format("6.0A\\WinSDK"), + value_name="InstallationFolder", + suffix="", + ), +] VS_KEYS = ( - RegKey(sdk_name="MSBuild 14", key=vs_root.format("14.0"), - value_name="MSBuildToolsPath", suffix=""), - - RegKey(sdk_name="MSBuild 12", key=vs_root.format("12.0"), - value_name="MSBuildToolsPath", suffix=""), - - RegKey(sdk_name="MSBuild 4", key=vs_root.format("4.0"), - value_name="MSBuildToolsPath", suffix=""), - - RegKey(sdk_name="MSBuild 3.5", key=vs_root.format("3.5"), - value_name="MSBuildToolsPath", suffix=""), - - RegKey(sdk_name="MSBuild 2.0", key=vs_root.format("2.0"), - value_name="MSBuildToolsPath", suffix=""), + 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="", + ), + RegKey( + sdk_name="MSBuild 12", + key=vs_root.format("12.0"), + value_name="MSBuildToolsPath", + suffix="", + ), + RegKey( + sdk_name="MSBuild 4", + key=vs_root.format("4.0"), + value_name="MSBuildToolsPath", + suffix="", + ), + RegKey( + sdk_name="MSBuild 3.5", + key=vs_root.format("3.5"), + value_name="MSBuildToolsPath", + suffix="", + ), + RegKey( + sdk_name="MSBuild 2.0", + key=vs_root.format("2.0"), + value_name="MSBuildToolsPath", + suffix="", + ), ) @@ -98,19 +158,19 @@ def _get_interop_filename(): required to generate the file. """ interop_filename = "interop{0}{1}{2}.cs".format( - PY_MAJOR, PY_MINOR, getattr(sys, "abiflags", "")) + PY_MAJOR, PY_MINOR, getattr(sys, "abiflags", "") + ) return os.path.join("src", "runtime", interop_filename) def _get_source_files(): """Walk project and collect the files needed for ext_module""" - for ext in (".sln", ): + for ext in (".sln",): for path in glob.glob("*" + ext): yield path for root, dirnames, filenames in os.walk("src"): - for ext in (".cs", ".csproj", ".snk", ".config", - ".py", ".c", ".h", ".ico"): + for ext in (".cs", ".csproj", ".snk", ".config", ".py", ".c", ".h", ".ico"): for filename in fnmatch.filter(filenames, "*" + ext): yield os.path.join(root, filename) @@ -122,15 +182,61 @@ def _get_source_files(): def _get_long_description(): """Helper to populate long_description for pypi releases""" - try: - import pypandoc - return pypandoc.convert('README.md', 'rst') - except ImportError: - return '.Net and Mono integration for Python' + return open("README.rst").read() + + +def _update_xlat_devtools(): + global DEVTOOLS + if DEVTOOLS == "MsDev": + DEVTOOLS = "MsDev15" + elif DEVTOOLS == "Mono": + DEVTOOLS = "dotnet" + + +def _collect_installed_windows_kits_v10(winreg): + """Adds the installed Windows 10 kits to WIN_SDK_KEYS """ + global WIN_SDK_KEYS + installed_kits = [] + + with winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, kits_root, 0, winreg.KEY_READ + ) as key: + i = 0 + while True: + try: + installed_kits.append(winreg.EnumKey(key, i)) + i += 1 + except WindowsError: + break + + def make_reg_key(version): + return RegKey( + sdk_name="Windows Kit 10.0", + key=kits_root, + value_name="KitsRoot10", + suffix=os.path.join("bin", version, ARCH), + ) + + WIN_SDK_KEYS += [make_reg_key(e) for e in installed_kits if e.startswith("10.")] + + # Make sure this function won't be called again + _collect_installed_windows_kits_v10 = lambda: None 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) @@ -150,6 +256,7 @@ def build_extension(self, ext): unicode_width = 2 if sys.maxunicode < 0x10FFFF else 4 else: import ctypes + unicode_width = ctypes.sizeof(ctypes.c_wchar) defines = [ @@ -161,7 +268,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") @@ -171,7 +278,7 @@ def build_extension(self, ext): # Double-check if libpython is linked dynamically with python ldd_cmd = ["otool", "-L"] if on_darwin else ["ldd"] lddout = _check_output(ldd_cmd + [sys.executable]) - if 'libpython' not in lddout: + if "libpython" not in lddout: enable_shared = False if not enable_shared: @@ -193,23 +300,42 @@ 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" _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', - '/p:Configuration={}'.format(_config), - '/p:Platform={}'.format(ARCH), - '/p:DefineConstants="{}"'.format(','.join(defines)), + _solution_file, + "/p:Configuration={}".format(_config), + "/p:Platform={}".format(ARCH), + '/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), + "/p:PackageId=pythonnet_py{0}{1}_{2}".format(PY_MAJOR, PY_MINOR, ARCH), + "/verbosity:{}".format(VERBOSITY), ] manifest = self._get_manifest(dest_dir) @@ -217,26 +343,46 @@ 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")) - cmd = [mt, '-inputresource:"{0}"'.format(sys.executable), - '-out:"{0}"'.format(manifest)] + cmd = [ + mt, + '-inputresource:"{0}"'.format(sys.executable), + '-out:"{0}"'.format(manifest), + ] self.debug_print("Extracting manifest from {}".format(sys.executable)) subprocess.check_call(" ".join(cmd), shell=False) return manifest def _build_monoclr(self): - mono_libs = _check_output("pkg-config --libs mono-2", shell=True) + try: + mono_libs = _check_output("pkg-config --libs mono-2", shell=True) + except: + if DEVTOOLS == "dotnet": + print("Skipping building monoclr module...") + return + raise mono_cflags = _check_output("pkg-config --cflags mono-2", shell=True) glib_libs = _check_output("pkg-config --libs glib-2.0", shell=True) glib_cflags = _check_output("pkg-config --cflags glib-2.0", shell=True) @@ -246,34 +392,80 @@ def _build_monoclr(self): # build the clr python module clr_ext = Extension( "clr", - sources=[ - "src/monoclr/pynetinit.c", - "src/monoclr/clrmod.c" - ], + sources=["src/monoclr/pynetinit.c", "src/monoclr/clrmod.c"], extra_compile_args=cflags.split(" "), - extra_link_args=libs.split(" ") + extra_link_args=libs.split(" "), ) build_ext.build_ext.build_extension(self, clr_ext) 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) + try: + # msbuild=14 is mainly for Mono issues + cmd = "{0} restore pythonnet.sln -MSBuildVersion 14 -o packages".format( + nuget + ) + self.debug_print("Installing packages: {0}".format(cmd)) + subprocess.check_call(cmd, shell=use_shell) + except: + # when only VS 2017 is installed do not specify msbuild version + 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: @@ -285,6 +477,8 @@ def _find_msbuild_tool(self, tool="msbuild.exe", use_windows_sdk=False): except ImportError: # PY3 import winreg + _collect_installed_windows_kits_v10(winreg) + keys_to_check = WIN_SDK_KEYS if use_windows_sdk else VS_KEYS hklm = winreg.HKEY_LOCAL_MACHINE for rkey in keys_to_check: @@ -295,8 +489,9 @@ def _find_msbuild_tool(self, tool="msbuild.exe", use_windows_sdk=False): continue path = os.path.join(val, rkey.suffix, tool) if os.path.exists(path): - self.debug_print("Using {0} from {1}".format( - tool, rkey.sdk_name)) + self.debug_print( + "Using {0} from {1}".format(tool, rkey.sdk_name) + ) return path except WindowsError: # Key doesn't exist @@ -316,12 +511,44 @@ 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): if not os.path.isdir(self.build_dir): - self.warn("'{0}' does not exist -- no Python modules" - " to install".format(self.build_dir)) + self.warn( + "'{0}' does not exist -- no Python modules" + " to install".format(self.build_dir) + ) return if not os.path.exists(self.install_dir): @@ -329,8 +556,7 @@ def install(self): # only copy clr.pyd/.so for srcfile in glob.glob(os.path.join(self.build_dir, "clr.*")): - destfile = os.path.join( - self.install_dir, os.path.basename(srcfile)) + destfile = os.path.join(self.install_dir, os.path.basename(srcfile)) self.copy_file(srcfile, destfile) @@ -339,8 +565,7 @@ def run(self): build_cmd = self.get_finalized_command("build_ext") install_cmd = self.get_finalized_command("install") build_lib = os.path.abspath(build_cmd.build_lib) - install_platlib = os.path.relpath( - install_cmd.install_platlib, self.install_dir) + install_platlib = os.path.relpath(install_cmd.install_platlib, self.install_dir) for i, data_files in enumerate(self.data_files): if isinstance(data_files, str): @@ -354,7 +579,40 @@ 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,42 +623,37 @@ def run(self): setup( name="pythonnet", - version="2.3.0", + version="2.4.1-dev", description=".Net and Mono integration for Python", - url='https://pythonnet.github.io/', - license='MIT', + url="https://pythonnet.github.io/", + license="MIT", author="The Python for .Net developers", author_email="pythondotnet@python.org", setup_requires=setup_requires, long_description=_get_long_description(), - ext_modules=[ - Extension("clr", sources=list(_get_source_files())) - ], - data_files=[ - ("{install_platlib}", [ - "{build_lib}/Python.Runtime.dll", - ]), - ], + ext_modules=[Extension("clr", sources=list(_get_source_files()))], + data_files=[("{install_platlib}", ["{build_lib}/Python.Runtime.dll"])], cmdclass={ + "install": InstallPythonnet, "build_ext": BuildExtPythonnet, "install_lib": InstallLibPythonnet, "install_data": InstallDataPythonnet, + "bdist_wheel": BDistWheelPythonnet, }, classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: C#', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux', - 'Operating System :: MacOS :: MacOS X', + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: C#", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", ], zip_safe=False, ) diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs index 24ba26862..dc72b0bdf 100644 --- a/src/SharedAssemblyInfo.cs +++ b/src/SharedAssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("pythonnet")] [assembly: AssemblyProduct("Python for .NET")] -[assembly: AssemblyCopyright("Copyright (c) 2006-2017 the contributors of the 'Python for .NET' project")] +[assembly: AssemblyCopyright("Copyright (c) 2006-2019 the contributors of the 'Python for .NET' project")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -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.1")] diff --git a/src/clrmodule.csproj b/src/clrmodule.csproj new file mode 100644 index 000000000..3e1d353a2 --- /dev/null +++ b/src/clrmodule.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + PYTHON3;TRACE;DEBUG + + + diff --git a/src/clrmodule/ClrModule.cs b/src/clrmodule/ClrModule.cs index d24376a1f..7fc654fd6 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.1"), #endif CultureInfo = CultureInfo.InvariantCulture }; diff --git a/src/clrmodule/Properties/AssemblyInfo.cs b/src/clrmodule/Properties/AssemblyInfo.cs deleted file mode 100644 index 939f4171f..000000000 --- a/src/clrmodule/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("clrmodule")] -[assembly: AssemblyDescription("")] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("ae10d6a4-55c2-482f-9716-9988e6c169e3")] diff --git a/src/clrmodule/clrmodule.csproj b/src/clrmodule/clrmodule.csproj index 6e5ff4966..3b0e27b9f 100644 --- a/src/clrmodule/clrmodule.csproj +++ b/src/clrmodule/clrmodule.csproj @@ -1,95 +1,15 @@ - - + + - Debug - AnyCPU - {86E834DE-1139-4511-96CC-69636A56E7AC} - Library - clrmodule - clrmodule - bin\clrmodule.xml - bin\ - v4.0 - - 1591 - ..\..\ - $(SolutionDir)\bin\ - Properties - 6 - true - prompt + netstandard2.0 - - x86 + + + TRACE;DEBUG;PYTHON3 - - x64 - - - true - PYTHON2;TRACE;DEBUG - full - - - PYTHON2 - true - pdbonly - - - true - PYTHON2;TRACE;DEBUG - full - - - PYTHON2 - true - pdbonly - - - true - PYTHON3;TRACE;DEBUG - full - - - PYTHON3 - true - pdbonly - - - true - PYTHON3;TRACE;DEBUG - full - - - PYTHON3 - true - pdbonly - - - - ..\..\packages\UnmanagedExports.1.2.7\lib\net\RGiesecke.DllExport.Metadata.dll - False - - - + - - - Properties\SharedAssemblyInfo.cs - - + - - - - - - $(TargetPath) - $(TargetDir)$(TargetName).pdb - - - - - - + diff --git a/src/console/Console.csproj b/src/console/Console.csproj index ea88b6356..89b9b00e5 100644 --- a/src/console/Console.csproj +++ b/src/console/Console.csproj @@ -1,101 +1,16 @@ - - + + - Debug - AnyCPU - {E29DCF0A-5114-4A98-B1DD-71264B6EA349} Exe - nPython - Python.Runtime - bin\nPython.xml - bin\ - v4.0 - - 1591 - ..\..\ - $(SolutionDir)\bin\ - Properties - 6 - python-clear.ico - prompt + netcoreapp2.2 - - x86 - - - x64 - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - $(PythonManifest) - - - - - - - - Properties\SharedAssemblyInfo.cs - - - + - - - Python.Runtime.dll - + + - - {097b4ac0-74e9-4c58-bcf8-c69746ec8271} - Python.Runtime - + - - - - + diff --git a/src/console/Properties/AssemblyInfo.cs b/src/console/Properties/AssemblyInfo.cs deleted file mode 100644 index 081ae0c94..000000000 --- a/src/console/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Reflection; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Python Console")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyDefaultAlias("python.exe")] diff --git a/src/console/app.config b/src/console/app.config new file mode 100644 index 000000000..312bb3f26 --- /dev/null +++ b/src/console/app.config @@ -0,0 +1,3 @@ + + + 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/GlobalTestsSetup.cs b/src/embed_tests/GlobalTestsSetup.cs new file mode 100644 index 000000000..458ab6a99 --- /dev/null +++ b/src/embed_tests/GlobalTestsSetup.cs @@ -0,0 +1,21 @@ +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + + // As the SetUpFixture, the OneTimeTearDown of this class is executed after + // all tests have run. + [SetUpFixture] + public class GlobalTestsSetup + { + [OneTimeTearDown] + public void FinalCleanup() + { + if (PythonEngine.IsInitialized) + { + PythonEngine.Shutdown(); + } + } + } +} 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.csproj b/src/embed_tests/Python.EmbeddingTest.csproj index 9c417cd27..a83853938 100644 --- a/src/embed_tests/Python.EmbeddingTest.csproj +++ b/src/embed_tests/Python.EmbeddingTest.csproj @@ -1,119 +1,23 @@ - - + + - Debug - AnyCPU - {4165C59D-2822-499F-A6DB-EACA4C331EB5} - Library - Python.EmbeddingTest - Python.EmbeddingTest - bin\Python.EmbeddingTest.xml - bin\ - v4.0 - - 1591 - ..\..\ - $(SolutionDir)\bin\ - 6 - true - prompt + netcoreapp2.2 + + false - - x86 - - - x64 - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - - - ..\..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll - - - + - - + + - - - - - - - - - - - - - - - - - + + + + - - {097B4AC0-74E9-4C58-BCF8-C69746EC8271} - Python.Runtime - + - - - - - - $(TargetPath) - $(TargetDir)$(TargetName).pdb - - - - - + diff --git a/src/embed_tests/TestConverter.cs b/src/embed_tests/TestConverter.cs new file mode 100644 index 000000000..73a8942b1 --- /dev/null +++ b/src/embed_tests/TestConverter.cs @@ -0,0 +1,49 @@ +using NUnit.Framework; +using Python.Runtime; +using System; + +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/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs new file mode 100644 index 000000000..b162d4eb0 --- /dev/null +++ b/src/embed_tests/TestDomainReload.cs @@ -0,0 +1,239 @@ +using System; +using System.CodeDom.Compiler; +using System.Reflection; +using NUnit.Framework; +using Python.Runtime; + +// +// This test case is disabled on .NET Standard because it doesn't have all the +// APIs we use. We could work around that, but .NET Core doesn't implement +// domain creation, so it's not worth it. +// +// Unfortunately this means no continuous integration testing for this case. +// +#if !NETSTANDARD && !NETCOREAPP +namespace Python.EmbeddingTest +{ + class TestDomainReload + { + /// + /// Test that the python runtime can survive a C# domain reload without crashing. + /// + /// At the time this test was written, there was a very annoying + /// seemingly random crash bug when integrating pythonnet into Unity. + /// + /// The repro steps that David Lassonde, Viktoria Kovecses and + /// Benoit Hudson eventually worked out: + /// 1. Write a HelloWorld.cs script that uses Python.Runtime to access + /// some C# data from python: C# calls python, which calls C#. + /// 2. Execute the script (e.g. make it a MenuItem and click it). + /// 3. Touch HelloWorld.cs on disk, forcing Unity to recompile scripts. + /// 4. Wait several seconds for Unity to be done recompiling and + /// reloading the C# domain. + /// 5. Make python run the gc (e.g. by calling gc.collect()). + /// + /// The reason: + /// A. In step 2, Python.Runtime registers a bunch of new types with + /// their tp_traverse slot pointing to managed code, and allocates + /// some objects of those types. + /// B. In step 4, Unity unloads the C# domain. That frees the managed + /// code. But at the time of the crash investigation, pythonnet + /// leaked the python side of the objects allocated in step 1. + /// C. In step 5, python sees some pythonnet objects in its gc list of + /// potentially-leaked objects. It calls tp_traverse on those objects. + /// But tp_traverse was freed in step 3 => CRASH. + /// + /// This test distills what's going on without needing Unity around (we'd see + /// similar behaviour if we were using pythonnet on a .NET web server that did + /// a hot reload). + /// + [Test] + public static void DomainReloadAndGC() + { + // We're set up to run in the directory that includes the bin directory. + System.IO.Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); + + Assembly pythonRunner1 = BuildAssembly("test1"); + RunAssemblyAndUnload(pythonRunner1, "test1"); + + // Verify that python is not initialized even though we ran it. + Assert.That(Runtime.Runtime.Py_IsInitialized(), Is.Zero); + + // This caused a crash because objects allocated in pythonRunner1 + // still existed in memory, but the code to do python GC on those + // objects is gone. + Assembly pythonRunner2 = BuildAssembly("test2"); + RunAssemblyAndUnload(pythonRunner2, "test2"); + } + + // + // The code we'll test. All that really matters is + // using GIL { Python.Exec(pyScript); } + // but the rest is useful for debugging. + // + // What matters in the python code is gc.collect and clr.AddReference. + // + // Note that the language version is 2.0, so no $"foo{bar}" syntax. + // + const string TestCode = @" + using Python.Runtime; + using System; + class PythonRunner { + public static void RunPython() { + AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; + string name = AppDomain.CurrentDomain.FriendlyName; + Console.WriteLine(string.Format(""[{0} in .NET] In PythonRunner.RunPython"", name)); + using (Py.GIL()) { + try { + var pyScript = string.Format(""import clr\n"" + + ""print('[{0} in python] imported clr')\n"" + + ""clr.AddReference('System')\n"" + + ""print('[{0} in python] allocated a clr object')\n"" + + ""import gc\n"" + + ""gc.collect()\n"" + + ""print('[{0} in python] collected garbage')\n"", + name); + PythonEngine.Exec(pyScript); + } catch(Exception e) { + Console.WriteLine(string.Format(""[{0} in .NET] Caught exception: {1}"", name, e)); + } + } + } + static void OnDomainUnload(object sender, EventArgs e) { + System.Console.WriteLine(string.Format(""[{0} in .NET] unloading"", AppDomain.CurrentDomain.FriendlyName)); + } + }"; + + + /// + /// Build an assembly out of the source code above. + /// + /// This creates a file .dll in order + /// to support the statement "proxy.theAssembly = assembly" below. + /// That statement needs a file, can't run via memory. + /// + static Assembly BuildAssembly(string assemblyName) + { + var provider = CodeDomProvider.CreateProvider("CSharp"); + + var compilerparams = new CompilerParameters(); + compilerparams.ReferencedAssemblies.Add("Python.Runtime.dll"); + compilerparams.GenerateExecutable = false; + compilerparams.GenerateInMemory = false; + compilerparams.IncludeDebugInformation = false; + compilerparams.OutputAssembly = assemblyName; + + var results = provider.CompileAssemblyFromSource(compilerparams, TestCode); + if (results.Errors.HasErrors) + { + var errors = new System.Text.StringBuilder("Compiler Errors:\n"); + foreach (CompilerError error in results.Errors) + { + errors.AppendFormat("Line {0},{1}\t: {2}\n", + error.Line, error.Column, error.ErrorText); + } + throw new Exception(errors.ToString()); + } + else + { + return results.CompiledAssembly; + } + } + + /// + /// This is a magic incantation required to run code in an application + /// domain other than the current one. + /// + class Proxy : MarshalByRefObject + { + Assembly theAssembly = null; + + public void InitAssembly(string assemblyPath) + { + theAssembly = Assembly.LoadFile(System.IO.Path.GetFullPath(assemblyPath)); + } + + public void RunPython() + { + Console.WriteLine("[Proxy] Entering RunPython"); + + // Call into the new assembly. Will execute Python code + var pythonrunner = theAssembly.GetType("PythonRunner"); + var runPythonMethod = pythonrunner.GetMethod("RunPython"); + runPythonMethod.Invoke(null, new object[] { }); + + Console.WriteLine("[Proxy] Leaving RunPython"); + } + } + + /// + /// Create a domain, run the assembly in it (the RunPython function), + /// and unload the domain. + /// + static void RunAssemblyAndUnload(Assembly assembly, string assemblyName) + { + Console.WriteLine($"[Program.Main] === creating domain for assembly {assembly.FullName}"); + + // Create the domain. Make sure to set PrivateBinPath to a relative + // path from the CWD (namely, 'bin'). + // See https://stackoverflow.com/questions/24760543/createinstanceandunwrap-in-another-domain + var currentDomain = AppDomain.CurrentDomain; + var domainsetup = new AppDomainSetup() + { + ApplicationBase = currentDomain.SetupInformation.ApplicationBase, + ConfigurationFile = currentDomain.SetupInformation.ConfigurationFile, + LoaderOptimization = LoaderOptimization.SingleDomain, + PrivateBinPath = "." + }; + var domain = AppDomain.CreateDomain( + $"My Domain {assemblyName}", + currentDomain.Evidence, + domainsetup); + + // Create a Proxy object in the new domain, where we want the + // assembly (and Python .NET) to reside + Type type = typeof(Proxy); + System.IO.Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); + var theProxy = (Proxy)domain.CreateInstanceAndUnwrap( + type.Assembly.FullName, + type.FullName); + + // From now on use the Proxy to call into the new assembly + theProxy.InitAssembly(assemblyName); + theProxy.RunPython(); + + Console.WriteLine($"[Program.Main] Before Domain Unload on {assembly.FullName}"); + AppDomain.Unload(domain); + Console.WriteLine($"[Program.Main] After Domain Unload on {assembly.FullName}"); + + // Validate that the assembly does not exist anymore + try + { + Console.WriteLine($"[Program.Main] The Proxy object is valid ({theProxy}). Unexpected domain unload behavior"); + } + catch (Exception) + { + Console.WriteLine("[Program.Main] The Proxy object is not valid anymore, domain unload complete."); + } + } + + /// + /// Resolves the assembly. Why doesn't this just work normally? + /// + static Assembly ResolveAssembly(object sender, ResolveEventArgs args) + { + var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + + foreach (var assembly in loadedAssemblies) + { + if (assembly.FullName == args.Name) + { + return assembly; + } + } + + return null; + } + } +} +#endif diff --git a/src/embed_tests/TestExample.cs b/src/embed_tests/TestExample.cs new file mode 100644 index 000000000..671f9e33d --- /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().Replace(" ", " ")); + } + } +} diff --git a/src/embed_tests/TestFinalizer.cs b/src/embed_tests/TestFinalizer.cs new file mode 100644 index 000000000..650ee5686 --- /dev/null +++ b/src/embed_tests/TestFinalizer.cs @@ -0,0 +1,253 @@ +using NUnit.Framework; +using Python.Runtime; +using System; +using System.Linq; +using System.Threading; + +namespace Python.EmbeddingTest +{ + public class TestFinalizer + { + private int _oldThreshold; + + [SetUp] + public void SetUp() + { + _oldThreshold = Finalizer.Instance.Threshold; + PythonEngine.Initialize(); + Exceptions.Clear(); + } + + [TearDown] + public void TearDown() + { + Finalizer.Instance.Threshold = _oldThreshold; + PythonEngine.Shutdown(); + } + + private static void FullGCCollect() + { + GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); + GC.WaitForPendingFinalizers(); + } + + [Test] + public void CollectBasicObject() + { + Assert.IsTrue(Finalizer.Instance.Enable); + + Finalizer.Instance.Threshold = 1; + bool called = false; + var objectCount = 0; + EventHandler handler = (s, e) => + { + objectCount = e.ObjectCount; + called = true; + }; + + Assert.IsFalse(called, "The event handler was called before it was installed"); + Finalizer.Instance.CollectOnce += handler; + + WeakReference shortWeak; + WeakReference longWeak; + { + MakeAGarbage(out shortWeak, out longWeak); + } + FullGCCollect(); + // The object has been resurrected + Warn.If( + shortWeak.IsAlive, + "The referenced object is alive although it should have been collected", + shortWeak + ); + Assert.IsTrue( + longWeak.IsAlive, + "The reference object is not alive although it should still be", + longWeak + ); + + { + var garbage = Finalizer.Instance.GetCollectedObjects(); + Assert.NotZero(garbage.Count, "There should still be garbage around"); + Warn.Unless( + garbage.Any(T => ReferenceEquals(T.Target, longWeak.Target)), + $"The {nameof(longWeak)} reference doesn't show up in the garbage list", + garbage + ); + } + try + { + Finalizer.Instance.Collect(forceDispose: false); + } + finally + { + Finalizer.Instance.CollectOnce -= handler; + } + Assert.IsTrue(called, "The event handler was not called during finalization"); + Assert.GreaterOrEqual(objectCount, 1); + } + + private static void MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak) + { + PyLong obj = new PyLong(1024); + shortWeak = new WeakReference(obj); + longWeak = new WeakReference(obj, true); + obj = null; + } + + private static long CompareWithFinalizerOn(PyObject pyCollect, bool enbale) + { + // Must larger than 512 bytes make sure Python use + string str = new string('1', 1024); + Finalizer.Instance.Enable = true; + FullGCCollect(); + FullGCCollect(); + pyCollect.Invoke(); + Finalizer.Instance.Collect(); + Finalizer.Instance.Enable = enbale; + + // Estimate unmanaged memory size + long before = Environment.WorkingSet - GC.GetTotalMemory(true); + for (int i = 0; i < 10000; i++) + { + // Memory will leak when disable Finalizer + new PyString(str); + } + FullGCCollect(); + FullGCCollect(); + pyCollect.Invoke(); + if (enbale) + { + Finalizer.Instance.Collect(); + } + + FullGCCollect(); + FullGCCollect(); + long after = Environment.WorkingSet - GC.GetTotalMemory(true); + return after - before; + + } + + /// + /// Because of two vms both have their memory manager, + /// this test only prove the finalizer has take effect. + /// + [Test] + [Ignore("Too many uncertainties, only manual on when debugging")] + public void SimpleTestMemory() + { + bool oldState = Finalizer.Instance.Enable; + try + { + using (PyObject gcModule = PythonEngine.ImportModule("gc")) + using (PyObject pyCollect = gcModule.GetAttr("collect")) + { + long span1 = CompareWithFinalizerOn(pyCollect, false); + long span2 = CompareWithFinalizerOn(pyCollect, true); + Assert.Less(span2, span1); + } + } + finally + { + Finalizer.Instance.Enable = oldState; + } + } + + class MyPyObject : PyObject + { + public MyPyObject(IntPtr op) : base(op) + { + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + GC.SuppressFinalize(this); + throw new Exception("MyPyObject"); + } + internal static void CreateMyPyObject(IntPtr op) + { + Runtime.Runtime.XIncref(op); + new MyPyObject(op); + } + } + + [Test] + public void ErrorHandling() + { + bool called = false; + var errorMessage = ""; + EventHandler handleFunc = (sender, args) => + { + called = true; + errorMessage = args.Error.Message; + }; + Finalizer.Instance.Threshold = 1; + Finalizer.Instance.ErrorHandler += handleFunc; + try + { + WeakReference shortWeak; + WeakReference longWeak; + { + MakeAGarbage(out shortWeak, out longWeak); + var obj = (PyLong)longWeak.Target; + IntPtr handle = obj.Handle; + shortWeak = null; + longWeak = null; + MyPyObject.CreateMyPyObject(handle); + obj.Dispose(); + obj = null; + } + FullGCCollect(); + Finalizer.Instance.Collect(); + Assert.IsTrue(called); + } + finally + { + Finalizer.Instance.ErrorHandler -= handleFunc; + } + Assert.AreEqual(errorMessage, "MyPyObject"); + } + + [Test] + public void ValidateRefCount() + { + if (!Finalizer.Instance.RefCountValidationEnabled) + { + Assert.Pass("Only run with FINALIZER_CHECK"); + } + IntPtr ptr = IntPtr.Zero; + bool called = false; + Finalizer.IncorrectRefCntHandler handler = (s, e) => + { + called = true; + Assert.AreEqual(ptr, e.Handle); + Assert.AreEqual(2, e.ImpactedObjects.Count); + // Fix for this test, don't do this on general environment + Runtime.Runtime.XIncref(e.Handle); + return false; + }; + Finalizer.Instance.IncorrectRefCntResolver += handler; + try + { + ptr = CreateStringGarbage(); + FullGCCollect(); + Assert.Throws(() => Finalizer.Instance.Collect()); + Assert.IsTrue(called); + } + finally + { + Finalizer.Instance.IncorrectRefCntResolver -= handler; + } + } + + private static IntPtr CreateStringGarbage() + { + PyString s1 = new PyString("test_string"); + // s2 steal a reference from s1 + PyString s2 = new PyString(s1.Handle); + return s1.Handle; + } + + } +} diff --git a/src/embed_tests/TestNamedArguments.cs b/src/embed_tests/TestNamedArguments.cs new file mode 100644 index 000000000..31f2ea1d2 --- /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/TestPyAnsiString.cs b/src/embed_tests/TestPyAnsiString.cs index 9ba7d6cc6..b4a965ff7 100644 --- a/src/embed_tests/TestPyAnsiString.cs +++ b/src/embed_tests/TestPyAnsiString.cs @@ -63,6 +63,7 @@ public void TestCtorPtr() const string expected = "foo"; var t = new PyAnsiString(expected); + Runtime.Runtime.XIncref(t.Handle); var actual = new PyAnsiString(t.Handle); Assert.AreEqual(expected, actual.ToString()); diff --git a/src/embed_tests/TestPyFloat.cs b/src/embed_tests/TestPyFloat.cs index f2c85a77f..94e7026c7 100644 --- a/src/embed_tests/TestPyFloat.cs +++ b/src/embed_tests/TestPyFloat.cs @@ -25,6 +25,7 @@ public void Dispose() public void IntPtrCtor() { var i = new PyFloat(1); + Runtime.Runtime.XIncref(i.Handle); var ii = new PyFloat(i.Handle); Assert.AreEqual(i.Handle, ii.Handle); } diff --git a/src/embed_tests/TestPyInt.cs b/src/embed_tests/TestPyInt.cs index 4117336d8..005ab466d 100644 --- a/src/embed_tests/TestPyInt.cs +++ b/src/embed_tests/TestPyInt.cs @@ -86,6 +86,7 @@ public void TestCtorSByte() public void TestCtorPtr() { var i = new PyInt(5); + Runtime.Runtime.XIncref(i.Handle); var a = new PyInt(i.Handle); Assert.AreEqual(5, a.ToInt32()); } @@ -94,6 +95,7 @@ public void TestCtorPtr() public void TestCtorPyObject() { var i = new PyInt(5); + Runtime.Runtime.XIncref(i.Handle); var a = new PyInt(i); Assert.AreEqual(5, a.ToInt32()); } diff --git a/src/embed_tests/TestPyLong.cs b/src/embed_tests/TestPyLong.cs index fe3e13ef5..3c155f315 100644 --- a/src/embed_tests/TestPyLong.cs +++ b/src/embed_tests/TestPyLong.cs @@ -102,6 +102,7 @@ public void TestCtorDouble() public void TestCtorPtr() { var i = new PyLong(5); + Runtime.Runtime.XIncref(i.Handle); var a = new PyLong(i.Handle); Assert.AreEqual(5, a.ToInt32()); } @@ -110,6 +111,7 @@ public void TestCtorPtr() public void TestCtorPyObject() { var i = new PyLong(5); + Runtime.Runtime.XIncref(i.Handle); var a = new PyLong(i); Assert.AreEqual(5, a.ToInt32()); } diff --git a/src/embed_tests/TestPyObject.cs b/src/embed_tests/TestPyObject.cs new file mode 100644 index 000000000..65ac20e9a --- /dev/null +++ b/src/embed_tests/TestPyObject.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class TestPyObject + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + [Test] + public void TestGetDynamicMemberNames() + { + List expectedMemberNames = new List + { + "add", + "getNumber", + "member1", + "member2" + }; + + PyDict locals = new PyDict(); + + PythonEngine.Exec(@" +class MemberNamesTest(object): + def __init__(self): + self.member1 = 123 + self.member2 = 'Test string' + + def getNumber(self): + return 123 + + def add(self, x, y): + return x + y + +a = MemberNamesTest() +", null, locals.Handle); + + PyObject a = locals.GetItem("a"); + + IEnumerable memberNames = a.GetDynamicMemberNames(); + + foreach (string expectedName in expectedMemberNames) + { + Assert.IsTrue(memberNames.Contains(expectedName), "Could not find member '{0}'.", expectedName); + } + } + } +} diff --git a/src/embed_tests/TestPyScope.cs b/src/embed_tests/TestPyScope.cs new file mode 100644 index 000000000..21c0d2b3f --- /dev/null +++ b/src/embed_tests/TestPyScope.cs @@ -0,0 +1,381 @@ +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() + { + using (Py.GIL()) + { + (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(); + try + { + 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); + } + } + finally + { + PythonEngine.EndAllowThreads(ts); + } + } + } +} diff --git a/src/embed_tests/TestPyString.cs b/src/embed_tests/TestPyString.cs index 9d1cdb0e9..0de436e35 100644 --- a/src/embed_tests/TestPyString.cs +++ b/src/embed_tests/TestPyString.cs @@ -64,6 +64,7 @@ public void TestCtorPtr() const string expected = "foo"; var t = new PyString(expected); + Runtime.Runtime.XIncref(t.Handle); var actual = new PyString(t.Handle); Assert.AreEqual(expected, actual.ToString()); diff --git a/src/embed_tests/TestPyWith.cs b/src/embed_tests/TestPyWith.cs new file mode 100644 index 000000000..0c4e9023f --- /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/TestPythonEngineProperties.cs b/src/embed_tests/TestPythonEngineProperties.cs index 01c6ae7e3..243349b82 100644 --- a/src/embed_tests/TestPythonEngineProperties.cs +++ b/src/embed_tests/TestPythonEngineProperties.cs @@ -109,18 +109,41 @@ public static void GetPythonHomeDefault() [Test] public void SetPythonHome() { + // We needs to ensure that engine was started and shutdown at least once before setting dummy home. + // Otherwise engine will not run with dummy path with random problem. + if (!PythonEngine.IsInitialized) + { + PythonEngine.Initialize(); + } + + PythonEngine.Shutdown(); + + var pythonHomeBackup = PythonEngine.PythonHome; + var pythonHome = "/dummypath/"; PythonEngine.PythonHome = pythonHome; PythonEngine.Initialize(); - Assert.AreEqual(pythonHome, PythonEngine.PythonHome); PythonEngine.Shutdown(); + + // Restoring valid pythonhome. + PythonEngine.PythonHome = pythonHomeBackup; } [Test] public void SetPythonHomeTwice() { + // We needs to ensure that engine was started and shutdown at least once before setting dummy home. + // Otherwise engine will not run with dummy path with random problem. + if (!PythonEngine.IsInitialized) + { + PythonEngine.Initialize(); + } + PythonEngine.Shutdown(); + + var pythonHomeBackup = PythonEngine.PythonHome; + var pythonHome = "/dummypath/"; PythonEngine.PythonHome = "/dummypath2/"; @@ -129,11 +152,20 @@ public void SetPythonHomeTwice() Assert.AreEqual(pythonHome, PythonEngine.PythonHome); PythonEngine.Shutdown(); + + PythonEngine.PythonHome = pythonHomeBackup; } [Test] public void SetProgramName() { + if (PythonEngine.IsInitialized) + { + PythonEngine.Shutdown(); + } + + var programNameBackup = PythonEngine.ProgramName; + var programName = "FooBar"; PythonEngine.ProgramName = programName; @@ -141,6 +173,8 @@ public void SetProgramName() Assert.AreEqual(programName, PythonEngine.ProgramName); PythonEngine.Shutdown(); + + PythonEngine.ProgramName = programNameBackup; } [Test] @@ -156,7 +190,7 @@ public void SetPythonPath() string path = PythonEngine.PythonPath; PythonEngine.Shutdown(); - PythonEngine.ProgramName = path; + PythonEngine.PythonPath = path; PythonEngine.Initialize(); Assert.AreEqual(path, PythonEngine.PythonPath); @@ -171,7 +205,6 @@ public void SetPythonPathExceptionOn27() Assert.Pass(); } - // Get previous path to avoid crashing Python PythonEngine.Initialize(); string path = PythonEngine.PythonPath; PythonEngine.Shutdown(); diff --git a/src/embed_tests/TestPythonException.cs b/src/embed_tests/TestPythonException.cs index 5470b246f..57a8d54af 100644 --- a/src/embed_tests/TestPythonException.cs +++ b/src/embed_tests/TestPythonException.cs @@ -40,5 +40,19 @@ public void TestNoError() var e = new PythonException(); // There is no PyErr to fetch Assert.AreEqual("", e.Message); } + + [Test] + public void TestPythonErrorTypeName() + { + try + { + var module = PythonEngine.ImportModule("really____unknown___module"); + Assert.Fail("Unknown module should not be loaded"); + } + catch (PythonException ex) + { + Assert.That(ex.PythonTypeName, Is.EqualTo("ModuleNotFoundError").Or.EqualTo("ImportError")); + } + } } } diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs index 22e6db0a9..ac1fa1ac0 100644 --- a/src/embed_tests/TestRuntime.cs +++ b/src/embed_tests/TestRuntime.cs @@ -6,10 +6,40 @@ namespace Python.EmbeddingTest { public class TestRuntime { + [OneTimeSetUp] + public void SetUp() + { + // We needs to ensure that no any engines are running. + if (PythonEngine.IsInitialized) + { + PythonEngine.Shutdown(); + } + } + + /// + /// Test the cache of the information from the platform module. + /// + /// Test fails on platforms we haven't implemented yet. + /// + [Test] + public static void PlatformCache() + { + Runtime.Runtime.Initialize(); + + Assert.That(Runtime.Runtime.Machine, Is.Not.EqualTo(Runtime.Runtime.MachineType.Other)); + Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.MachineName)); + + Assert.That(Runtime.Runtime.OperatingSystem, Is.Not.EqualTo(Runtime.Runtime.OperatingSystemType.Other)); + Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.OperatingSystemName)); + + // Don't shut down the runtime: if the python engine was initialized + // but not shut down by another test, we'd end up in a bad state. + } + [Test] public static void Py_IsInitializedValue() { - Runtime.Runtime.Py_Finalize(); // In case another test left it on. + Runtime.Runtime.Py_Finalize(); Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized()); Runtime.Runtime.Py_Initialize(); Assert.AreEqual(1, Runtime.Runtime.Py_IsInitialized()); @@ -47,5 +77,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/TestTypeManager.cs b/src/embed_tests/TestTypeManager.cs new file mode 100644 index 000000000..931c44236 --- /dev/null +++ b/src/embed_tests/TestTypeManager.cs @@ -0,0 +1,65 @@ +using NUnit.Framework; +using Python.Runtime; +using System.Runtime.InteropServices; + +namespace Python.EmbeddingTest +{ + class TestTypeManager + { + [SetUp] + public static void Init() + { + Runtime.Runtime.Initialize(); + } + + [TearDown] + public static void Fini() + { + // Don't shut down the runtime: if the python engine was initialized + // but not shut down by another test, we'd end up in a bad state. + } + + [Test] + public static void TestNativeCode() + { + Assert.That(() => { var _ = TypeManager.NativeCode.Active; }, Throws.Nothing); + Assert.That(TypeManager.NativeCode.Active.Code.Length, Is.GreaterThan(0)); + } + + [Test] + public static void TestMemoryMapping() + { + Assert.That(() => { var _ = TypeManager.CreateMemoryMapper(); }, Throws.Nothing); + var mapper = TypeManager.CreateMemoryMapper(); + + // Allocate a read-write page. + int len = 12; + var page = mapper.MapWriteable(len); + Assert.That(() => { Marshal.WriteInt64(page, 17); }, Throws.Nothing); + Assert.That(Marshal.ReadInt64(page), Is.EqualTo(17)); + + // Mark it read-execute. We can still read, haven't changed any values. + mapper.SetReadExec(page, len); + Assert.That(Marshal.ReadInt64(page), Is.EqualTo(17)); + + // Test that we can't write to the protected page. + // + // We can't actually test access protection under Microsoft + // versions of .NET, because AccessViolationException is assumed to + // mean we're in a corrupted state: + // https://stackoverflow.com/questions/3469368/how-to-handle-accessviolationexception + // + // We can test under Mono but it throws NRE instead of AccessViolationException. + // + // We can't use compiler flags because we compile with MONO_LINUX + // while running on the Microsoft .NET Core during continuous + // integration tests. + /* if (System.Type.GetType ("Mono.Runtime") != null) + { + // Mono throws NRE instead of AccessViolationException for some reason. + Assert.That(() => { Marshal.WriteInt64(page, 73); }, Throws.TypeOf()); + Assert.That(Marshal.ReadInt64(page), Is.EqualTo(17)); + } */ + } + } +} diff --git a/src/embed_tests/dynamic.cs b/src/embed_tests/dynamic.cs index 94397072a..81345cee7 100644 --- a/src/embed_tests/dynamic.cs +++ b/src/embed_tests/dynamic.cs @@ -12,13 +12,23 @@ public class DynamicTest [SetUp] public void SetUp() { - _gs = Py.GIL(); + try { + _gs = Py.GIL(); + } catch (Exception e) { + Console.WriteLine($"exception in SetUp: {e}"); + throw; + } } [TearDown] public void Dispose() { - _gs.Dispose(); + try { + _gs.Dispose(); + } catch(Exception e) { + Console.WriteLine($"exception in TearDown: {e}"); + throw; + } } /// @@ -103,7 +113,7 @@ public void PassObjectInPython() Assert.AreEqual(sys.testattr3.ToString(), "True"); // Compare in .NET - Assert.AreEqual(sys.testattr1, sys.testattr2); + Assert.IsTrue(sys.testattr1.Equals(sys.testattr2)); } /// @@ -118,14 +128,15 @@ public void PassPyObjectInNet() sys.testattr2 = sys.testattr1; // Compare in Python - PyObject res = PythonEngine.RunString( + PythonEngine.RunSimpleString( "import sys\n" + "sys.testattr3 = sys.testattr1 is sys.testattr2\n" ); + Assert.AreEqual(sys.testattr3.ToString(), "True"); // Compare in .NET - Assert.AreEqual(sys.testattr1, sys.testattr2); + Assert.IsTrue(sys.testattr1.Equals(sys.testattr2)); } } } 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/embed_tests/pyinitialize.cs b/src/embed_tests/pyinitialize.cs index 2f9aae2c7..ea1d8d023 100644 --- a/src/embed_tests/pyinitialize.cs +++ b/src/embed_tests/pyinitialize.cs @@ -74,5 +74,65 @@ public void ReInitialize() } PythonEngine.Shutdown(); } + + [Test] + public void TestScopeIsShutdown() + { + PythonEngine.Initialize(); + var scope = PyScopeManager.Global.Create("test"); + PythonEngine.Shutdown(); + Assert.That(PyScopeManager.Global.Contains("test"), Is.False); + } + + /// + /// Helper for testing the shutdown handlers. + /// + int shutdown_count = 0; + void OnShutdownIncrement() + { + shutdown_count++; + } + void OnShutdownDouble() + { + shutdown_count *= 2; + } + + /// + /// Test the shutdown handlers. + /// + [Test] + public void ShutdownHandlers() + { + // Test we can run one shutdown handler. + shutdown_count = 0; + PythonEngine.Initialize(); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.Shutdown(); + Assert.That(shutdown_count, Is.EqualTo(1)); + + // Test we can run multiple shutdown handlers in the right order. + shutdown_count = 4; + PythonEngine.Initialize(); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.AddShutdownHandler(OnShutdownDouble); + PythonEngine.Shutdown(); + // Correct: 4 * 2 + 1 = 9 + // Wrong: (4 + 1) * 2 = 10 + Assert.That(shutdown_count, Is.EqualTo(9)); + + // Test we can remove shutdown handlers, handling duplicates. + shutdown_count = 4; + PythonEngine.Initialize(); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.AddShutdownHandler(OnShutdownDouble); + PythonEngine.AddShutdownHandler(OnShutdownIncrement); + PythonEngine.AddShutdownHandler(OnShutdownDouble); + PythonEngine.RemoveShutdownHandler(OnShutdownDouble); + PythonEngine.Shutdown(); + // Correct: (4 + 1) * 2 + 1 + 1 = 12 + // Wrong: (4 * 2) + 1 + 1 + 1 = 11 + Assert.That(shutdown_count, Is.EqualTo(12)); + } } } diff --git a/src/embed_tests/pyrunstring.cs b/src/embed_tests/pyrunstring.cs index 81a1b07ca..07875a2a8 100644 --- a/src/embed_tests/pyrunstring.cs +++ b/src/embed_tests/pyrunstring.cs @@ -57,5 +57,20 @@ public void TestExec() object c = locals.GetItem("c").AsManagedObject(typeof(int)); Assert.AreEqual(111, c); } + + [Test] + public void TestExec2() + { + string code = @" +class Test1(): + pass + +class Test2(): + def __init__(self): + Test1() + +Test2()"; + PythonEngine.Exec(code); + } } } 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..a4d1773f7 --- /dev/null +++ b/src/runtime/Python.Runtime.15.csproj @@ -0,0 +1,147 @@ + + + + net40;netstandard2.0 + AnyCPU + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + net45 + Python.Runtime + Python.Runtime + pythonnet + 2.4.1 + true + false + Python for .NET + Copyright (c) 2006-2019 the contributors of the 'Python for .NET' project + Python and CLR (.NET and Mono) cross-platform language interop + pythonnet + https://github.com/pythonnet/pythonnet/blob/master/LICENSE + https://github.com/pythonnet/pythonnet + git + + python interop dynamic dlr Mono pinvoke + https://raw.githubusercontent.com/pythonnet/pythonnet/master/src/console/python-clear.ico + https://pythonnet.github.io/ + 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) + PYTHON37 + $(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);FINALIZER_CHECK;TRACE;DEBUG + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonMonoDefineConstants);FINALIZER_CHECK;TRACE;DEBUG + + + $(DefineConstants);PYTHON2;$(Python2Version);$(PythonWinDefineConstants) + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonWinDefineConstants) + + + $(DefineConstants);PYTHON2;$(Python2Version);$(PythonWinDefineConstants);FINALIZER_CHECK;TRACE;DEBUG + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonWinDefineConstants);FINALIZER_CHECK;TRACE;DEBUG + + + + + + + + + + + + + + + + + + + + + + clr.py + + + + + + + + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + + + + + diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 8580b7f61..ffe1974ca 100644 --- a/src/runtime/Python.Runtime.csproj +++ b/src/runtime/Python.Runtime.csproj @@ -1,166 +1,25 @@ - - - - Debug - AnyCPU - {097B4AC0-74E9-4C58-BCF8-C69746EC8271} - Library - Python.Runtime - Python.Runtime - bin\Python.Runtime.xml - bin\ - v4.0 - - 1591 - ..\..\ - $(SolutionDir)\bin\ - Properties - 6 - true - false - ..\pythonnet.snk - - - x86 - - - x64 - - - PYTHON2;PYTHON27;UCS4 - true - pdbonly - - - PYTHON3;PYTHON36;UCS4 - true - pdbonly - - - true - PYTHON2;PYTHON27;UCS4;TRACE;DEBUG - false - full - - - true - PYTHON3;PYTHON36;UCS4;TRACE;DEBUG - false - full - - - PYTHON2;PYTHON27;UCS2 - true - pdbonly - - - PYTHON3;PYTHON36;UCS2 - true - pdbonly - - - true - PYTHON2;PYTHON27;UCS2;TRACE;DEBUG - false - full - - - true - PYTHON3;PYTHON36;UCS2;TRACE;DEBUG - false - full - - - - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - clr.py - - - - - $(TargetPath) - $(TargetDir)$(TargetName).pdb - - - - - - + + + + netstandard2.0 + + + + TRACE;DEBUG;PYTHON3;PYTHON37;UCS2 + true + + + + + + + + + + + + + + + + diff --git a/src/runtime/Util.cs b/src/runtime/Util.cs new file mode 100644 index 000000000..dc5f78608 --- /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/arrayobject.cs b/src/runtime/arrayobject.cs index a10688749..c37295704 100644 --- a/src/runtime/arrayobject.cs +++ b/src/runtime/arrayobject.cs @@ -93,7 +93,7 @@ public static IntPtr mp_subscript(IntPtr ob, IntPtr idx) return IntPtr.Zero; } - int count = Runtime.PyTuple_Size(idx); + var count = Runtime.PyTuple_Size(idx); var args = new int[count]; @@ -186,7 +186,7 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) return -1; } - int count = Runtime.PyTuple_Size(idx); + var count = Runtime.PyTuple_Size(idx); var args = new int[count]; for (var i = 0; i < count; i++) diff --git a/src/runtime/assemblymanager.cs b/src/runtime/assemblymanager.cs index 06a4449a2..3085bb639 100644 --- a/src/runtime/assemblymanager.cs +++ b/src/runtime/assemblymanager.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection; using System.Threading; @@ -17,16 +18,24 @@ internal class AssemblyManager { // modified from event handlers below, potentially triggered from different .NET threads // therefore this should be a ConcurrentDictionary - private static ConcurrentDictionary> namespaces; + // + // WARNING: Dangerous if cross-app domain usage is ever supported + // Reusing the dictionary with assemblies accross multiple initializations is problematic. + // Loading happens from CurrentDomain (see line 53). And if the first call is from AppDomain that is later unloaded, + // than it can end up referring to assemblies that are already unloaded (default behavior after unload appDomain - + // unless LoaderOptimization.MultiDomain is used); + // So for multidomain support it is better to have the dict. recreated for each app-domain initialization + private static ConcurrentDictionary> namespaces = + new ConcurrentDictionary>(); //private static Dictionary> generics; private static AssemblyLoadEventHandler lhandler; private static ResolveEventHandler rhandler; // updated only under GIL? - private static Dictionary probed; + private static Dictionary probed = new Dictionary(32); // modified from event handlers below, potentially triggered from different .NET threads - private static AssemblyList assemblies; + private static ConcurrentQueue assemblies; internal static List pypath; private AssemblyManager() @@ -40,10 +49,7 @@ private AssemblyManager() /// internal static void Initialize() { - namespaces = new ConcurrentDictionary>(); - probed = new Dictionary(32); - //generics = new Dictionary>(); - assemblies = new AssemblyList(16); + assemblies = new ConcurrentQueue(); pypath = new List(16); AppDomain domain = AppDomain.CurrentDomain; @@ -60,7 +66,7 @@ internal static void Initialize() try { ScanAssembly(a); - assemblies.Add(a); + assemblies.Enqueue(a); } catch (Exception ex) { @@ -91,7 +97,7 @@ internal static void Shutdown() private static void AssemblyLoadHandler(object ob, AssemblyLoadEventArgs args) { Assembly assembly = args.LoadedAssembly; - assemblies.Add(assembly); + assemblies.Enqueue(assembly); ScanAssembly(assembly); } @@ -132,7 +138,7 @@ private static Assembly ResolveHandler(object ob, ResolveEventArgs args) internal static void UpdatePath() { IntPtr list = Runtime.PySys_GetObject("path"); - int count = Runtime.PyList_Size(list); + var count = Runtime.PyList_Size(list); if (count != pypath.Count) { pypath.Clear(); @@ -343,9 +349,7 @@ internal static void ScanAssembly(Assembly assembly) // A couple of things we want to do here: first, we want to // gather a list of all of the namespaces contributed to by // the assembly. - - Type[] types = assembly.GetTypes(); - foreach (Type t in types) + foreach (Type t in GetTypes(assembly)) { string ns = t.Namespace ?? ""; if (!namespaces.ContainsKey(ns)) @@ -419,10 +423,9 @@ public static List GetNames(string nsname) { foreach (Assembly a in namespaces[nsname].Keys) { - Type[] types = a.GetTypes(); - foreach (Type t in types) + foreach (Type t in GetTypes(a)) { - if ((t.Namespace ?? "") == nsname) + if ((t.Namespace ?? "") == nsname && !t.IsNested) { names.Add(t.Name); } @@ -462,102 +465,31 @@ public static Type LookupType(string qname) return null; } - /// - /// Wrapper around List<Assembly> for thread safe access - /// - private class AssemblyList : IEnumerable + internal static Type[] GetTypes(Assembly a) { - private readonly List _list; - private readonly ReaderWriterLockSlim _lock; - - public AssemblyList(int capacity) - { - _list = new List(capacity); - _lock = new ReaderWriterLockSlim(); - } - - public int Count - { - get - { - _lock.EnterReadLock(); - try - { - return _list.Count; - } - finally - { - _lock.ExitReadLock(); - } - } - } - - public void Add(Assembly assembly) + if (a.IsDynamic) { - _lock.EnterWriteLock(); try { - _list.Add(assembly); + return a.GetTypes(); } - finally + catch (ReflectionTypeLoadException exc) { - _lock.ExitWriteLock(); + // Return all types that were successfully loaded + return exc.Types.Where(x => x != null).ToArray(); } } - - public IEnumerator GetEnumerator() - { - return ((IEnumerable)this).GetEnumerator(); - } - - /// - /// Enumerator wrapping around 's enumerator. - /// Acquires and releases a read lock on during enumeration - /// - private class Enumerator : IEnumerator + else { - private readonly AssemblyList _assemblyList; - - private readonly IEnumerator _listEnumerator; - - public Enumerator(AssemblyList assemblyList) - { - _assemblyList = assemblyList; - _assemblyList._lock.EnterReadLock(); - _listEnumerator = _assemblyList._list.GetEnumerator(); - } - - public void Dispose() - { - _listEnumerator.Dispose(); - _assemblyList._lock.ExitReadLock(); - } - - public bool MoveNext() - { - return _listEnumerator.MoveNext(); - } - - public void Reset() - { - _listEnumerator.Reset(); - } - - public Assembly Current + try { - get { return _listEnumerator.Current; } + return a.GetExportedTypes(); } - - object IEnumerator.Current + catch (FileNotFoundException) { - get { return Current; } + return new Type[0]; } } - - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(this); - } } } -} +} \ No newline at end of file diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs index 4dd3b5364..5846fa82a 100644 --- a/src/runtime/classbase.cs +++ b/src/runtime/classbase.cs @@ -247,24 +247,6 @@ public static IntPtr tp_str(IntPtr ob) } - /// - /// Default implementations for required Python GC support. - /// - public static int tp_traverse(IntPtr ob, IntPtr func, IntPtr args) - { - return 0; - } - - public static int tp_clear(IntPtr ob) - { - return 0; - } - - public static int tp_is_gc(IntPtr type) - { - return 1; - } - /// /// Standard dealloc implementation for instances of reflected types. /// diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs index c180f9acc..ec3734ea5 100644 --- a/src/runtime/classderived.cs +++ b/src/runtime/classderived.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; +using System.Resources; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -32,6 +33,12 @@ static ClassDerivedObject() moduleBuilders = new Dictionary, ModuleBuilder>(); } + public static void Reset() + { + assemblyBuilders = new Dictionary(); + moduleBuilders = new Dictionary, ModuleBuilder>(); + } + internal ClassDerivedObject(Type tp) : base(tp) { } @@ -808,7 +815,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 +827,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/classmanager.cs b/src/runtime/classmanager.cs index 6a9d40ebd..0b084a49d 100644 --- a/src/runtime/classmanager.cs +++ b/src/runtime/classmanager.cs @@ -34,6 +34,11 @@ static ClassManager() dtype = typeof(MulticastDelegate); } + public static void Reset() + { + cache = new Dictionary(128); + } + /// /// Return the ClassBase-derived instance that implements a particular /// reflected managed type, creating it if it doesn't yet exist. diff --git a/src/runtime/classobject.cs b/src/runtime/classobject.cs index 46257c73f..83d761fd0 100644 --- a/src/runtime/classobject.cs +++ b/src/runtime/classobject.cs @@ -230,10 +230,10 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) } // Get the args passed in. - int i = Runtime.PyTuple_Size(args); + var i = Runtime.PyTuple_Size(args); IntPtr defaultArgs = cls.indexer.GetDefaultArgs(args); - int numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs); - int temp = i + numOfDefaultArgs; + var numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs); + var temp = i + numOfDefaultArgs; IntPtr real = Runtime.PyTuple_New(temp + 1); for (var n = 0; n < i; n++) { diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs index 472e5dcbb..502677655 100644 --- a/src/runtime/clrobject.cs +++ b/src/runtime/clrobject.cs @@ -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/constructorbinding.cs b/src/runtime/constructorbinding.cs index 4839f9913..3908628b9 100644 --- a/src/runtime/constructorbinding.cs +++ b/src/runtime/constructorbinding.cs @@ -75,7 +75,7 @@ public static IntPtr tp_descr_get(IntPtr op, IntPtr instance, IntPtr owner) return Exceptions.RaiseTypeError("How in the world could that happen!"); } }*/ - Runtime.XIncref(self.pyHandle); // Decref'd by the interpreter. + Runtime.XIncref(self.pyHandle); return self.pyHandle; } @@ -105,8 +105,6 @@ public static IntPtr mp_subscript(IntPtr op, IntPtr key) } var boundCtor = new BoundContructor(self.type, self.pyTypeHndl, self.ctorBinder, ci); - /* Since nothing is cached, do we need the increment??? - Runtime.XIncref(boundCtor.pyHandle); // Decref'd by the interpreter??? */ return boundCtor.pyHandle; } diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index b66150a29..615c81cee 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -1,9 +1,11 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Security; +using System.ComponentModel; namespace Python.Runtime { @@ -11,7 +13,7 @@ namespace Python.Runtime /// Performs data conversions between managed types and Python types. /// [SuppressUnmanagedCodeSecurity] - internal class Converter + public class Converter { private Converter() { @@ -133,12 +135,36 @@ internal static IntPtr ToPython(object value, Type type) return result; } + if (value is IList && !(value is INotifyPropertyChanged) && 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; if (null != pyderived) { + #if NETSTANDARD return ClassDerivedObject.ToPython(pyderived); + #else + // if object is remote don't do this + if (!System.Runtime.Remoting.RemotingServices.IsTransparentProxy(pyderived)) + { + return ClassDerivedObject.ToPython(pyderived); + } + #endif } // hmm - from Python, we almost never care what the declared @@ -247,7 +273,7 @@ internal static IntPtr ToPythonImplicit(object value) /// Return a managed object for the given Python object, taking funny /// byref types into account. /// - internal static bool ToManaged(IntPtr value, Type type, + public static bool ToManaged(IntPtr value, Type type, out object result, bool setError) { if (type.IsByRef) @@ -301,6 +327,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 +779,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 +798,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; } @@ -805,7 +843,7 @@ private static void SetConversionError(IntPtr value, Type target) private static bool ToArray(IntPtr value, Type obType, out object result, bool setError) { Type elementType = obType.GetElementType(); - int size = Runtime.PySequence_Size(value); + var size = Runtime.PySequence_Size(value); result = null; if (size < 0) diff --git a/src/runtime/debughelper.cs b/src/runtime/debughelper.cs index 2a91a74b4..3fe9ee5bb 100644 --- a/src/runtime/debughelper.cs +++ b/src/runtime/debughelper.cs @@ -116,10 +116,10 @@ internal static void debug(string msg) Console.WriteLine(" {0}", msg); } - /// + /// /// Helper function to inspect/compare managed to native conversions. - /// Especially useful when debugging CustomMarshaler. - /// + /// Especially useful when debugging CustomMarshaler. + /// /// [Conditional("DEBUG")] public static void PrintHexBytes(byte[] bytes) diff --git a/src/runtime/delegatemanager.cs b/src/runtime/delegatemanager.cs index df5eec427..bd8f1ee4c 100644 --- a/src/runtime/delegatemanager.cs +++ b/src/runtime/delegatemanager.cs @@ -181,10 +181,12 @@ A possible alternate strategy would be to create custom subclasses too "special" for this to work. It would be more work, so for now the 80/20 rule applies :) */ - public class Dispatcher + public class Dispatcher : IPyDisposable { public IntPtr target; public Type dtype; + private bool _disposed = false; + private bool _finalized = false; public Dispatcher(IntPtr target, Type dtype) { @@ -195,14 +197,25 @@ public Dispatcher(IntPtr target, Type dtype) ~Dispatcher() { - // Note: the managed GC thread can run and try to free one of - // these *after* the Python runtime has been finalized! - if (Runtime.Py_IsInitialized() > 0) + if (_finalized || _disposed) { - IntPtr gs = PythonEngine.AcquireLock(); - Runtime.XDecref(target); - PythonEngine.ReleaseLock(gs); + return; } + _finalized = true; + Finalizer.Instance.AddFinalizedObject(this); + } + + public void Dispose() + { + if (_disposed) + { + return; + } + _disposed = true; + Runtime.XDecref(target); + target = IntPtr.Zero; + dtype = null; + GC.SuppressFinalize(this); } public object Dispatch(ArrayList args) @@ -263,6 +276,11 @@ public object TrueDispatch(ArrayList args) Runtime.XDecref(op); return result; } + + public IntPtr[] GetTrackedHandles() + { + return new IntPtr[] { target }; + } } diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index 9023cfcfa..8bed0abfd 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -256,7 +256,10 @@ public static void SetError(Exception e) var pe = e as PythonException; if (pe != null) { - Runtime.PyErr_SetObject(pe.PyType, pe.PyValue); + Runtime.XIncref(pe.PyType); + Runtime.XIncref(pe.PyValue); + Runtime.XIncref(pe.PyTB); + Runtime.PyErr_Restore(pe.PyType, pe.PyValue, pe.PyTB); return; } @@ -276,7 +279,7 @@ public static void SetError(Exception e) /// public static bool ErrorOccurred() { - return Runtime.PyErr_Occurred() != 0; + return Runtime.PyErr_Occurred() != IntPtr.Zero; } /// diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs index 9569b0485..693a46f42 100644 --- a/src/runtime/extensiontype.cs +++ b/src/runtime/extensiontype.cs @@ -81,27 +81,6 @@ public static int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val) } - /// - /// Required Python GC support. - /// - public static int tp_traverse(IntPtr ob, IntPtr func, IntPtr args) - { - return 0; - } - - - public static int tp_clear(IntPtr ob) - { - return 0; - } - - - public static int tp_is_gc(IntPtr type) - { - return 1; - } - - /// /// Default dealloc implementation. /// diff --git a/src/runtime/finalizer.cs b/src/runtime/finalizer.cs new file mode 100644 index 000000000..dd5c0b4dd --- /dev/null +++ b/src/runtime/finalizer.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Python.Runtime +{ + public class Finalizer + { + public class CollectArgs : EventArgs + { + public int ObjectCount { get; set; } + } + + public class ErrorArgs : EventArgs + { + public Exception Error { get; set; } + } + + public static readonly Finalizer Instance = new Finalizer(); + + public event EventHandler CollectOnce; + public event EventHandler ErrorHandler; + + public int Threshold { get; set; } + public bool Enable { get; set; } + + private ConcurrentQueue _objQueue = new ConcurrentQueue(); + private bool _pending = false; + private readonly object _collectingLock = new object(); + private Task _finalizerTask; + + #region FINALIZER_CHECK + +#if FINALIZER_CHECK + private readonly object _queueLock = new object(); + public bool RefCountValidationEnabled { get; set; } = true; +#else + public readonly bool RefCountValidationEnabled = false; +#endif + // Keep these declarations for compat even no FINALIZER_CHECK + public class IncorrectFinalizeArgs : EventArgs + { + public IntPtr Handle { get; internal set; } + public ICollection ImpactedObjects { get; internal set; } + } + + public class IncorrectRefCountException : Exception + { + public IntPtr PyPtr { get; internal set; } + private string _message; + public override string Message => _message; + + public IncorrectRefCountException(IntPtr ptr) + { + PyPtr = ptr; + IntPtr pyname = Runtime.PyObject_Unicode(PyPtr); + string name = Runtime.GetManagedString(pyname); + Runtime.XDecref(pyname); + _message = $"{name} may has a incorrect ref count"; + } + } + + public delegate bool IncorrectRefCntHandler(object sender, IncorrectFinalizeArgs e); + public event IncorrectRefCntHandler IncorrectRefCntResolver; + public bool ThrowIfUnhandleIncorrectRefCount { get; set; } = true; + + #endregion + + private Finalizer() + { + Enable = true; + Threshold = 200; + } + + public void Collect(bool forceDispose = true) + { + if (Instance._finalizerTask != null + && !Instance._finalizerTask.IsCompleted) + { + var ts = PythonEngine.BeginAllowThreads(); + Instance._finalizerTask.Wait(); + PythonEngine.EndAllowThreads(ts); + } + else if (forceDispose) + { + Instance.DisposeAll(); + } + } + + public List GetCollectedObjects() + { + return _objQueue.Select(T => new WeakReference(T)).ToList(); + } + + internal void AddFinalizedObject(IPyDisposable obj) + { + if (!Enable) + { + return; + } + if (Runtime.Py_IsInitialized() == 0) + { + // XXX: Memory will leak if a PyObject finalized after Python shutdown, + // for avoiding that case, user should call GC.Collect manual before shutdown. + return; + } +#if FINALIZER_CHECK + lock (_queueLock) +#endif + { + _objQueue.Enqueue(obj); + } + GC.ReRegisterForFinalize(obj); + if (!_pending && _objQueue.Count >= Threshold) + { + AddPendingCollect(); + } + } + + internal static void Shutdown() + { + if (Runtime.Py_IsInitialized() == 0) + { + Instance._objQueue = new ConcurrentQueue(); + return; + } + Instance.Collect(forceDispose: true); + } + + private void AddPendingCollect() + { + if(Monitor.TryEnter(_collectingLock)) + { + try + { + if (!_pending) + { + _pending = true; + // should already be complete but just in case + _finalizerTask?.Wait(); + + _finalizerTask = Task.Factory.StartNew(() => + { + using (Py.GIL()) + { + Instance.DisposeAll(); + _pending = false; + } + }); + } + } + finally + { + Monitor.Exit(_collectingLock); + } + } + } + + private void DisposeAll() + { + CollectOnce?.Invoke(this, new CollectArgs() + { + ObjectCount = _objQueue.Count + }); +#if FINALIZER_CHECK + lock (_queueLock) +#endif + { +#if FINALIZER_CHECK + ValidateRefCount(); +#endif + IPyDisposable obj; + while (_objQueue.TryDequeue(out obj)) + { + try + { + obj.Dispose(); + Runtime.CheckExceptionOccurred(); + } + catch (Exception e) + { + // We should not bother the main thread + ErrorHandler?.Invoke(this, new ErrorArgs() + { + Error = e + }); + } + } + } + } + +#if FINALIZER_CHECK + private void ValidateRefCount() + { + if (!RefCountValidationEnabled) + { + return; + } + var counter = new Dictionary(); + var holdRefs = new Dictionary(); + var indexer = new Dictionary>(); + foreach (var obj in _objQueue) + { + IntPtr[] handles = obj.GetTrackedHandles(); + foreach (var handle in handles) + { + if (handle == IntPtr.Zero) + { + continue; + } + if (!counter.ContainsKey(handle)) + { + counter[handle] = 0; + } + counter[handle]++; + if (!holdRefs.ContainsKey(handle)) + { + holdRefs[handle] = Runtime.Refcount(handle); + } + List objs; + if (!indexer.TryGetValue(handle, out objs)) + { + objs = new List(); + indexer.Add(handle, objs); + } + objs.Add(obj); + } + } + foreach (var pair in counter) + { + IntPtr handle = pair.Key; + long cnt = pair.Value; + // Tracked handle's ref count is larger than the object's holds + // it may take an unspecified behaviour if it decref in Dispose + if (cnt > holdRefs[handle]) + { + var args = new IncorrectFinalizeArgs() + { + Handle = handle, + ImpactedObjects = indexer[handle] + }; + bool handled = false; + if (IncorrectRefCntResolver != null) + { + var funcList = IncorrectRefCntResolver.GetInvocationList(); + foreach (IncorrectRefCntHandler func in funcList) + { + if (func(this, args)) + { + handled = true; + break; + } + } + } + if (!handled && ThrowIfUnhandleIncorrectRefCount) + { + throw new IncorrectRefCountException(handle); + } + } + // Make sure no other references for PyObjects after this method + indexer[handle].Clear(); + } + indexer.Clear(); + } +#endif + } +} diff --git a/src/runtime/genericutil.cs b/src/runtime/genericutil.cs index 9772d082f..3a230e12c 100644 --- a/src/runtime/genericutil.cs +++ b/src/runtime/genericutil.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Resources; namespace Python.Runtime { @@ -20,6 +21,11 @@ static GenericUtil() mapping = new Dictionary>>(); } + public static void Reset() + { + mapping = new Dictionary>>(); + } + /// /// Register a generic type that appears in a given namespace. /// diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs index bc9ac5eee..7e4a208f5 100644 --- a/src/runtime/importhook.cs +++ b/src/runtime/importhook.cs @@ -155,7 +155,7 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) // hook is saved as this.py_import. This version handles CLR // import and defers to the normal builtin for everything else. - int num_args = Runtime.PyTuple_Size(args); + var num_args = Runtime.PyTuple_Size(args); if (num_args < 1) { return Exceptions.RaiseTypeError("__import__() takes at least 1 argument (0 given)"); @@ -237,6 +237,11 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) if (res != IntPtr.Zero) { // There was no error. + if (fromlist && IsLoadAll(fromList)) + { + var mod = ManagedType.GetManagedObject(res) as ModuleObject; + mod?.LoadNames(); + } return res; } // There was an error @@ -290,6 +295,11 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) { if (fromlist) { + if (IsLoadAll(fromList)) + { + var mod = ManagedType.GetManagedObject(module) as ModuleObject; + mod?.LoadNames(); + } Runtime.XIncref(module); return module; } @@ -345,20 +355,33 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) } } - ModuleObject mod = fromlist ? tail : head; - - if (fromlist && Runtime.PySequence_Size(fromList) == 1) { - IntPtr fp = Runtime.PySequence_GetItem(fromList, 0); - if (!CLRModule.preload && Runtime.GetManagedString(fp) == "*") + var mod = fromlist ? tail : head; + + if (fromlist && IsLoadAll(fromList)) { mod.LoadNames(); } - Runtime.XDecref(fp); + + Runtime.XIncref(mod.pyHandle); + return mod.pyHandle; } + } - Runtime.XIncref(mod.pyHandle); - return mod.pyHandle; + private static bool IsLoadAll(IntPtr fromList) + { + if (CLRModule.preload) + { + return false; + } + if (Runtime.PySequence_Size(fromList) != 1) + { + return false; + } + IntPtr fp = Runtime.PySequence_GetItem(fromList, 0); + bool res = Runtime.GetManagedString(fp) == "*"; + Runtime.XDecref(fp); + return res; } } } diff --git a/src/runtime/indexer.cs b/src/runtime/indexer.cs index 7b6d90ca8..71f7e7aa1 100644 --- a/src/runtime/indexer.cs +++ b/src/runtime/indexer.cs @@ -56,7 +56,7 @@ internal void SetItem(IntPtr inst, IntPtr args) internal bool NeedsDefaultArgs(IntPtr args) { - int pynargs = Runtime.PyTuple_Size(args); + var pynargs = Runtime.PyTuple_Size(args); MethodBase[] methods = SetterBinder.GetMethods(); if (methods.Length == 0) { @@ -72,7 +72,7 @@ internal bool NeedsDefaultArgs(IntPtr args) return false; } - for (int v = pynargs; v < clrnargs; v++) + for (var v = pynargs; v < clrnargs; v++) { if (pi[v].DefaultValue == DBNull.Value) { @@ -95,7 +95,7 @@ internal IntPtr GetDefaultArgs(IntPtr args) { return Runtime.PyTuple_New(0); } - int pynargs = Runtime.PyTuple_Size(args); + var pynargs = Runtime.PyTuple_Size(args); // Get the default arg tuple MethodBase[] methods = SetterBinder.GetMethods(); diff --git a/src/runtime/interfaceobject.cs b/src/runtime/interfaceobject.cs index ce1bc9eb0..616ced6bd 100644 --- a/src/runtime/interfaceobject.cs +++ b/src/runtime/interfaceobject.cs @@ -36,7 +36,7 @@ static InterfaceObject() public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { var self = (InterfaceObject)GetManagedObject(tp); - int nargs = Runtime.PyTuple_Size(args); + var nargs = Runtime.PyTuple_Size(args); Type type = self.type; object obj; diff --git a/src/runtime/interop33.cs b/src/runtime/interop37.cs similarity index 94% rename from src/runtime/interop33.cs rename to src/runtime/interop37.cs index f684df6c6..d5fc76ad3 100644 --- a/src/runtime/interop33.cs +++ b/src/runtime/interop37.cs @@ -2,7 +2,7 @@ // DO NOT MODIFIY BY HAND. -#if PYTHON33 +#if PYTHON37 using System; using System.Collections; using System.Collections.Specialized; @@ -42,7 +42,7 @@ public static int magic() public static int tp_print = 0; public static int tp_getattr = 0; public static int tp_setattr = 0; - public static int tp_reserved = 0; + public static int tp_as_async = 0; public static int tp_repr = 0; public static int tp_as_number = 0; public static int tp_as_sequence = 0; @@ -81,6 +81,10 @@ public static int magic() public static int tp_weaklist = 0; public static int tp_del = 0; public static int tp_version_tag = 0; + public static int tp_finalize = 0; + public static int am_await = 0; + public static int am_aiter = 0; + public static int am_anext = 0; public static int nb_add = 0; public static int nb_subtract = 0; public static int nb_multiply = 0; @@ -115,6 +119,8 @@ public static int magic() public static int nb_inplace_floor_divide = 0; public static int nb_inplace_true_divide = 0; public static int nb_index = 0; + public static int nb_matrix_multiply = 0; + public static int nb_inplace_matrix_multiply = 0; public static int mp_length = 0; public static int mp_subscript = 0; public static int mp_ass_subscript = 0; diff --git a/src/runtime/iterator.cs b/src/runtime/iterator.cs index c7c60ab19..f9cf10178 100644 --- a/src/runtime/iterator.cs +++ b/src/runtime/iterator.cs @@ -23,9 +23,21 @@ public Iterator(IEnumerator e) public static IntPtr tp_iternext(IntPtr ob) { var self = GetManagedObject(ob) as Iterator; - if (!self.iter.MoveNext()) + try { - Exceptions.SetError(Exceptions.StopIteration, Runtime.PyNone); + if (!self.iter.MoveNext()) + { + Exceptions.SetError(Exceptions.StopIteration, Runtime.PyNone); + return IntPtr.Zero; + } + } + catch (Exception e) + { + if (e.InnerException != null) + { + e = e.InnerException; + } + Exceptions.SetError(e); return IntPtr.Zero; } object item = self.iter.Current; diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs index 9ee8d223b..3191da949 100644 --- a/src/runtime/managedtype.cs +++ b/src/runtime/managedtype.cs @@ -28,12 +28,16 @@ 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 ? Marshal.ReadIntPtr(tp, TypeOffset.magic()) : Marshal.ReadIntPtr(ob, ObjectOffset.magic(ob)); + if (op == IntPtr.Zero) + { + return null; + } var gc = (GCHandle)op; return (ManagedType)gc.Target; } @@ -63,7 +67,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..8853c2d5e 100644 --- a/src/runtime/metatype.cs +++ b/src/runtime/metatype.cs @@ -29,7 +29,7 @@ public static IntPtr Initialize() /// public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { - int len = Runtime.PyTuple_Size(args); + var len = Runtime.PyTuple_Size(args); if (len < 3) { return Exceptions.RaiseTypeError("invalid argument list"); @@ -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/methodbinder.cs b/src/runtime/methodbinder.cs index f0c58f34f..7471d5d7c 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -279,10 +279,8 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth { // loop to find match, return invoker w/ or /wo error MethodBase[] _methods = null; - int pynargs = Runtime.PyTuple_Size(args); - object arg; + var pynargs = (int)Runtime.PyTuple_Size(args); var isGeneric = false; - ArrayList defaultArgList = null; if (info != null) { _methods = new MethodBase[1]; @@ -292,7 +290,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth { _methods = GetMethods(); } - Type clrtype; + // TODO: Clean up foreach (MethodBase mi in _methods) { @@ -301,192 +299,242 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth isGeneric = true; } ParameterInfo[] pi = mi.GetParameters(); - int clrnargs = pi.Length; - var match = false; - int arrayStart = -1; + ArrayList defaultArgList; + bool paramsArray; + + if (!MatchesArgumentCount(pynargs, pi, out paramsArray, out defaultArgList)) { + continue; + } var outs = 0; + var margs = TryConvertArguments(pi, paramsArray, args, pynargs, defaultArgList, + needsResolution: _methods.Length > 1, + outs: out outs); - if (pynargs == clrnargs) + if (margs == null) { - match = true; + continue; } - else if (pynargs < clrnargs) + + object target = null; + if (!mi.IsStatic && inst != IntPtr.Zero) { - match = true; - defaultArgList = new ArrayList(); - for (int v = pynargs; v < clrnargs; v++) + //CLRObject co = (CLRObject)ManagedType.GetManagedObject(inst); + // InvalidCastException: Unable to cast object of type + // 'Python.Runtime.ClassObject' to type 'Python.Runtime.CLRObject' + var co = ManagedType.GetManagedObject(inst) as CLRObject; + + // Sanity check: this ensures a graceful exit if someone does + // something intentionally wrong like call a non-static method + // on the class rather than on an instance of the class. + // XXX maybe better to do this before all the other rigmarole. + if (co == null) { - if (pi[v].DefaultValue == DBNull.Value) - { - match = false; - } - else - { - defaultArgList.Add(pi[v].DefaultValue); - } + return null; } + target = co.inst; } - else if (pynargs > clrnargs && clrnargs > 0 && - Attribute.IsDefined(pi[clrnargs - 1], typeof(ParamArrayAttribute))) + + return new Binding(mi, target, margs, outs); + } + // We weren't able to find a matching method but at least one + // is a generic method and info is null. That happens when a generic + // method was not called using the [] syntax. Let's introspect the + // type of the arguments and use it to construct the correct method. + if (isGeneric && info == null && methodinfo != null) + { + Type[] types = Runtime.PythonArgsToTypeArray(args, true); + MethodInfo mi = MatchParameters(methodinfo, types); + return Bind(inst, args, kw, mi, null); + } + return null; + } + + /// + /// Attempts to convert Python argument tuple into an array of managed objects, + /// that can be passed to a method. + /// + /// Information about expected parameters + /// true, if the last parameter is a params array. + /// A pointer to the Python argument tuple + /// Number of arguments, passed by Python + /// A list of default values for omitted parameters + /// true, if overloading resolution is required + /// Returns number of output parameters + /// An array of .NET arguments, that can be passed to a method. + static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray, + IntPtr args, int pyArgCount, + ArrayList defaultArgList, + bool needsResolution, + out int outs) + { + outs = 0; + var margs = new object[pi.Length]; + int arrayStart = paramsArray ? pi.Length - 1 : -1; + + for (int paramIndex = 0; paramIndex < pi.Length; paramIndex++) + { + if (paramIndex >= pyArgCount) { - // This is a `foo(params object[] bar)` style method - match = true; - arrayStart = clrnargs - 1; + if (defaultArgList != null) + { + margs[paramIndex] = defaultArgList[paramIndex - pyArgCount]; + } + + continue; } - if (match) + var parameter = pi[paramIndex]; + IntPtr op = (arrayStart == paramIndex) + // map remaining Python arguments to a tuple since + // the managed function accepts it - hopefully :] + ? Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount) + : Runtime.PyTuple_GetItem(args, paramIndex); + + bool isOut; + if (!TryConvertArgument(op, parameter.ParameterType, needsResolution, out margs[paramIndex], out isOut)) { - var margs = new object[clrnargs]; + return null; + } - for (var n = 0; n < clrnargs; n++) + if (arrayStart == paramIndex) + { + // TODO: is this a bug? Should this happen even if the conversion fails? + // GetSlice() creates a new reference but GetItem() + // returns only a borrow reference. + Runtime.XDecref(op); + } + + if (parameter.IsOut || isOut) + { + outs++; + } + } + + return margs; + } + + static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResolution, + out object arg, out bool isOut) + { + arg = null; + isOut = false; + var clrtype = TryComputeClrArgumentType(parameterType, op, needsResolution: needsResolution); + if (clrtype == null) + { + return false; + } + + if (!Converter.ToManaged(op, clrtype, out arg, false)) + { + Exceptions.Clear(); + return false; + } + + isOut = clrtype.IsByRef; + return true; + } + + static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool needsResolution) + { + // this logic below handles cases when multiple overloading methods + // are ambiguous, hence comparison between Python and CLR types + // is necessary + Type clrtype = null; + IntPtr pyoptype; + if (needsResolution) + { + // HACK: each overload should be weighted in some way instead + pyoptype = Runtime.PyObject_Type(argument); + Exceptions.Clear(); + if (pyoptype != IntPtr.Zero) + { + clrtype = Converter.GetTypeByAlias(pyoptype); + } + Runtime.XDecref(pyoptype); + } + + if (clrtype != null) + { + var typematch = false; + if ((parameterType != typeof(object)) && (parameterType != clrtype)) + { + IntPtr pytype = Converter.GetPythonTypeByAlias(parameterType); + pyoptype = Runtime.PyObject_Type(argument); + Exceptions.Clear(); + if (pyoptype != IntPtr.Zero) { - IntPtr op; - if (n < pynargs) + if (pytype != pyoptype) { - if (arrayStart == n) - { - // map remaining Python arguments to a tuple since - // the managed function accepts it - hopefully :] - op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs); - } - else - { - op = Runtime.PyTuple_GetItem(args, n); - } - - // this logic below handles cases when multiple overloading methods - // are ambiguous, hence comparison between Python and CLR types - // is necessary - clrtype = null; - IntPtr pyoptype; - if (_methods.Length > 1) - { - pyoptype = IntPtr.Zero; - pyoptype = Runtime.PyObject_Type(op); - Exceptions.Clear(); - if (pyoptype != IntPtr.Zero) - { - clrtype = Converter.GetTypeByAlias(pyoptype); - } - Runtime.XDecref(pyoptype); - } - - - if (clrtype != null) - { - var typematch = false; - if ((pi[n].ParameterType != typeof(object)) && (pi[n].ParameterType != clrtype)) - { - IntPtr pytype = Converter.GetPythonTypeByAlias(pi[n].ParameterType); - pyoptype = Runtime.PyObject_Type(op); - Exceptions.Clear(); - if (pyoptype != IntPtr.Zero) - { - if (pytype != pyoptype) - { - typematch = false; - } - else - { - typematch = true; - clrtype = pi[n].ParameterType; - } - } - if (!typematch) - { - // this takes care of enum values - TypeCode argtypecode = Type.GetTypeCode(pi[n].ParameterType); - TypeCode paramtypecode = Type.GetTypeCode(clrtype); - if (argtypecode == paramtypecode) - { - typematch = true; - clrtype = pi[n].ParameterType; - } - } - Runtime.XDecref(pyoptype); - if (!typematch) - { - margs = null; - break; - } - } - else - { - typematch = true; - clrtype = pi[n].ParameterType; - } - } - else - { - clrtype = pi[n].ParameterType; - } - - if (pi[n].IsOut || clrtype.IsByRef) - { - outs++; - } - - if (!Converter.ToManaged(op, clrtype, out arg, false)) - { - Exceptions.Clear(); - margs = null; - break; - } - if (arrayStart == n) - { - // GetSlice() creates a new reference but GetItem() - // returns only a borrow reference. - Runtime.XDecref(op); - } - margs[n] = arg; + typematch = false; } else { - if (defaultArgList != null) - { - margs[n] = defaultArgList[n - pynargs]; - } + typematch = true; + clrtype = parameterType; } } - - if (margs == null) + if (!typematch) { - continue; - } - - object target = null; - if (!mi.IsStatic && inst != IntPtr.Zero) - { - //CLRObject co = (CLRObject)ManagedType.GetManagedObject(inst); - // InvalidCastException: Unable to cast object of type - // 'Python.Runtime.ClassObject' to type 'Python.Runtime.CLRObject' - var co = ManagedType.GetManagedObject(inst) as CLRObject; - - // Sanity check: this ensures a graceful exit if someone does - // something intentionally wrong like call a non-static method - // on the class rather than on an instance of the class. - // XXX maybe better to do this before all the other rigmarole. - if (co == null) + // this takes care of enum values + TypeCode argtypecode = Type.GetTypeCode(parameterType); + TypeCode paramtypecode = Type.GetTypeCode(clrtype); + if (argtypecode == paramtypecode) { - return null; + typematch = true; + clrtype = parameterType; } - target = co.inst; } - - return new Binding(mi, target, margs, outs); + Runtime.XDecref(pyoptype); + if (!typematch) + { + return null; + } + } + else + { + typematch = true; + clrtype = parameterType; } } - // We weren't able to find a matching method but at least one - // is a generic method and info is null. That happens when a generic - // method was not called using the [] syntax. Let's introspect the - // type of the arguments and use it to construct the correct method. - if (isGeneric && info == null && methodinfo != null) + else { - Type[] types = Runtime.PythonArgsToTypeArray(args, true); - MethodInfo mi = MatchParameters(methodinfo, types); - return Bind(inst, args, kw, mi, null); + clrtype = parameterType; } - return null; + + return clrtype; + } + + static bool MatchesArgumentCount(int argumentCount, ParameterInfo[] parameters, + out bool paramsArray, + out ArrayList defaultArgList) + { + defaultArgList = null; + var match = false; + paramsArray = false; + + if (argumentCount == parameters.Length) + { + match = true; + } else if (argumentCount < parameters.Length) + { + match = true; + defaultArgList = new ArrayList(); + for (var v = argumentCount; v < parameters.Length; v++) { + if (parameters[v].DefaultValue == DBNull.Value) { + match = false; + } else { + defaultArgList.Add(parameters[v].DefaultValue); + } + } + } else if (argumentCount > parameters.Length && parameters.Length > 0 && + Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute))) + { + // This is a `foo(params object[] bar)` style method + match = true; + paramsArray = true; + } + + return match; } internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) @@ -507,7 +555,12 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i if (binding == null) { - Exceptions.SetError(Exceptions.TypeError, "No method matches given arguments"); + var value = "No method matches given arguments"; + if (methodinfo != null && methodinfo.Length > 0) + { + value += $" for {methodinfo[0].Name}"; + } + Exceptions.SetError(Exceptions.TypeError, value); return IntPtr.Zero; } @@ -596,6 +649,19 @@ internal class MethodSorter : IComparer { int IComparer.Compare(object m1, object m2) { + var me1 = (MethodBase)m1; + var me2 = (MethodBase)m2; + if (me1.DeclaringType != me2.DeclaringType) + { + // m2's type derives from m1's type, favor m2 + if (me1.DeclaringType.IsAssignableFrom(me2.DeclaringType)) + return 1; + + // m1's type derives from m2's type, favor m1 + if (me2.DeclaringType.IsAssignableFrom(me1.DeclaringType)) + return -1; + } + int p1 = MethodBinder.GetPrecedence((MethodBase)m1); int p2 = MethodBinder.GetPrecedence((MethodBase)m2); if (p1 < p2) diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs index 07090a92c..f402f91f8 100644 --- a/src/runtime/methodbinding.cs +++ b/src/runtime/methodbinding.cs @@ -56,7 +56,6 @@ public static IntPtr mp_subscript(IntPtr tp, IntPtr idx) } var mb = new MethodBinding(self.m, self.target) { info = mi }; - Runtime.XIncref(mb.pyHandle); return mb.pyHandle; } @@ -85,7 +84,6 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key) case "__overloads__": case "Overloads": var om = new OverloadMapper(self.m, self.target); - Runtime.XIncref(om.pyHandle); return om.pyHandle; default: return Runtime.PyObject_GenericGetAttr(ob, key); @@ -106,7 +104,7 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) { if (self.info.IsGenericMethod) { - int len = Runtime.PyTuple_Size(args); //FIXME: Never used + var len = Runtime.PyTuple_Size(args); //FIXME: Never used Type[] sigTp = Runtime.PythonArgsToTypeArray(args, true); if (sigTp != null) { @@ -131,7 +129,7 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) if (target == IntPtr.Zero && !self.m.IsStatic()) { - int len = Runtime.PyTuple_Size(args); + var len = Runtime.PyTuple_Size(args); if (len < 1) { Exceptions.SetError(Exceptions.TypeError, "not enough arguments"); diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs index 258d77bac..7a45c6c81 100644 --- a/src/runtime/moduleobject.cs +++ b/src/runtime/moduleobject.cs @@ -190,10 +190,17 @@ public void LoadNames() foreach (string name in AssemblyManager.GetNames(_namespace)) { cache.TryGetValue(name, out m); - if (m == null) + if (m != null) { - ManagedType attr = GetAttribute(name, true); + continue; } + IntPtr attr = Runtime.PyDict_GetItemString(dict, name); + // If __dict__ has already set a custom property, skip it. + if (attr != IntPtr.Zero) + { + continue; + } + GetAttribute(name, true); } } @@ -328,6 +335,17 @@ public CLRModule() : base("clr") } } + public static void Reset() + { + hacked = false; + interactive_preload = true; + preload = false; + + // XXX Test performance of new features // + _SuppressDocs = false; + _SuppressOverloads = false; + } + /// /// The initializing of the preload hook has to happen as late as /// possible since sys.ps1 is created after the CLR module is @@ -403,6 +421,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..b5bf25dd7 100644 --- a/src/runtime/nativecall.cs +++ b/src/runtime/nativecall.cs @@ -23,6 +23,30 @@ namespace Python.Runtime /// internal class NativeCall { +#if NETSTANDARD + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void Void_1_Delegate(IntPtr a1); + + [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) + { + var d = (Interop.TernaryFunc)Marshal.GetDelegateForFunctionPointer(fp, typeof(Interop.TernaryFunc)); + return d(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 +156,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 +173,5 @@ public interface INativeCall IntPtr Call_3(IntPtr funcPtr, IntPtr a1, IntPtr a2, IntPtr a3); } +#endif } diff --git a/src/runtime/overload.cs b/src/runtime/overload.cs index 6b48299e8..67868a1b1 100644 --- a/src/runtime/overload.cs +++ b/src/runtime/overload.cs @@ -44,7 +44,6 @@ public static IntPtr mp_subscript(IntPtr tp, IntPtr idx) } var mb = new MethodBinding(self.m, self.target) { info = mi }; - Runtime.XIncref(mb.pyHandle); return mb.pyHandle; } 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/propertyobject.cs b/src/runtime/propertyobject.cs index f2c97f163..893fbe54b 100644 --- a/src/runtime/propertyobject.cs +++ b/src/runtime/propertyobject.cs @@ -13,7 +13,6 @@ internal class PropertyObject : ExtensionType private MethodInfo getter; private MethodInfo setter; - [StrongNameIdentityPermission(SecurityAction.Assert)] public PropertyObject(PropertyInfo md) { getter = md.GetGetMethod(true); diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs index 47f413409..831f427ed 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -1,10 +1,17 @@ using System; using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; using System.Dynamic; using System.Linq.Expressions; namespace Python.Runtime { + public interface IPyDisposable : IDisposable + { + IntPtr[] GetTrackedHandles(); + } + /// /// Represents a generic Python object. The methods of this class are /// generally equivalent to the Python "abstract object API". See @@ -12,10 +19,18 @@ namespace Python.Runtime /// PY3: https://docs.python.org/3/c-api/object.html /// for details. /// - public class PyObject : DynamicObject, IDisposable + public class PyObject : DynamicObject, IEnumerable, IPyDisposable { +#if TRACE_ALLOC + /// + /// Trace stack for PyObject's construction + /// + public StackTrace Traceback { get; private set; } +#endif + protected internal IntPtr obj = IntPtr.Zero; private bool disposed = false; + private bool _finalized = false; /// /// PyObject Constructor @@ -29,6 +44,9 @@ public class PyObject : DynamicObject, IDisposable public PyObject(IntPtr ptr) { obj = ptr; +#if TRACE_ALLOC + Traceback = new StackTrace(1); +#endif } // Protected default constructor to allow subclasses to manage @@ -36,14 +54,26 @@ public PyObject(IntPtr ptr) protected PyObject() { +#if TRACE_ALLOC + Traceback = new StackTrace(1); +#endif } // Ensure that encapsulated Python object is decref'ed appropriately // when the managed wrapper is garbage-collected. - ~PyObject() { - Dispose(); + if (obj == IntPtr.Zero) + { + return; + } + if (_finalized || disposed) + { + return; + } + // Prevent a infinity loop by calling GC.WaitForPendingFinalizers + _finalized = true; + Finalizer.Instance.AddFinalizedObject(this); } @@ -97,6 +127,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; + } + /// /// Dispose Method @@ -130,6 +181,10 @@ public void Dispose() GC.SuppressFinalize(this); } + public IntPtr[] GetTrackedHandles() + { + return new IntPtr[] { obj }; + } /// /// GetPythonType Method @@ -493,9 +548,9 @@ public virtual void DelItem(int index) /// Returns the length for objects that support the Python sequence /// protocol, or 0 if the object does not support the protocol. /// - public virtual int Length() + public virtual long Length() { - int s = Runtime.PyObject_Size(obj); + var s = Runtime.PyObject_Size(obj); if (s < 0) { Runtime.PyErr_Clear(); @@ -781,7 +836,7 @@ public bool IsCallable() /// public bool IsIterable() { - return Runtime.PyIter_Check(obj); + return Runtime.PyObject_IsIterable(obj); } @@ -797,6 +852,10 @@ public bool IsTrue() return Runtime.PyObject_IsTrue(obj) != 0; } + /// + /// Return true if the object is None + /// + public bool IsNone() => CheckNone(this) == null; /// /// Dir Method @@ -884,7 +943,7 @@ public override bool Equals(object o) /// public override int GetHashCode() { - return Runtime.PyObject_Hash(obj).ToInt32(); + return ((ulong)Runtime.PyObject_Hash(obj)).GetHashCode(); } @@ -915,6 +974,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 +1012,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 +1034,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 +1068,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 +1098,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 @@ -1053,10 +1154,10 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg res = Runtime.PyNumber_InPlaceMultiply(this.obj, ((PyObject)arg).obj); break; case ExpressionType.Divide: - res = Runtime.PyNumber_Divide(this.obj, ((PyObject)arg).obj); + res = Runtime.PyNumber_TrueDivide(this.obj, ((PyObject)arg).obj); break; case ExpressionType.DivideAssign: - res = Runtime.PyNumber_InPlaceDivide(this.obj, ((PyObject)arg).obj); + res = Runtime.PyNumber_InPlaceTrueDivide(this.obj, ((PyObject)arg).obj); break; case ExpressionType.And: res = Runtime.PyNumber_And(this.obj, ((PyObject)arg).obj); @@ -1171,5 +1272,20 @@ public override bool TryUnaryOperation(UnaryOperationBinder binder, out object r result = CheckNone(new PyObject(res)); return true; } + + /// + /// Returns the enumeration of all dynamic member names. + /// + /// + /// This method exists for debugging purposes only. + /// + /// A sequence that contains dynamic member names. + public override IEnumerable GetDynamicMemberNames() + { + foreach (PyObject pyObj in Dir()) + { + yield return pyObj.ToString(); + } + } } } diff --git a/src/runtime/pyscope.cs b/src/runtime/pyscope.cs new file mode 100644 index 000000000..4008ce29a --- /dev/null +++ b/src/runtime/pyscope.cs @@ -0,0 +1,669 @@ +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, IPyDisposable + { + 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; + private bool _finalized = false; + + /// + /// 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); + } + + public IntPtr[] GetTrackedHandles() + { + return new IntPtr[] { obj }; + } + + ~PyScope() + { + if (_finalized || _isDisposed) + { + return; + } + _finalized = true; + Finalizer.Instance.AddFinalizedObject(this); + } + } + + public class PyScopeManager + { + public static PyScopeManager Global; + + private Dictionary NamedScopes = new Dictionary(); + + internal static void Reset() + { + Global = new PyScopeManager(); + } + + 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..f3f5995a1 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -140,9 +140,9 @@ public static void Initialize() Initialize(setSysArgv: true); } - public static void Initialize(bool setSysArgv = true) + public static void Initialize(bool setSysArgv = true, bool initSigs = false) { - Initialize(Enumerable.Empty(), setSysArgv: setSysArgv); + Initialize(Enumerable.Empty(), setSysArgv: setSysArgv, initSigs: initSigs); } /// @@ -153,8 +153,9 @@ public static void Initialize(bool setSysArgv = true) /// more than once, though initialization will only happen on the /// first call. It is *not* necessary to hold the Python global /// interpreter lock (GIL) to call this method. + /// initSigs can be set to 1 to do default python signal configuration. This will override the way signals are handled by the application. /// - public static void Initialize(IEnumerable args, bool setSysArgv = true) + public static void Initialize(IEnumerable args, bool setSysArgv = true, bool initSigs = false) { if (!initialized) { @@ -164,10 +165,20 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true) // during an initial "import clr", and the world ends shortly thereafter. // This is probably masking some bad mojo happening somewhere in Runtime.Initialize(). delegateManager = new DelegateManager(); - Runtime.Initialize(); + Runtime.Initialize(initSigs); initialized = true; Exceptions.Clear(); + // Make sure we clean up properly on app domain unload. + AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; + + // Remember to shut down the runtime. + AddShutdownHandler(Runtime.Shutdown); + + // The global scope gets used implicitly quite early on, remember + // to clear it out when we shut down. + AddShutdownHandler(PyScopeManager.Global.Clear); + if (setSysArgv) { Py.SetArgv(args); @@ -194,7 +205,9 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true) Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins); Assembly assembly = Assembly.GetExecutingAssembly(); - using (Stream stream = assembly.GetManifestResourceStream("clr.py")) + // use this to check what the correct path to the resource is: + //string[] names = assembly.GetManifestResourceNames(); + using (Stream stream = assembly.GetManifestResourceStream("Python.Runtime.resources.clr.py")) using (var reader = new StreamReader(stream)) { // add the contents of clr.py to the module @@ -223,6 +236,11 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true) } } + static void OnDomainUnload(object _, EventArgs __) + { + Shutdown(); + } + /// /// A helper to perform initialization from the context of an active /// CPython interpreter process - this bootstraps the managed runtime @@ -294,18 +312,81 @@ public static void Shutdown() { if (initialized) { - Marshal.FreeHGlobal(_pythonHome); - _pythonHome = IntPtr.Zero; - Marshal.FreeHGlobal(_programName); - _programName = IntPtr.Zero; - Marshal.FreeHGlobal(_pythonPath); - _pythonPath = IntPtr.Zero; + PyScopeManager.Global.Clear(); + + // If the shutdown handlers trigger a domain unload, + // don't call shutdown again. + AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload; + + ExecuteShutdownHandlers(); - Runtime.Shutdown(); initialized = false; } } + /// + /// Called when the engine is shut down. + /// + /// Shutdown handlers are run in reverse order they were added, so that + /// resources available when running a shutdown handler are the same as + /// what was available when it was added. + /// + public delegate void ShutdownHandler(); + + static List ShutdownHandlers = new List(); + + /// + /// Add a function to be called when the engine is shut down. + /// + /// Shutdown handlers are executed in the opposite order they were + /// added, so that you can be sure that everything that was initialized + /// when you added the handler is still initialized when you need to shut + /// down. + /// + /// If the same shutdown handler is added several times, it will be run + /// several times. + /// + /// Don't add shutdown handlers while running a shutdown handler. + /// + public static void AddShutdownHandler(ShutdownHandler handler) + { + ShutdownHandlers.Add(handler); + } + + /// + /// Remove a shutdown handler. + /// + /// If the same shutdown handler is added several times, only the last + /// one is removed. + /// + /// Don't remove shutdown handlers while running a shutdown handler. + /// + public static void RemoveShutdownHandler(ShutdownHandler handler) + { + for (int index = ShutdownHandlers.Count - 1; index >= 0; --index) + { + if (ShutdownHandlers[index] == handler) + { + ShutdownHandlers.RemoveAt(index); + break; + } + } + } + + /// + /// Run all the shutdown handlers. + /// + /// They're run in opposite order they were added. + /// + static void ExecuteShutdownHandlers() + { + while(ShutdownHandlers.Count > 0) + { + var handler = ShutdownHandlers[ShutdownHandlers.Count - 1]; + ShutdownHandlers.RemoveAt(ShutdownHandlers.Count - 1); + handler(); + } + } /// /// AcquireLock Method @@ -421,6 +502,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 @@ -488,12 +576,10 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals, borrowedGlobals = false; } } - - var borrowedLocals = true; + if (locals == null) { - locals = Runtime.PyDict_New(); - borrowedLocals = false; + locals = globals; } try @@ -508,10 +594,6 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals, } finally { - if (!borrowedLocals) - { - Runtime.XDecref(locals.Value); - } if (!borrowedGlobals) { Runtime.XDecref(globals.Value); @@ -539,6 +621,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 +726,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..295a63b3d 100644 --- a/src/runtime/pythonexception.cs +++ b/src/runtime/pythonexception.cs @@ -6,22 +6,21 @@ namespace Python.Runtime /// Provides a managed interface to exceptions thrown by the Python /// runtime. /// - public class PythonException : System.Exception + public class PythonException : System.Exception, IPyDisposable { private IntPtr _pyType = IntPtr.Zero; private IntPtr _pyValue = IntPtr.Zero; private IntPtr _pyTB = IntPtr.Zero; private string _tb = ""; private string _message = ""; + private string _pythonTypeName = ""; private bool disposed = false; + private bool _finalized = false; public PythonException() { IntPtr gs = PythonEngine.AcquireLock(); Runtime.PyErr_Fetch(ref _pyType, ref _pyValue, ref _pyTB); - Runtime.XIncref(_pyType); - Runtime.XIncref(_pyValue); - Runtime.XIncref(_pyTB); if (_pyType != IntPtr.Zero && _pyValue != IntPtr.Zero) { string type; @@ -33,6 +32,8 @@ public PythonException() type = pyTypeName.ToString(); } + _pythonTypeName = type; + Runtime.XIncref(_pyValue); using (var pyValue = new PyObject(_pyValue)) { @@ -42,11 +43,13 @@ public PythonException() } if (_pyTB != IntPtr.Zero) { - PyObject tb_module = PythonEngine.ImportModule("traceback"); - Runtime.XIncref(_pyTB); - using (var pyTB = new PyObject(_pyTB)) + using (PyObject tb_module = PythonEngine.ImportModule("traceback")) { - _tb = tb_module.InvokeMethod("format_tb", pyTB).ToString(); + Runtime.XIncref(_pyTB); + using (var pyTB = new PyObject(_pyTB)) + { + _tb = tb_module.InvokeMethod("format_tb", pyTB).ToString(); + } } } PythonEngine.ReleaseLock(gs); @@ -57,7 +60,12 @@ public PythonException() ~PythonException() { - Dispose(); + if (_finalized || disposed) + { + return; + } + _finalized = true; + Finalizer.Instance.AddFinalizedObject(this); } /// @@ -125,9 +133,16 @@ public override string Message /// public override string StackTrace { - get { return _tb; } + get { return _tb + base.StackTrace; } } + /// + /// Python error type name. + /// + public string PythonTypeName + { + get { return _pythonTypeName; } + } /// /// Dispose Method @@ -159,6 +174,11 @@ public void Dispose() } } + public IntPtr[] GetTrackedHandles() + { + return new IntPtr[] { _pyType, _pyValue, _pyTB }; + } + /// /// Matches Method /// diff --git a/src/runtime/resources/clr.py b/src/runtime/resources/clr.py index 91f1f6b15..45265226a 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.1" class clrproperty(object): diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 346ea745f..4229e43bb 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -2,13 +2,36 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; +using System.Threading; +using System.Collections.Generic; namespace Python.Runtime { [SuppressUnmanagedCodeSecurity] - internal static class NativeMethods + public 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 +46,8 @@ public static IntPtr LoadLibrary(string fileName) { return dlopen(fileName, RTLD_NOW | RTLD_SHARED); } +#endif + public static void FreeLibrary(IntPtr handle) { @@ -48,16 +73,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 +105,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 +120,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 +131,36 @@ 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"; -#elif PYTHON33 - public const string pyversion = "3.3"; - public const string pyver = "33"; + internal const string _pyversion = "2.7"; + internal const string _pyver = "27"; #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"; -#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.6"; + internal const string _pyver = "36"; +#elif PYTHON37 + internal const string _pyversion = "3.7"; + internal const string _pyver = "37"; #else -#error You must define one of PYTHON33 to PYTHON37 or PYTHON27 +#error You must define one of PYTHON34 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,35 +174,114 @@ 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; + public static object IsFinalizingLock = new object(); + public static bool IsFinalizing; - internal static bool Is32Bit = IntPtr.Size == 4; - internal static bool IsPython2 = pyversionnumber < 30; - internal static bool IsPython3 = pyversionnumber >= 30; + public static bool Is32Bit = IntPtr.Size == 4; + + // .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + public static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; + + /// + /// Operating system type as reported by Python. + /// + public enum OperatingSystemType + { + Windows, + Darwin, + Linux, + Other + } + + static readonly Dictionary OperatingSystemTypeMapping = new Dictionary() + { + { "Windows", OperatingSystemType.Windows }, + { "Darwin", OperatingSystemType.Darwin }, + { "Linux", OperatingSystemType.Linux }, + }; + + /// + /// Gets the operating system as reported by python's platform.system(). + /// + public static OperatingSystemType OperatingSystem { get; private set; } + + /// + /// Gets the operating system as reported by python's platform.system(). + /// + public static string OperatingSystemName { get; private set; } + + public enum MachineType + { + i386, + x86_64, + armv7l, + armv8, + Other + }; + + /// + /// Map lower-case version of the python machine name to the processor + /// type. There are aliases, e.g. x86_64 and amd64 are two names for + /// the same thing. Make sure to lower-case the search string, because + /// capitalization can differ. + /// + static readonly Dictionary MachineTypeMapping = new Dictionary() + { + ["i386"] = MachineType.i386, + ["i686"] = MachineType.i386, + ["x86"] = MachineType.i386, + ["x86_64"] = MachineType.x86_64, + ["amd64"] = MachineType.x86_64, + ["x64"] = MachineType.x86_64, + ["em64t"] = MachineType.x86_64, + ["armv7l"] = MachineType.armv7l, + ["armv8"] = MachineType.armv8, + }; + + /// + /// Gets the machine architecture as reported by python's platform.machine(). + /// + public static MachineType Machine { get; private set; }/* set in Initialize using python's platform.machine */ + + /// + /// Gets the machine architecture as reported by python's platform.machine(). + /// + public static string MachineName { get; private set; } + + public static bool IsPython2 = pyversionnumber < 30; + public static bool IsPython3 = pyversionnumber >= 30; + + public static int MainManagedThreadId { get; private set; } /// /// Encoding to use to convert Unicode to/from Managed to Native /// - internal static readonly Encoding PyEncoding = UCS == 2 ? Encoding.Unicode : Encoding.UTF32; + public static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32; /// /// Initialize the runtime... /// - internal static void Initialize() + public static void Initialize(bool initSigs = false) { if (Py_IsInitialized() == 0) { - Py_Initialize(); + Py_InitializeEx(initSigs ? 1 : 0); + MainManagedThreadId = Thread.CurrentThread.ManagedThreadId; } if (PyEval_ThreadsInitialized() == 0) @@ -175,6 +289,15 @@ internal static void Initialize() PyEval_InitThreads(); } + IsFinalizing = false; + + CLRModule.Reset(); + GenericUtil.Reset(); + PyScopeManager.Reset(); + ClassManager.Reset(); + ClassDerivedObject.Reset(); + TypeManager.Reset(); + IntPtr op; IntPtr dict; if (IsPython3) @@ -274,20 +397,24 @@ 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 data about the platform we're running on. We need + // this for the type manager and potentially other details. Must + // happen after caching the python types, above. + InitializePlatformData(); // Initialize modules that depend on the runtime class. AssemblyManager.Initialize(); @@ -305,16 +432,64 @@ internal static void Initialize() AssemblyManager.UpdatePath(); } - internal static void Shutdown() + /// + /// Initializes the data about platforms. + /// + /// This must be the last step when initializing the runtime: + /// GetManagedString needs to have the cached values for types. + /// But it must run before initializing anything outside the runtime + /// because those rely on the platform data. + /// + private static void InitializePlatformData() + { + IntPtr op; + IntPtr fn; + IntPtr platformModule = PyImport_ImportModule("platform"); + IntPtr emptyTuple = PyTuple_New(0); + + fn = PyObject_GetAttrString(platformModule, "system"); + op = PyObject_Call(fn, emptyTuple, IntPtr.Zero); + OperatingSystemName = GetManagedString(op); + XDecref(op); + XDecref(fn); + + fn = PyObject_GetAttrString(platformModule, "machine"); + op = PyObject_Call(fn, emptyTuple, IntPtr.Zero); + MachineName = GetManagedString(op); + XDecref(op); + XDecref(fn); + + XDecref(emptyTuple); + XDecref(platformModule); + + // Now convert the strings into enum values so we can do switch + // statements rather than constant parsing. + OperatingSystemType OSType; + if (!OperatingSystemTypeMapping.TryGetValue(OperatingSystemName, out OSType)) + { + OSType = OperatingSystemType.Other; + } + OperatingSystem = OSType; + + MachineType MType; + if (!MachineTypeMapping.TryGetValue(MachineName.ToLower(), out MType)) + { + MType = MachineType.Other; + } + Machine = MType; + } + + public static void Shutdown() { AssemblyManager.Shutdown(); Exceptions.Shutdown(); ImportHook.Shutdown(); + Finalizer.Shutdown(); Py_Finalize(); } // called *without* the GIL acquired by clr._AtExit - internal static int AtExit() + public static int AtExit() { lock (IsFinalizingLock) { @@ -323,36 +498,36 @@ internal static int AtExit() return 0; } - internal static IntPtr Py_single_input = (IntPtr)256; - internal static IntPtr Py_file_input = (IntPtr)257; - internal static IntPtr Py_eval_input = (IntPtr)258; - - internal static IntPtr PyBaseObjectType; - internal static IntPtr PyModuleType; - internal static IntPtr PyClassType; - internal static IntPtr PyInstanceType; - internal static IntPtr PyCLRMetaType; - internal static IntPtr PyMethodType; - internal static IntPtr PyWrapperDescriptorType; - - internal static IntPtr PyUnicodeType; - internal static IntPtr PyStringType; - internal static IntPtr PyTupleType; - internal static IntPtr PyListType; - internal static IntPtr PyDictType; - internal static IntPtr PyIntType; - internal static IntPtr PyLongType; - internal static IntPtr PyFloatType; - internal static IntPtr PyBoolType; - internal static IntPtr PyNoneType; - internal static IntPtr PyTypeType; + public static IntPtr Py_single_input = (IntPtr)256; + public static IntPtr Py_file_input = (IntPtr)257; + public static IntPtr Py_eval_input = (IntPtr)258; + + public static IntPtr PyBaseObjectType; + public static IntPtr PyModuleType; + public static IntPtr PyClassType; + public static IntPtr PyInstanceType; + public static IntPtr PyCLRMetaType; + public static IntPtr PyMethodType; + public static IntPtr PyWrapperDescriptorType; + + public static IntPtr PyUnicodeType; + public static IntPtr PyStringType; + public static IntPtr PyTupleType; + public static IntPtr PyListType; + public static IntPtr PyDictType; + public static IntPtr PyIntType; + public static IntPtr PyLongType; + public static IntPtr PyFloatType; + public static IntPtr PyBoolType; + public static IntPtr PyNoneType; + public static IntPtr PyTypeType; #if PYTHON3 - internal static IntPtr PyBytesType; - internal static IntPtr _PyObject_NextNotImplemented; + public static IntPtr PyBytesType; #endif + public static IntPtr _PyObject_NextNotImplemented; - internal static IntPtr PyNotImplemented; + public static IntPtr PyNotImplemented; internal const int Py_LT = 0; internal const int Py_LE = 1; internal const int Py_EQ = 2; @@ -360,10 +535,23 @@ internal static int AtExit() internal const int Py_GT = 4; internal const int Py_GE = 5; - internal static IntPtr PyTrue; - internal static IntPtr PyFalse; - internal static IntPtr PyNone; - internal static IntPtr Error; + public static IntPtr PyTrue; + public static IntPtr PyFalse; + public static IntPtr PyNone; + public static IntPtr Error; + + public static PyObject _none; + public static PyObject GetPyNone() + { + if (_none == null) + { + var result = Runtime.PyNone; + Runtime.XIncref(result); + _none= new PyObject(result); + } + + return _none; + } /// /// Check if any Python Exceptions occurred. @@ -372,40 +560,17 @@ internal static int AtExit() /// /// Can be used instead of `obj == IntPtr.Zero` for example. /// - internal static void CheckExceptionOccurred() + public static void CheckExceptionOccurred() { - if (PyErr_Occurred() != 0) + if (PyErr_Occurred() != IntPtr.Zero) { throw new PythonException(); } } - internal static IntPtr GetBoundArgTuple(IntPtr obj, IntPtr args) + public static IntPtr ExtendTuple(IntPtr t, params 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); + var size = PyTuple_Size(t); int add = args.Length; IntPtr item; @@ -427,12 +592,12 @@ internal static IntPtr ExtendTuple(IntPtr t, params IntPtr[] args) return items; } - internal static Type[] PythonArgsToTypeArray(IntPtr arg) + public static Type[] PythonArgsToTypeArray(IntPtr arg) { return PythonArgsToTypeArray(arg, false); } - internal static Type[] PythonArgsToTypeArray(IntPtr arg, bool mangleObjects) + public static Type[] PythonArgsToTypeArray(IntPtr arg, bool mangleObjects) { // Given a PyObject * that is either a single type object or a // tuple of (managed or unmanaged) type objects, return a Type[] @@ -448,7 +613,7 @@ internal static Type[] PythonArgsToTypeArray(IntPtr arg, bool mangleObjects) free = true; } - int n = PyTuple_Size(args); + var n = PyTuple_Size(args); var types = new Type[n]; Type t = null; @@ -497,9 +662,9 @@ internal static Type[] PythonArgsToTypeArray(IntPtr arg, bool mangleObjects) /// some optimization to avoid managed <--> unmanaged transitions /// (mostly for heavily used methods). /// - internal static unsafe void XIncref(IntPtr op) + public static unsafe void XIncref(IntPtr op) { -#if PYTHON_WITH_PYDEBUG +#if PYTHON_WITH_PYDEBUG || NETSTANDARD Py_IncRef(op); return; #else @@ -518,9 +683,9 @@ internal static unsafe void XIncref(IntPtr op) #endif } - internal static unsafe void XDecref(IntPtr op) + public static unsafe void XDecref(IntPtr op) { -#if PYTHON_WITH_PYDEBUG +#if PYTHON_WITH_PYDEBUG || NETSTANDARD Py_DecRef(op); return; #else @@ -555,7 +720,7 @@ internal static unsafe void XDecref(IntPtr op) #endif } - internal static unsafe long Refcount(IntPtr op) + public static unsafe long Refcount(IntPtr op) { var p = (void*)op; if ((void*)0 == p) @@ -570,166 +735,172 @@ internal static unsafe long Refcount(IntPtr op) /// Limit this function usage for Testing and Py_Debug builds /// /// PyObject Ptr - [DllImport(PythonDll)] - internal static extern void Py_IncRef(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void Py_IncRef(IntPtr ob); /// /// Export of Macro Py_XDecRef. Use XDecref instead. /// Limit this function usage for Testing and Py_Debug builds /// /// PyObject Ptr - [DllImport(PythonDll)] - internal static extern void Py_DecRef(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void Py_DecRef(IntPtr ob); - [DllImport(PythonDll)] - internal static extern void Py_Initialize(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void Py_Initialize(); - [DllImport(PythonDll)] - internal static extern int Py_IsInitialized(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void Py_InitializeEx(int initsigs); - [DllImport(PythonDll)] - internal static extern void Py_Finalize(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int Py_IsInitialized(); - [DllImport(PythonDll)] - internal static extern IntPtr Py_NewInterpreter(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void Py_Finalize(); - [DllImport(PythonDll)] - internal static extern void Py_EndInterpreter(IntPtr threadState); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_NewInterpreter(); - [DllImport(PythonDll)] - internal static extern IntPtr PyThreadState_New(IntPtr istate); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void Py_EndInterpreter(IntPtr threadState); - [DllImport(PythonDll)] - internal static extern IntPtr PyThreadState_Get(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyThreadState_New(IntPtr istate); - [DllImport(PythonDll)] - internal static extern IntPtr PyThread_get_key_value(IntPtr key); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyThreadState_Get(); - [DllImport(PythonDll)] - internal static extern int PyThread_get_thread_ident(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyThread_get_key_value(IntPtr key); - [DllImport(PythonDll)] - internal static extern int PyThread_set_key_value(IntPtr key, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyThread_get_thread_ident(); - [DllImport(PythonDll)] - internal static extern IntPtr PyThreadState_Swap(IntPtr key); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyThread_set_key_value(IntPtr key, IntPtr value); - [DllImport(PythonDll)] - internal static extern IntPtr PyGILState_Ensure(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyThreadState_Swap(IntPtr key); - [DllImport(PythonDll)] - internal static extern void PyGILState_Release(IntPtr gs); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyGILState_Ensure(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyGILState_Release(IntPtr gs); - [DllImport(PythonDll)] - internal static extern IntPtr PyGILState_GetThisThreadState(); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public 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)] - internal static extern void PyEval_InitThreads(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyEval_InitThreads(); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyEval_ThreadsInitialized(); - [DllImport(PythonDll)] - internal static extern int PyEval_ThreadsInitialized(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyEval_AcquireLock(); - [DllImport(PythonDll)] - internal static extern void PyEval_AcquireLock(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyEval_ReleaseLock(); - [DllImport(PythonDll)] - internal static extern void PyEval_ReleaseLock(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyEval_AcquireThread(IntPtr tstate); - [DllImport(PythonDll)] - internal static extern void PyEval_AcquireThread(IntPtr tstate); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyEval_ReleaseThread(IntPtr tstate); - [DllImport(PythonDll)] - internal static extern void PyEval_ReleaseThread(IntPtr tstate); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyEval_SaveThread(); - [DllImport(PythonDll)] - internal static extern IntPtr PyEval_SaveThread(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyEval_RestoreThread(IntPtr tstate); - [DllImport(PythonDll)] - internal static extern void PyEval_RestoreThread(IntPtr tstate); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyEval_GetBuiltins(); - [DllImport(PythonDll)] - internal static extern IntPtr PyEval_GetBuiltins(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyEval_GetGlobals(); - [DllImport(PythonDll)] - internal static extern IntPtr PyEval_GetGlobals(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyEval_GetLocals(); - [DllImport(PythonDll)] - internal static extern IntPtr PyEval_GetLocals(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_GetProgramName(); - [DllImport(PythonDll)] - internal static extern IntPtr Py_GetProgramName(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void Py_SetProgramName(IntPtr name); - [DllImport(PythonDll)] - internal static extern void Py_SetProgramName(IntPtr name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_GetPythonHome(); - [DllImport(PythonDll)] - internal static extern IntPtr Py_GetPythonHome(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void Py_SetPythonHome(IntPtr home); - [DllImport(PythonDll)] - internal static extern void Py_SetPythonHome(IntPtr home); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_GetPath(); - [DllImport(PythonDll)] - internal static extern IntPtr Py_GetPath(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void Py_SetPath(IntPtr home); - [DllImport(PythonDll)] - internal static extern void Py_SetPath(IntPtr home); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_GetVersion(); - [DllImport(PythonDll)] - internal static extern IntPtr Py_GetVersion(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_GetPlatform(); - [DllImport(PythonDll)] - internal static extern IntPtr Py_GetPlatform(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_GetCopyright(); - [DllImport(PythonDll)] - internal static extern IntPtr Py_GetCopyright(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_GetCompiler(); - [DllImport(PythonDll)] - internal static extern IntPtr Py_GetCompiler(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_GetBuildInfo(); - [DllImport(PythonDll)] - internal static extern IntPtr Py_GetBuildInfo(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyRun_SimpleString(string code); - [DllImport(PythonDll)] - internal static extern int PyRun_SimpleString(string code); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals); - [DllImport(PythonDll)] - internal static extern IntPtr PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals); - [DllImport(PythonDll)] - internal static extern IntPtr Py_CompileString(string code, string file, IntPtr tok); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Py_CompileString(string code, string file, IntPtr tok); - [DllImport(PythonDll)] - internal static extern IntPtr PyImport_ExecCodeModule(string name, IntPtr code); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyImport_ExecCodeModule(string name, IntPtr code); - [DllImport(PythonDll)] - internal static extern IntPtr PyCFunction_NewEx(IntPtr ml, IntPtr self, IntPtr mod); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyCFunction_NewEx(IntPtr ml, IntPtr self, IntPtr mod); - [DllImport(PythonDll)] - internal static extern IntPtr PyCFunction_Call(IntPtr func, IntPtr args, IntPtr kw); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyCFunction_Call(IntPtr func, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] - internal static extern IntPtr PyClass_New(IntPtr bases, IntPtr dict, IntPtr name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyClass_New(IntPtr bases, IntPtr dict, IntPtr name); - [DllImport(PythonDll)] - internal static extern IntPtr PyInstance_New(IntPtr cls, IntPtr args, IntPtr kw); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyInstance_New(IntPtr cls, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] - internal static extern IntPtr PyInstance_NewRaw(IntPtr cls, IntPtr dict); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyInstance_NewRaw(IntPtr cls, IntPtr dict); - [DllImport(PythonDll)] - internal static extern IntPtr PyMethod_New(IntPtr func, IntPtr self, IntPtr cls); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyMethod_New(IntPtr func, IntPtr self, IntPtr cls); //==================================================================== @@ -741,7 +912,7 @@ public static extern int Py_Main( /// designed to be lean and mean in IL & avoid managed <-> unmanaged /// transitions. Note that this does not incref the type object. /// - internal static unsafe IntPtr PyObject_TYPE(IntPtr op) + public static unsafe IntPtr PyObject_TYPE(IntPtr op) { var p = (void*)op; if ((void*)0 == p) @@ -763,61 +934,76 @@ internal static unsafe IntPtr PyObject_TYPE(IntPtr op) /// This version avoids a managed <-> unmanaged transition. /// This one does incref the returned type object. /// - internal static IntPtr PyObject_Type(IntPtr op) + public static IntPtr PyObject_Type(IntPtr op) { IntPtr tp = PyObject_TYPE(op); XIncref(tp); return tp; } - internal static string PyObject_GetTypeName(IntPtr op) + public static string PyObject_GetTypeName(IntPtr op) { IntPtr pyType = Marshal.ReadIntPtr(op, ObjectOffset.ob_type); IntPtr ppName = Marshal.ReadIntPtr(pyType, TypeOffset.tp_name); return Marshal.PtrToStringAnsi(ppName); } - [DllImport(PythonDll)] - internal static extern int PyObject_HasAttrString(IntPtr pointer, string name); + /// + /// Test whether the Python object is an iterable. + /// + public 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)] - internal static extern IntPtr PyObject_GetAttrString(IntPtr pointer, string name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_HasAttrString(IntPtr pointer, string name); - [DllImport(PythonDll)] - internal static extern int PyObject_SetAttrString(IntPtr pointer, string name, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_GetAttrString(IntPtr pointer, string name); - [DllImport(PythonDll)] - internal static extern int PyObject_HasAttr(IntPtr pointer, IntPtr name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_SetAttrString(IntPtr pointer, string name, IntPtr value); - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_GetAttr(IntPtr pointer, IntPtr name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_HasAttr(IntPtr pointer, IntPtr name); - [DllImport(PythonDll)] - internal static extern int PyObject_SetAttr(IntPtr pointer, IntPtr name, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_GetAttr(IntPtr pointer, IntPtr name); - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_GetItem(IntPtr pointer, IntPtr key); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_SetAttr(IntPtr pointer, IntPtr name, IntPtr value); - [DllImport(PythonDll)] - internal static extern int PyObject_SetItem(IntPtr pointer, IntPtr key, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_GetItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] - internal static extern int PyObject_DelItem(IntPtr pointer, IntPtr key); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_SetItem(IntPtr pointer, IntPtr key, IntPtr value); - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_GetIter(IntPtr op); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_DelItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_Call(IntPtr pointer, IntPtr args, IntPtr kw); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_GetIter(IntPtr op); - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_CallObject(IntPtr pointer, IntPtr args); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_Call(IntPtr pointer, IntPtr args, IntPtr kw); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_CallObject(IntPtr pointer, IntPtr args); #if PYTHON3 - [DllImport(PythonDll)] - internal static extern int PyObject_RichCompareBool(IntPtr value1, IntPtr value2, int opid); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_RichCompareBool(IntPtr value1, IntPtr value2, int opid); - internal static int PyObject_Compare(IntPtr value1, IntPtr value2) + public static int PyObject_Compare(IntPtr value1, IntPtr value2) { int res; res = PyObject_RichCompareBool(value1, value2, Py_LT); @@ -842,47 +1028,53 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2) return -1; } #elif PYTHON2 - [DllImport(PythonDll)] - internal static extern int PyObject_Compare(IntPtr value1, IntPtr value2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_Compare(IntPtr value1, IntPtr value2); #endif - [DllImport(PythonDll)] - internal static extern int PyObject_IsInstance(IntPtr ob, IntPtr type); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_IsInstance(IntPtr ob, IntPtr type); - [DllImport(PythonDll)] - internal static extern int PyObject_IsSubclass(IntPtr ob, IntPtr type); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_IsSubclass(IntPtr ob, IntPtr type); - [DllImport(PythonDll)] - internal static extern int PyCallable_Check(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyCallable_Check(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern int PyObject_IsTrue(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_IsTrue(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern int PyObject_Not(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_Not(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern int PyObject_Size(IntPtr pointer); + public static long PyObject_Size(IntPtr pointer) + { + return (long) _PyObject_Size(pointer); + } - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_Hash(IntPtr op); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyObject_Size")] + private static extern IntPtr _PyObject_Size(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_Repr(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_Hash(IntPtr op); - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_Str(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_Repr(IntPtr pointer); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_Str(IntPtr pointer); #if PYTHON3 - [DllImport(PythonDll, EntryPoint = "PyObject_Str")] - internal static extern IntPtr PyObject_Unicode(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyObject_Str")] + public static extern IntPtr PyObject_Unicode(IntPtr pointer); #elif PYTHON2 - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_Unicode(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_Unicode(IntPtr pointer); #endif - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_Dir(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_Dir(IntPtr pointer); //==================================================================== @@ -890,352 +1082,453 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2) //==================================================================== #if PYTHON3 - [DllImport(PythonDll, EntryPoint = "PyNumber_Long")] - internal static extern IntPtr PyNumber_Int(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyNumber_Long")] + public static extern IntPtr PyNumber_Int(IntPtr ob); #elif PYTHON2 - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Int(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Int(IntPtr ob); #endif - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Long(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Long(IntPtr ob); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Float(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Float(IntPtr ob); - [DllImport(PythonDll)] - internal static extern bool PyNumber_Check(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern bool PyNumber_Check(IntPtr ob); - internal static bool PyInt_Check(IntPtr ob) + public static bool PyInt_Check(IntPtr ob) { return PyObject_TypeCheck(ob, PyIntType); } - internal static bool PyBool_Check(IntPtr ob) + public static bool PyBool_Check(IntPtr ob) { return PyObject_TypeCheck(ob, PyBoolType); } - internal static IntPtr PyInt_FromInt32(int value) + public static IntPtr PyInt_FromInt32(int value) { var v = new IntPtr(value); return PyInt_FromLong(v); } - internal static IntPtr PyInt_FromInt64(long value) + public static IntPtr PyInt_FromInt64(long value) { var v = new IntPtr(value); return PyInt_FromLong(v); } #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")] - internal static extern int PyInt_AsLong(IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_AsLong")] + public static extern int PyInt_AsLong(IntPtr value); - [DllImport(PythonDll, EntryPoint = "PyLong_FromString")] - internal static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_FromString")] + public static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); - [DllImport(PythonDll, EntryPoint = "PyLong_GetMax")] - internal static extern int PyInt_GetMax(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_GetMax")] + public static extern int PyInt_GetMax(); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr PyInt_FromLong(IntPtr value); - [DllImport(PythonDll)] - internal static extern int PyInt_AsLong(IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyInt_AsLong(IntPtr value); - [DllImport(PythonDll)] - internal static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); - [DllImport(PythonDll)] - internal static extern int PyInt_GetMax(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyInt_GetMax(); #endif - internal static bool PyLong_Check(IntPtr ob) + public static bool PyLong_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyLongType; } - [DllImport(PythonDll)] - internal static extern IntPtr PyLong_FromLong(long value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyLong_FromLong(long value); - [DllImport(PythonDll)] - internal static extern IntPtr PyLong_FromUnsignedLong(uint value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyLong_FromUnsignedLong(uint value); - [DllImport(PythonDll)] - internal static extern IntPtr PyLong_FromDouble(double value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyLong_FromDouble(double value); - [DllImport(PythonDll)] - internal static extern IntPtr PyLong_FromLongLong(long value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyLong_FromLongLong(long value); - [DllImport(PythonDll)] - internal static extern IntPtr PyLong_FromUnsignedLongLong(ulong value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyLong_FromUnsignedLongLong(ulong value); - [DllImport(PythonDll)] - internal static extern IntPtr PyLong_FromString(string value, IntPtr end, int radix); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyLong_FromString(string value, IntPtr end, int radix); - [DllImport(PythonDll)] - internal static extern int PyLong_AsLong(IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyLong_AsLong(IntPtr value); - [DllImport(PythonDll)] - internal static extern uint PyLong_AsUnsignedLong(IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern uint PyLong_AsUnsignedLong(IntPtr value); - [DllImport(PythonDll)] - internal static extern long PyLong_AsLongLong(IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern long PyLong_AsLongLong(IntPtr value); - [DllImport(PythonDll)] - internal static extern ulong PyLong_AsUnsignedLongLong(IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern ulong PyLong_AsUnsignedLongLong(IntPtr value); - internal static bool PyFloat_Check(IntPtr ob) + public static bool PyFloat_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyFloatType; } - [DllImport(PythonDll)] - internal static extern IntPtr PyFloat_FromDouble(double value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyFloat_FromDouble(double value); - [DllImport(PythonDll)] - internal static extern IntPtr PyFloat_FromString(IntPtr value, IntPtr junk); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyFloat_FromString(IntPtr value, IntPtr junk); - [DllImport(PythonDll)] - internal static extern double PyFloat_AsDouble(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern double PyFloat_AsDouble(IntPtr ob); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Add(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Add(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Subtract(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Subtract(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Multiply(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Multiply(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Divide(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_TrueDivide(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_And(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_And(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Xor(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Xor(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Or(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Or(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Lshift(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Lshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Rshift(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Rshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Power(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Power(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Remainder(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Remainder(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceAdd(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceAdd(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceSubtract(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceSubtract(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceMultiply(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceMultiply(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceDivide(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceTrueDivide(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceAnd(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceAnd(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceXor(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceXor(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceOr(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceOr(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceLshift(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceLshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceRshift(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceRshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlacePower(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlacePower(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_InPlaceRemainder(IntPtr o1, IntPtr o2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_InPlaceRemainder(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Negative(IntPtr o1); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Negative(IntPtr o1); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Positive(IntPtr o1); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Positive(IntPtr o1); - [DllImport(PythonDll)] - internal static extern IntPtr PyNumber_Invert(IntPtr o1); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyNumber_Invert(IntPtr o1); //==================================================================== // Python sequence API //==================================================================== - [DllImport(PythonDll)] - internal static extern bool PySequence_Check(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern bool PySequence_Check(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PySequence_GetItem(IntPtr pointer, int index); + public static IntPtr PySequence_GetItem(IntPtr pointer, long index) + { + return PySequence_GetItem(pointer, new IntPtr(index)); + } - [DllImport(PythonDll)] - internal static extern int PySequence_SetItem(IntPtr pointer, int index, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PySequence_GetItem(IntPtr pointer, IntPtr index); - [DllImport(PythonDll)] - internal static extern int PySequence_DelItem(IntPtr pointer, int index); + public static int PySequence_SetItem(IntPtr pointer, long index, IntPtr value) + { + return PySequence_SetItem(pointer, new IntPtr(index), value); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PySequence_SetItem(IntPtr pointer, IntPtr index, IntPtr value); - [DllImport(PythonDll)] - internal static extern IntPtr PySequence_GetSlice(IntPtr pointer, int i1, int i2); + public static int PySequence_DelItem(IntPtr pointer, long index) + { + return PySequence_DelItem(pointer, new IntPtr(index)); + } - [DllImport(PythonDll)] - internal static extern int PySequence_SetSlice(IntPtr pointer, int i1, int i2, IntPtr v); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PySequence_DelItem(IntPtr pointer, IntPtr index); - [DllImport(PythonDll)] - internal static extern int PySequence_DelSlice(IntPtr pointer, int i1, int i2); + public static IntPtr PySequence_GetSlice(IntPtr pointer, long i1, long i2) + { + return PySequence_GetSlice(pointer, new IntPtr(i1), new IntPtr(i2)); + } - [DllImport(PythonDll)] - internal static extern int PySequence_Size(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PySequence_GetSlice(IntPtr pointer, IntPtr i1, IntPtr i2); - [DllImport(PythonDll)] - internal static extern int PySequence_Contains(IntPtr pointer, IntPtr item); + public static int PySequence_SetSlice(IntPtr pointer, long i1, long i2, IntPtr v) + { + return PySequence_SetSlice(pointer, new IntPtr(i1), new IntPtr(i2), v); + } - [DllImport(PythonDll)] - internal static extern IntPtr PySequence_Concat(IntPtr pointer, IntPtr other); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PySequence_SetSlice(IntPtr pointer, IntPtr i1, IntPtr i2, IntPtr v); - [DllImport(PythonDll)] - internal static extern IntPtr PySequence_Repeat(IntPtr pointer, int count); + public static int PySequence_DelSlice(IntPtr pointer, long i1, long i2) + { + return PySequence_DelSlice(pointer, new IntPtr(i1), new IntPtr(i2)); + } - [DllImport(PythonDll)] - internal static extern int PySequence_Index(IntPtr pointer, IntPtr item); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PySequence_DelSlice(IntPtr pointer, IntPtr i1, IntPtr i2); + + public static long PySequence_Size(IntPtr pointer) + { + return (long) _PySequence_Size(pointer); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PySequence_Size")] + private static extern IntPtr _PySequence_Size(IntPtr pointer); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PySequence_Contains(IntPtr pointer, IntPtr item); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PySequence_Concat(IntPtr pointer, IntPtr other); + + public static IntPtr PySequence_Repeat(IntPtr pointer, long count) + { + return PySequence_Repeat(pointer, new IntPtr(count)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PySequence_Repeat(IntPtr pointer, IntPtr count); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PySequence_Index(IntPtr pointer, IntPtr item); + + public static long PySequence_Count(IntPtr pointer, IntPtr value) + { + return (long) _PySequence_Count(pointer, value); + } - [DllImport(PythonDll)] - internal static extern int PySequence_Count(IntPtr pointer, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PySequence_Count")] + private static extern IntPtr _PySequence_Count(IntPtr pointer, IntPtr value); - [DllImport(PythonDll)] - internal static extern IntPtr PySequence_Tuple(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PySequence_Tuple(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PySequence_List(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PySequence_List(IntPtr pointer); //==================================================================== // Python string API //==================================================================== - internal static bool IsStringType(IntPtr op) + public static bool IsStringType(IntPtr op) { IntPtr t = PyObject_TYPE(op); return (t == PyStringType) || (t == PyUnicodeType); } - internal static bool PyString_Check(IntPtr ob) + public static bool PyString_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyStringType; } - internal static IntPtr PyString_FromString(string value) + public static IntPtr PyString_FromString(string value) { +#if PYTHON3 + return PyUnicode_FromKindAndData(_UCS, value, value.Length); +#elif PYTHON2 return PyString_FromStringAndSize(value, value.Length); +#endif } #if PYTHON3 - [DllImport(PythonDll)] - internal static extern IntPtr PyBytes_FromString(string op); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyBytes_FromString(string op); + + public static long PyBytes_Size(IntPtr op) + { + return (long) _PyBytes_Size(op); + } - [DllImport(PythonDll)] - internal static extern int PyBytes_Size(IntPtr op); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyBytes_Size")] + private static extern IntPtr _PyBytes_Size(IntPtr op); - internal static IntPtr PyBytes_AS_STRING(IntPtr ob) + public static IntPtr PyBytes_AS_STRING(IntPtr ob) { return ob + BytesOffset.ob_sval; } - [DllImport(PythonDll, EntryPoint = "PyUnicode_FromStringAndSize")] - internal static extern IntPtr PyString_FromStringAndSize( + public static IntPtr PyString_FromStringAndSize(string value, long size) + { + return _PyString_FromStringAndSize(value, new IntPtr(size)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicode_FromStringAndSize")] + public static extern IntPtr _PyString_FromStringAndSize( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string value, - int size + IntPtr size ); - [DllImport(PythonDll)] - internal static extern IntPtr PyUnicode_FromStringAndSize(IntPtr value, int size); + public static IntPtr PyUnicode_FromStringAndSize(IntPtr value, long size) + { + return PyUnicode_FromStringAndSize(value, new IntPtr(size)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyUnicode_FromStringAndSize(IntPtr value, IntPtr size); #elif PYTHON2 - [DllImport(PythonDll)] - internal static extern IntPtr PyString_FromStringAndSize(string value, int size); + public static IntPtr PyString_FromStringAndSize(string value, long size) + { + return PyString_FromStringAndSize(value, new IntPtr(size)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyString_FromStringAndSize(string value, IntPtr size); - [DllImport(PythonDll)] - internal static extern IntPtr PyString_AsString(IntPtr op); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyString_AsString(IntPtr op); - [DllImport(PythonDll)] - internal static extern int PyString_Size(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyString_Size(IntPtr pointer); #endif - internal static bool PyUnicode_Check(IntPtr ob) + public static bool PyUnicode_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyUnicodeType; } #if PYTHON3 - [DllImport(PythonDll)] - internal static extern IntPtr PyUnicode_FromObject(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyUnicode_FromObject(IntPtr ob); - [DllImport(PythonDll)] - internal static extern IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); - [DllImport(PythonDll)] - internal static extern IntPtr PyUnicode_FromKindAndData( + public static IntPtr PyUnicode_FromKindAndData(int kind, string s, long size) + { + return PyUnicode_FromKindAndData(kind, s, new IntPtr(size)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyUnicode_FromKindAndData( int kind, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UcsMarshaler))] string s, - int size + IntPtr size ); - internal static IntPtr PyUnicode_FromUnicode(string s, int size) + public static IntPtr PyUnicode_FromUnicode(string s, long size) { - return PyUnicode_FromKindAndData(UCS, s, size); + return PyUnicode_FromKindAndData(_UCS, s, size); } - [DllImport(PythonDll)] - internal static extern int PyUnicode_GetSize(IntPtr ob); + public static long PyUnicode_GetSize(IntPtr ob) + { + return (long)_PyUnicode_GetSize(ob); + } - [DllImport(PythonDll)] - internal static extern IntPtr PyUnicode_AsUnicode(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyUnicode_GetSize")] + private static extern IntPtr _PyUnicode_GetSize(IntPtr ob); - [DllImport(PythonDll)] - internal static extern IntPtr PyUnicode_FromOrdinal(int c); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyUnicode_AsUnicode(IntPtr ob); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyUnicode_FromOrdinal(int c); #elif PYTHON2 - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromObject")] - internal static extern IntPtr PyUnicode_FromObject(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromObject")] + public static extern IntPtr PyUnicode_FromObject(IntPtr ob); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromEncodedObject")] + public static extern IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromEncodedObject")] - internal static extern IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); + public static IntPtr PyUnicode_FromUnicode(string s, long size) + { + return PyUnicode_FromUnicode(s, new IntPtr(size)); + } - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromUnicode")] - internal static extern IntPtr PyUnicode_FromUnicode( + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromUnicode")] + private static extern IntPtr PyUnicode_FromUnicode( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UcsMarshaler))] string s, - int size + IntPtr size ); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "GetSize")] - internal static extern int PyUnicode_GetSize(IntPtr ob); + public static long PyUnicode_GetSize(IntPtr ob) + { + return (long) _PyUnicode_GetSize(ob); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "GetSize")] + public static extern IntPtr _PyUnicode_GetSize(IntPtr ob); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "AsUnicode")] - internal static extern IntPtr PyUnicode_AsUnicode(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "AsUnicode")] + public static extern IntPtr PyUnicode_AsUnicode(IntPtr ob); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromOrdinal")] - internal static extern IntPtr PyUnicode_FromOrdinal(int c); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromOrdinal")] + public static extern IntPtr PyUnicode_FromOrdinal(int c); #endif - internal static IntPtr PyUnicode_FromString(string s) + public static IntPtr PyUnicode_FromString(string s) { return PyUnicode_FromUnicode(s, s.Length); } @@ -1253,7 +1546,7 @@ internal static IntPtr PyUnicode_FromString(string s) /// /// PyStringType or PyUnicodeType object to convert /// Managed String - internal static string GetManagedString(IntPtr op) + public static string GetManagedString(IntPtr op) { IntPtr type = PyObject_TYPE(op); @@ -1267,9 +1560,9 @@ internal static string GetManagedString(IntPtr op) if (type == PyUnicodeType) { IntPtr p = PyUnicode_AsUnicode(op); - int length = PyUnicode_GetSize(op); + int length = (int)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); @@ -1283,325 +1576,410 @@ internal static string GetManagedString(IntPtr op) // Python dictionary API //==================================================================== - internal static bool PyDict_Check(IntPtr ob) + public static bool PyDict_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyDictType; } - [DllImport(PythonDll)] - internal static extern IntPtr PyDict_New(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyDict_New(); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyDictProxy_New(IntPtr dict); - [DllImport(PythonDll)] - internal static extern IntPtr PyDictProxy_New(IntPtr dict); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyDict_GetItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] - internal static extern IntPtr PyDict_GetItem(IntPtr pointer, IntPtr key); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyDict_GetItemString(IntPtr pointer, string key); - [DllImport(PythonDll)] - internal static extern IntPtr PyDict_GetItemString(IntPtr pointer, string key); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyDict_SetItem(IntPtr pointer, IntPtr key, IntPtr value); - [DllImport(PythonDll)] - internal static extern int PyDict_SetItem(IntPtr pointer, IntPtr key, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyDict_SetItemString(IntPtr pointer, string key, IntPtr value); - [DllImport(PythonDll)] - internal static extern int PyDict_SetItemString(IntPtr pointer, string key, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyDict_DelItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] - internal static extern int PyDict_DelItem(IntPtr pointer, IntPtr key); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyDict_DelItemString(IntPtr pointer, string key); - [DllImport(PythonDll)] - internal static extern int PyDict_DelItemString(IntPtr pointer, string key); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyMapping_HasKey(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] - internal static extern int PyMapping_HasKey(IntPtr pointer, IntPtr key); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyDict_Keys(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PyDict_Keys(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyDict_Values(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PyDict_Values(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyDict_Items(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PyDict_Items(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyDict_Copy(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PyDict_Copy(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyDict_Update(IntPtr pointer, IntPtr other); - [DllImport(PythonDll)] - internal static extern int PyDict_Update(IntPtr pointer, IntPtr other); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyDict_Clear(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern void PyDict_Clear(IntPtr pointer); + public static long PyDict_Size(IntPtr pointer) + { + return (long) _PyDict_Size(pointer); + } - [DllImport(PythonDll)] - internal static extern int PyDict_Size(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyDict_Size")] + public static extern IntPtr _PyDict_Size(IntPtr pointer); //==================================================================== // Python list API //==================================================================== - internal static bool PyList_Check(IntPtr ob) + public static bool PyList_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyListType; } - [DllImport(PythonDll)] - internal static extern IntPtr PyList_New(int size); + public static IntPtr PyList_New(long size) + { + return PyList_New(new IntPtr(size)); + } - [DllImport(PythonDll)] - internal static extern IntPtr PyList_AsTuple(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyList_New(IntPtr size); - [DllImport(PythonDll)] - internal static extern IntPtr PyList_GetItem(IntPtr pointer, int index); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyList_AsTuple(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern int PyList_SetItem(IntPtr pointer, int index, IntPtr value); + public static IntPtr PyList_GetItem(IntPtr pointer, long index) + { + return PyList_GetItem(pointer, new IntPtr(index)); + } - [DllImport(PythonDll)] - internal static extern int PyList_Insert(IntPtr pointer, int index, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyList_GetItem(IntPtr pointer, IntPtr index); - [DllImport(PythonDll)] - internal static extern int PyList_Append(IntPtr pointer, IntPtr value); + public static int PyList_SetItem(IntPtr pointer, long index, IntPtr value) + { + return PyList_SetItem(pointer, new IntPtr(index), value); + } - [DllImport(PythonDll)] - internal static extern int PyList_Reverse(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PyList_SetItem(IntPtr pointer, IntPtr index, IntPtr value); - [DllImport(PythonDll)] - internal static extern int PyList_Sort(IntPtr pointer); + public static int PyList_Insert(IntPtr pointer, long index, IntPtr value) + { + return PyList_Insert(pointer, new IntPtr(index), value); + } - [DllImport(PythonDll)] - internal static extern IntPtr PyList_GetSlice(IntPtr pointer, int start, int end); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PyList_Insert(IntPtr pointer, IntPtr index, IntPtr value); - [DllImport(PythonDll)] - internal static extern int PyList_SetSlice(IntPtr pointer, int start, int end, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyList_Append(IntPtr pointer, IntPtr value); - [DllImport(PythonDll)] - internal static extern int PyList_Size(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyList_Reverse(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyList_Sort(IntPtr pointer); + + public static IntPtr PyList_GetSlice(IntPtr pointer, long start, long end) + { + return PyList_GetSlice(pointer, new IntPtr(start), new IntPtr(end)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyList_GetSlice(IntPtr pointer, IntPtr start, IntPtr end); + + public static int PyList_SetSlice(IntPtr pointer, long start, long end, IntPtr value) + { + return PyList_SetSlice(pointer, new IntPtr(start), new IntPtr(end), value); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PyList_SetSlice(IntPtr pointer, IntPtr start, IntPtr end, IntPtr value); + + public static long PyList_Size(IntPtr pointer) + { + return (long) _PyList_Size(pointer); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyList_Size")] + private static extern IntPtr _PyList_Size(IntPtr pointer); //==================================================================== // Python tuple API //==================================================================== - internal static bool PyTuple_Check(IntPtr ob) + public static bool PyTuple_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyTupleType; } - [DllImport(PythonDll)] - internal static extern IntPtr PyTuple_New(int size); + public static IntPtr PyTuple_New(long size) + { + return PyTuple_New(new IntPtr(size)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyTuple_New(IntPtr size); - [DllImport(PythonDll)] - internal static extern IntPtr PyTuple_GetItem(IntPtr pointer, int index); + public static IntPtr PyTuple_GetItem(IntPtr pointer, long index) + { + return PyTuple_GetItem(pointer, new IntPtr(index)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyTuple_GetItem(IntPtr pointer, IntPtr index); + + public static int PyTuple_SetItem(IntPtr pointer, long index, IntPtr value) + { + return PyTuple_SetItem(pointer, new IntPtr(index), value); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PyTuple_SetItem(IntPtr pointer, IntPtr index, IntPtr value); - [DllImport(PythonDll)] - internal static extern int PyTuple_SetItem(IntPtr pointer, int index, IntPtr value); + public static IntPtr PyTuple_GetSlice(IntPtr pointer, long start, long end) + { + return PyTuple_GetSlice(pointer, new IntPtr(start), new IntPtr(end)); + } - [DllImport(PythonDll)] - internal static extern IntPtr PyTuple_GetSlice(IntPtr pointer, int start, int end); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyTuple_GetSlice(IntPtr pointer, IntPtr start, IntPtr end); - [DllImport(PythonDll)] - internal static extern int PyTuple_Size(IntPtr pointer); + public static long PyTuple_Size(IntPtr pointer) + { + return (long) _PyTuple_Size(pointer); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyTuple_Size")] + private static extern IntPtr _PyTuple_Size(IntPtr pointer); //==================================================================== // Python iterator API //==================================================================== -#if PYTHON2 - [DllImport(PythonDll)] - internal static extern bool PyIter_Check(IntPtr pointer); -#elif PYTHON3 - internal static bool PyIter_Check(IntPtr pointer) + public 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)] - internal static extern IntPtr PyIter_Next(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyIter_Next(IntPtr pointer); //==================================================================== // Python module API //==================================================================== - [DllImport(PythonDll)] - internal static extern IntPtr PyModule_New(string name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyModule_New(string name); - [DllImport(PythonDll)] - internal static extern string PyModule_GetName(IntPtr module); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern string PyModule_GetName(IntPtr module); - [DllImport(PythonDll)] - internal static extern IntPtr PyModule_GetDict(IntPtr module); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyModule_GetDict(IntPtr module); - [DllImport(PythonDll)] - internal static extern string PyModule_GetFilename(IntPtr module); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern string PyModule_GetFilename(IntPtr module); #if PYTHON3 - [DllImport(PythonDll)] - internal static extern IntPtr PyModule_Create2(IntPtr module, int apiver); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyModule_Create2(IntPtr module, int apiver); #endif - [DllImport(PythonDll)] - internal static extern IntPtr PyImport_Import(IntPtr name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyImport_Import(IntPtr name); - [DllImport(PythonDll)] - internal static extern IntPtr PyImport_ImportModule(string name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyImport_ImportModule(string name); - [DllImport(PythonDll)] - internal static extern IntPtr PyImport_ReloadModule(IntPtr module); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyImport_ReloadModule(IntPtr module); - [DllImport(PythonDll)] - internal static extern IntPtr PyImport_AddModule(string name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyImport_AddModule(string name); - [DllImport(PythonDll)] - internal static extern IntPtr PyImport_GetModuleDict(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyImport_GetModuleDict(); #if PYTHON3 - [DllImport(PythonDll)] - internal static extern void PySys_SetArgvEx( + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PySys_SetArgvEx( int argc, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StrArrayMarshaler))] string[] argv, int updatepath ); #elif PYTHON2 - [DllImport(PythonDll)] - internal static extern void PySys_SetArgvEx( + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PySys_SetArgvEx( int argc, string[] argv, int updatepath ); #endif - [DllImport(PythonDll)] - internal static extern IntPtr PySys_GetObject(string name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PySys_GetObject(string name); - [DllImport(PythonDll)] - internal static extern int PySys_SetObject(string name, IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PySys_SetObject(string name, IntPtr ob); //==================================================================== // Python type object API //==================================================================== - internal static bool PyType_Check(IntPtr ob) + public static bool PyType_Check(IntPtr ob) { return PyObject_TypeCheck(ob, PyTypeType); } - [DllImport(PythonDll)] - internal static extern void PyType_Modified(IntPtr type); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyType_Modified(IntPtr type); - [DllImport(PythonDll)] - internal static extern bool PyType_IsSubtype(IntPtr t1, IntPtr t2); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern bool PyType_IsSubtype(IntPtr t1, IntPtr t2); - internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) + public static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) { IntPtr t = PyObject_TYPE(ob); return (t == tp) || PyType_IsSubtype(t, tp); } - [DllImport(PythonDll)] - internal static extern IntPtr PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] - internal static extern IntPtr PyType_GenericAlloc(IntPtr type, int n); + public static IntPtr PyType_GenericAlloc(IntPtr type, long n) + { + return PyType_GenericAlloc(type, new IntPtr(n)); + } - [DllImport(PythonDll)] - internal static extern int PyType_Ready(IntPtr type); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n); - [DllImport(PythonDll)] - internal static extern IntPtr _PyType_Lookup(IntPtr type, IntPtr name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyType_Ready(IntPtr type); - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_GenericGetAttr(IntPtr obj, IntPtr name); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr _PyType_Lookup(IntPtr type, IntPtr name); - [DllImport(PythonDll)] - internal static extern int PyObject_GenericSetAttr(IntPtr obj, IntPtr name, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_GenericGetAttr(IntPtr obj, IntPtr name); - [DllImport(PythonDll)] - internal static extern IntPtr _PyObject_GetDictPtr(IntPtr obj); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyObject_GenericSetAttr(IntPtr obj, IntPtr name, IntPtr value); - [DllImport(PythonDll)] - internal static extern IntPtr PyObject_GC_New(IntPtr tp); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr _PyObject_GetDictPtr(IntPtr obj); - [DllImport(PythonDll)] - internal static extern void PyObject_GC_Del(IntPtr tp); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyObject_GC_New(IntPtr tp); - [DllImport(PythonDll)] - internal static extern void PyObject_GC_Track(IntPtr tp); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyObject_GC_Del(IntPtr tp); - [DllImport(PythonDll)] - internal static extern void PyObject_GC_UnTrack(IntPtr tp); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyObject_GC_Track(IntPtr tp); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyObject_GC_UnTrack(IntPtr tp); //==================================================================== // Python memory API //==================================================================== - [DllImport(PythonDll)] - internal static extern IntPtr PyMem_Malloc(int size); + public static IntPtr PyMem_Malloc(long size) + { + return PyMem_Malloc(new IntPtr(size)); + } - [DllImport(PythonDll)] - internal static extern IntPtr PyMem_Realloc(IntPtr ptr, int size); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyMem_Malloc(IntPtr size); - [DllImport(PythonDll)] - internal static extern void PyMem_Free(IntPtr ptr); + public static IntPtr PyMem_Realloc(IntPtr ptr, long size) + { + return PyMem_Realloc(ptr, new IntPtr(size)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyMem_Realloc(IntPtr ptr, IntPtr size); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyMem_Free(IntPtr ptr); //==================================================================== // Python exception API //==================================================================== - [DllImport(PythonDll)] - internal static extern void PyErr_SetString(IntPtr ob, string message); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyErr_SetString(IntPtr ob, string message); - [DllImport(PythonDll)] - internal static extern void PyErr_SetObject(IntPtr ob, IntPtr message); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyErr_SetObject(IntPtr ob, IntPtr message); - [DllImport(PythonDll)] - internal static extern IntPtr PyErr_SetFromErrno(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyErr_SetFromErrno(IntPtr ob); - [DllImport(PythonDll)] - internal static extern void PyErr_SetNone(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyErr_SetNone(IntPtr ob); - [DllImport(PythonDll)] - internal static extern int PyErr_ExceptionMatches(IntPtr exception); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyErr_ExceptionMatches(IntPtr exception); - [DllImport(PythonDll)] - internal static extern int PyErr_GivenExceptionMatches(IntPtr ob, IntPtr val); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int PyErr_GivenExceptionMatches(IntPtr ob, IntPtr val); - [DllImport(PythonDll)] - internal static extern void PyErr_NormalizeException(IntPtr ob, IntPtr val, IntPtr tb); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyErr_NormalizeException(IntPtr ob, IntPtr val, IntPtr tb); - [DllImport(PythonDll)] - internal static extern int PyErr_Occurred(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyErr_Occurred(); - [DllImport(PythonDll)] - internal static extern void PyErr_Fetch(ref IntPtr ob, ref IntPtr val, ref IntPtr tb); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyErr_Fetch(ref IntPtr ob, ref IntPtr val, ref IntPtr tb); - [DllImport(PythonDll)] - internal static extern void PyErr_Restore(IntPtr ob, IntPtr val, IntPtr tb); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyErr_Restore(IntPtr ob, IntPtr val, IntPtr tb); - [DllImport(PythonDll)] - internal static extern void PyErr_Clear(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyErr_Clear(); - [DllImport(PythonDll)] - internal static extern void PyErr_Print(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void PyErr_Print(); //==================================================================== // Miscellaneous //==================================================================== - [DllImport(PythonDll)] - internal static extern IntPtr PyMethod_Self(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyMethod_Self(IntPtr ob); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr PyMethod_Function(IntPtr ob); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int Py_AddPendingCall(IntPtr func, IntPtr arg); - [DllImport(PythonDll)] - internal static extern IntPtr PyMethod_Function(IntPtr ob); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + public static extern int Py_MakePendingCalls(); } } diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index 6f373f036..663cab417 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -10,7 +10,7 @@ namespace Python.Runtime /// The TypeManager class is responsible for building binary-compatible /// Python type objects that are implemented in managed code. /// - internal class TypeManager + public class TypeManager { private static BindingFlags tbFlags; private static Dictionary cache; @@ -21,6 +21,10 @@ static TypeManager() cache = new Dictionary(128); } + public static void Reset() + { + cache = new Dictionary(128); + } /// /// Given a managed Type derived from ExtensionType, get the handle to @@ -86,7 +90,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 +164,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 +210,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 +223,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 +327,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 +384,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); @@ -445,6 +451,257 @@ internal static IntPtr AllocateTypeObject(string name) } + #region Native Code Page + /// + /// Initialized by InitializeNativeCodePage. + /// + /// This points to a page of memory allocated using mmap or VirtualAlloc + /// (depending on the system), and marked read and execute (not write). + /// Very much on purpose, the page is *not* released on a shutdown and + /// is instead leaked. See the TestDomainReload test case. + /// + /// The contents of the page are two native functions: one that returns 0, + /// one that returns 1. + /// + /// If python didn't keep its gc list through a Py_Finalize we could remove + /// this entire section. + /// + internal static IntPtr NativeCodePage = IntPtr.Zero; + + /// + /// Structure to describe native code. + /// + /// Use NativeCode.Active to get the native code for the current platform. + /// + /// Generate the code by creating the following C code: + /// + /// int Return0() { return 0; } + /// int Return1() { return 1; } + /// + /// Then compiling on the target platform, e.g. with gcc or clang: + /// cc -c -fomit-frame-pointer -O2 foo.c + /// And then analyzing the resulting functions with a hex editor, e.g.: + /// objdump -disassemble foo.o + /// + public class NativeCode + { + /// + /// The code, as a string of bytes. + /// + public byte[] Code { get; private set; } + + /// + /// Where does the "return 0" function start? + /// + public int Return0 { get; private set; } + + /// + /// Where does the "return 1" function start? + /// + public int Return1 { get; private set; } + + public static NativeCode Active + { + get + { + switch(Runtime.Machine) + { + case Runtime.MachineType.i386: + return I386; + case Runtime.MachineType.x86_64: + return X86_64; + case Runtime.MachineType.armv7l: + return Armv7l; + case Runtime.MachineType.armv8: + return Armv8; + default: + throw new NotImplementedException($"No support for {Runtime.MachineName}"); + } + } + } + + /// + /// Code for x86_64. See the class comment for how it was generated. + /// + public static readonly NativeCode X86_64 = new NativeCode() + { + Return0 = 0x10, + Return1 = 0, + Code = new byte[] + { + // First Return1: + 0xb8, 0x01, 0x00, 0x00, 0x00, // movl $1, %eax + 0xc3, // ret + + // Now some padding so that Return0 can be 16-byte-aligned. + // I put Return1 first so there's not as much padding to type in. + 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, // nop + + // Now Return0. + 0x31, 0xc0, // xorl %eax, %eax + 0xc3, // ret + } + }; + + /// + /// Code for X86. + /// + /// It's bitwise identical to X86_64, so we just point to it. + /// + /// + public static readonly NativeCode I386 = X86_64; + + public static readonly NativeCode Armv7l = new NativeCode() + { + Return0 = 0, + Return1 = 0x08, + Code = new byte[] + { + 0xe3, 0xa0, 0x00, 0x00, // mov r0, #0 + 0xe1, 0x2f, 0xff, 0x1e, // bx lr + + 0xe3, 0xa0, 0x00, 0x01, // mov r0, #1 + 0xe1, 0x2f, 0xff, 0x1e, // bx lr + } + }; + + public static readonly NativeCode Armv8 = new NativeCode() + { + Return0 = 0, + Return1 = 0x08, + Code = new byte[] + { + 0x52, 0x80, 0x00, 0x00, // mov w0, #0x0 + 0xd6, 0x5f, 0x03, 0xc0, // ret + + 0x52, 0x80, 0x00, 0x20, // mov w0, #0x1 + 0xd6, 0x5f, 0x03, 0xc0, // ret + } + }; + } + + /// + /// Platform-dependent mmap and mprotect. + /// + public interface IMemoryMapper + { + /// + /// Map at least numBytes of memory. Mark the page read-write (but not exec). + /// + IntPtr MapWriteable(int numBytes); + + /// + /// Sets the mapped memory to be read-exec (but not write). + /// + void SetReadExec(IntPtr mappedMemory, int numBytes); + } + + class WindowsMemoryMapper : IMemoryMapper + { + const UInt32 MEM_COMMIT = 0x1000; + const UInt32 MEM_RESERVE = 0x2000; + const UInt32 PAGE_READWRITE = 0x04; + const UInt32 PAGE_EXECUTE_READ = 0x20; + + [DllImport("kernel32.dll")] + static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, UInt32 flAllocationType, UInt32 flProtect); + + [DllImport("kernel32.dll")] + static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, UInt32 flNewProtect, out UInt32 lpflOldProtect); + + public IntPtr MapWriteable(int numBytes) + { + return VirtualAlloc(IntPtr.Zero, new IntPtr(numBytes), + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + } + + public void SetReadExec(IntPtr mappedMemory, int numBytes) + { + UInt32 _; + VirtualProtect(mappedMemory, new IntPtr(numBytes), PAGE_EXECUTE_READ, out _); + } + } + + class UnixMemoryMapper : IMemoryMapper + { + const int PROT_READ = 0x1; + const int PROT_WRITE = 0x2; + const int PROT_EXEC = 0x4; + + const int MAP_PRIVATE = 0x2; + int MAP_ANONYMOUS + { + get + { + switch (Runtime.OperatingSystem) + { + case Runtime.OperatingSystemType.Darwin: + return 0x1000; + case Runtime.OperatingSystemType.Linux: + return 0x20; + default: + throw new NotImplementedException($"mmap is not supported on {Runtime.OperatingSystemName}"); + } + } + } + + [DllImport("libc")] + static extern IntPtr mmap(IntPtr addr, IntPtr len, int prot, int flags, int fd, IntPtr offset); + + [DllImport("libc")] + static extern int mprotect(IntPtr addr, IntPtr len, int prot); + + public IntPtr MapWriteable(int numBytes) + { + // MAP_PRIVATE must be set on linux, even though MAP_ANON implies it. + // It doesn't hurt on darwin, so just do it. + return mmap(IntPtr.Zero, new IntPtr(numBytes), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, IntPtr.Zero); + } + + public void SetReadExec(IntPtr mappedMemory, int numBytes) + { + mprotect(mappedMemory, new IntPtr(numBytes), PROT_READ | PROT_EXEC); + } + } + + public static IMemoryMapper CreateMemoryMapper() + { + switch (Runtime.OperatingSystem) + { + case Runtime.OperatingSystemType.Darwin: + case Runtime.OperatingSystemType.Linux: + return new UnixMemoryMapper(); + case Runtime.OperatingSystemType.Windows: + return new WindowsMemoryMapper(); + default: + throw new NotImplementedException($"No support for {Runtime.OperatingSystemName}"); + } + } + + /// + /// Initializes the native code page. + /// + /// Safe to call if we already initialized (this function is idempotent). + /// + /// + internal static void InitializeNativeCodePage() + { + // Do nothing if we already initialized. + if (NativeCodePage != IntPtr.Zero) + { + return; + } + + // Allocate the page, write the native code into it, then set it + // to be executable. + IMemoryMapper mapper = CreateMemoryMapper(); + int codeLength = NativeCode.Active.Code.Length; + NativeCodePage = mapper.MapWriteable(codeLength); + Marshal.Copy(NativeCode.Active.Code, 0, NativeCodePage, codeLength); + mapper.SetReadExec(NativeCodePage, codeLength); + } +#endregion + /// /// Given a newly allocated Python type object and a managed Type that /// provides the implementation for the type, connect the type slots of @@ -452,8 +709,10 @@ internal static IntPtr AllocateTypeObject(string name) /// internal static void InitializeSlots(IntPtr type, Type impl) { - var seen = new Hashtable(8); - Type offsetType = typeof(TypeOffset); + // We work from the most-derived class up; make sure to get + // the most-derived slot and not to override it with a base + // class's slot. + var seen = new HashSet(); while (impl != null) { @@ -471,24 +730,52 @@ internal static void InitializeSlots(IntPtr type, Type impl) continue; } - if (seen[name] != null) + if (seen.Contains(name)) { continue; } - FieldInfo fi = offsetType.GetField(name); - var offset = (int)fi.GetValue(offsetType); - - IntPtr slot = Interop.GetThunk(method); - Marshal.WriteIntPtr(type, offset, slot); + InitializeSlot(type, Interop.GetThunk(method), name); - seen[name] = 1; + seen.Add(name); } impl = impl.BaseType; } + + // See the TestDomainReload test: there was a crash related to + // the gc-related slots. They always return 0 or 1 because we don't + // really support gc: + // tp_traverse (returns 0) + // tp_clear (returns 0) + // tp_is_gc (returns 1) + // We can't do without: python really wants those slots to exist. + // We can't implement those in C# because the application domain + // can be shut down and the memory released. + InitializeNativeCodePage(); + InitializeSlot(type, NativeCodePage + NativeCode.Active.Return0, "tp_traverse"); + InitializeSlot(type, NativeCodePage + NativeCode.Active.Return0, "tp_clear"); + InitializeSlot(type, NativeCodePage + NativeCode.Active.Return1, "tp_is_gc"); } + /// + /// Helper for InitializeSlots. + /// + /// Initializes one slot to point to a function pointer. + /// The function pointer might be a thunk for C#, or it may be + /// an address in the NativeCodePage. + /// + /// Type being initialized. + /// Function pointer. + /// Name of the method. + static void InitializeSlot(IntPtr type, IntPtr slot, string name) + { + Type typeOffset = typeof(TypeOffset); + FieldInfo fi = typeOffset.GetField(name); + var offset = (int)fi.GetValue(typeOffset); + + Marshal.WriteIntPtr(type, offset, slot); + } /// /// Given a newly allocated Python type object and a managed Type that diff --git a/src/testing/InheritanceTest.cs b/src/testing/InheritanceTest.cs new file mode 100644 index 000000000..529703b3c --- /dev/null +++ b/src/testing/InheritanceTest.cs @@ -0,0 +1,14 @@ +using System; + +namespace Python.Test +{ + public class BaseClass + { + public bool IsBase() => true; + } + + public class DerivedClass : BaseClass + { + public new bool IsBase() => false; + } +} diff --git a/src/testing/Python.Test.csproj b/src/testing/Python.Test.csproj index ce8dca10d..005e09734 100644 --- a/src/testing/Python.Test.csproj +++ b/src/testing/Python.Test.csproj @@ -1,113 +1,15 @@ - - + + - Debug - AnyCPU - {6F401A34-273B-450F-9A4C-13550BE0767B} - Library - Python.Test - Python.Test - bin\Python.Test.xml - bin\ - v4.0 - - 1591,0067 - ..\..\ - $(SolutionDir)\bin\ - 6 - false - ..\pythonnet.snk - prompt - - - x86 - - - x64 - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly + netstandard2.0 + - - - - - - - - - - - - - - - - - - - - + + - - + - - - {097B4AC0-74E9-4C58-BCF8-C69746EC8271} - Python.Runtime - - - - - $(TargetPath) - $(TargetDir)$(TargetName).pdb - - - - - + diff --git a/src/testing/conversiontest.cs b/src/testing/conversiontest.cs index 7179c7607..06ab7cb4e 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 { @@ -54,4 +60,19 @@ public string GetValue() return value; } } + + public class UnicodeString + { + public string value = "안녕"; + + public string GetString() + { + return value; + } + + public override string ToString() + { + return value; + } + } } diff --git a/src/testing/exceptiontest.cs b/src/testing/exceptiontest.cs index e4f683721..45acaa2cc 100644 --- a/src/testing/exceptiontest.cs +++ b/src/testing/exceptiontest.cs @@ -1,4 +1,6 @@ using System; +using System.Collections; +using System.Collections.Generic; namespace Python.Test { @@ -54,6 +56,13 @@ public static bool ThrowException() throw new OverflowException("error"); } + public static IEnumerable ThrowExceptionInIterator(Exception e) + { + yield return 1; + yield return 2; + throw e; + } + public static void ThrowChainedExceptions() { try diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index 83dc907c0..cf653f9f9 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -666,3 +666,23 @@ public string PublicMethod(string echo) } } } + +namespace PlainOldNamespace +{ + public class PlainOldClass + { + public PlainOldClass() { } + + public PlainOldClass(int param) { } + + private readonly byte[] payload = new byte[(int)Math.Pow(2, 20)]; //1 MB + + public void NonGenericMethod() { } + + public void GenericMethod() { } + + public void OverloadedMethod() { } + + public void OverloadedMethod(int param) { } + } +} 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/importtest.py b/src/tests/importtest.py new file mode 100644 index 000000000..fe93764e9 --- /dev/null +++ b/src/tests/importtest.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +import sys +try: + del sys.modules["System.IO"] +except KeyError: + pass + +assert "FileStream" not in globals() +import System.IO +from System.IO import * + +assert "FileStream" in globals() diff --git a/src/tests/test_class.py b/src/tests/test_class.py index 68773508b..612ce442e 100644 --- a/src/tests/test_class.py +++ b/src/tests/test_class.py @@ -281,3 +281,14 @@ def PyCallback(self, self2): testobj.DoCallback() assert testobj.PyCallbackWasCalled assert testobj.SameReference + + +def test_method_inheritance(): + """Ensure that we call the overridden method instead of the one provided in + the base class.""" + + base = Test.BaseClass() + derived = Test.DerivedClass() + + assert base.IsBase() == True + assert derived.IsBase() == False 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..0ba10a80e 100644 --- a/src/tests/test_conversion.py +++ b/src/tests/test_conversion.py @@ -2,11 +2,12 @@ """Test CLR <-> Python type conversions.""" +from __future__ import unicode_literals import System import pytest -from Python.Test import ConversionTest +from Python.Test import ConversionTest, UnicodeString -from ._compat import indexbytes, long, unichr +from ._compat import indexbytes, long, unichr, text_type, PY2, PY3 def test_bool_conversion(): @@ -466,17 +467,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(): @@ -546,6 +536,14 @@ def test_string_conversion(): with pytest.raises(TypeError): ConversionTest().StringField = 1 + + world = UnicodeString() + test_unicode_str = u"안녕" + assert test_unicode_str == text_type(world.value) + assert test_unicode_str == text_type(world.GetString()) + # TODO: not sure what to do for Python 2 here (GH PR #670) + if PY3: + assert test_unicode_str == text_type(world) def test_interface_conversion(): @@ -641,6 +639,8 @@ def test_enum_conversion(): def test_null_conversion(): """Test null conversion.""" + import System + ob = ConversionTest() ob.StringField = None @@ -652,6 +652,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_exceptions.py b/src/tests/test_exceptions.py index f47209f6d..a10d9a183 100644 --- a/src/tests/test_exceptions.py +++ b/src/tests/test_exceptions.py @@ -270,12 +270,6 @@ def test_str_of_exception(): with pytest.raises(FormatException) as cm: Convert.ToDateTime('this will fail') - e = cm.value - # fix for international installation - msg = text_type(e).encode("utf8") - fnd = text_type('System.Convert.ToDateTime').encode("utf8") - assert msg.find(fnd) > -1, msg - def test_python_compat_of_managed_exceptions(): """Test managed exceptions compatible with Python's implementation""" @@ -289,7 +283,8 @@ def test_python_compat_of_managed_exceptions(): assert e.args == (msg,) assert isinstance(e.args, tuple) if PY3: - assert repr(e) == "OverflowException('Simple message',)" + strexp = "OverflowException('Simple message" + assert repr(e)[:len(strexp)] == strexp elif PY2: assert repr(e) == "OverflowException(u'Simple message',)" @@ -345,3 +340,44 @@ def test_chained_exceptions(): assert exc.Message == msg assert exc.__cause__ == exc.InnerException exc = exc.__cause__ + +def test_iteration_exception(): + from Python.Test import ExceptionTest + from System import OverflowException + + exception = OverflowException("error") + + val = ExceptionTest.ThrowExceptionInIterator(exception).__iter__() + assert next(val) == 1 + assert next(val) == 2 + with pytest.raises(OverflowException) as cm: + next(val) + + exc = cm.value + + assert exc == exception + + # after exception is thrown iterator is no longer valid + with pytest.raises(StopIteration): + next(val) + + +def test_iteration_innerexception(): + from Python.Test import ExceptionTest + from System import OverflowException + + exception = System.Exception("message", OverflowException("error")) + + val = ExceptionTest.ThrowExceptionInIterator(exception).__iter__() + assert next(val) == 1 + assert next(val) == 2 + with pytest.raises(OverflowException) as cm: + next(val) + + exc = cm.value + + assert exc == exception.InnerException + + # after exception is thrown iterator is no longer valid + with pytest.raises(StopIteration): + next(val) diff --git a/src/tests/test_import.py b/src/tests/test_import.py index 42cafc4df..25877be15 100644 --- a/src/tests/test_import.py +++ b/src/tests/test_import.py @@ -3,7 +3,7 @@ """Test the import statement.""" import pytest - +import sys def test_relative_missing_import(): """Test that a relative missing import doesn't crash. @@ -11,3 +11,12 @@ def test_relative_missing_import(): Relative import in the site-packages folder""" with pytest.raises(ImportError): from . import _missing_import + + +def test_import_all_on_second_time(): + """Test import all attributes after a normal import without '*'. + Due to import * only allowed at module level, the test body splited + to a module file.""" + from . import importtest + del sys.modules[importtest.__name__] + diff --git a/src/tests/test_method.py b/src/tests/test_method.py index ad182678d..ad678611b 100644 --- a/src/tests/test_method.py +++ b/src/tests/test_method.py @@ -832,3 +832,137 @@ def test_case_sensitive(): with pytest.raises(AttributeError): MethodTest.casesensitive() + +def test_getting_generic_method_binding_does_not_leak_ref_count(): + """Test that managed object is freed after calling generic method. Issue #691""" + + from PlainOldNamespace import PlainOldClass + + import sys + + refCount = sys.getrefcount(PlainOldClass().GenericMethod[str]) + assert refCount == 1 + +def test_getting_generic_method_binding_does_not_leak_memory(): + """Test that managed object is freed after calling generic method. Issue #691""" + + from PlainOldNamespace import PlainOldClass + + import psutil, os, gc, clr + + process = psutil.Process(os.getpid()) + processBytesBeforeCall = process.memory_info().rss + print("\n\nMemory consumption (bytes) at start of test: " + str(processBytesBeforeCall)) + + iterations = 500 + for i in range(iterations): + PlainOldClass().GenericMethod[str] + + gc.collect() + clr.System.GC.Collect() + + processBytesAfterCall = process.memory_info().rss + print("Memory consumption (bytes) at end of test: " + str(processBytesAfterCall)) + processBytesDelta = processBytesAfterCall - processBytesBeforeCall + print("Memory delta: " + str(processBytesDelta)) + + bytesAllocatedPerIteration = pow(2, 20) # 1MB + bytesLeakedPerIteration = processBytesDelta / iterations + + # Allow 50% threshold - this shows the original issue is fixed, which leaks the full allocated bytes per iteration + failThresholdBytesLeakedPerIteration = bytesAllocatedPerIteration / 2 + + assert bytesLeakedPerIteration < failThresholdBytesLeakedPerIteration + +def test_getting_overloaded_method_binding_does_not_leak_ref_count(): + """Test that managed object is freed after calling overloaded method. Issue #691""" + + from PlainOldNamespace import PlainOldClass + + import sys + + refCount = sys.getrefcount(PlainOldClass().OverloadedMethod.Overloads[int]) + assert refCount == 1 + +def test_getting_overloaded_method_binding_does_not_leak_memory(): + """Test that managed object is freed after calling overloaded method. Issue #691""" + + from PlainOldNamespace import PlainOldClass + + import psutil, os, gc, clr + + process = psutil.Process(os.getpid()) + processBytesBeforeCall = process.memory_info().rss + print("\n\nMemory consumption (bytes) at start of test: " + str(processBytesBeforeCall)) + + iterations = 500 + for i in range(iterations): + PlainOldClass().OverloadedMethod.Overloads[int] + + gc.collect() + clr.System.GC.Collect() + + processBytesAfterCall = process.memory_info().rss + print("Memory consumption (bytes) at end of test: " + str(processBytesAfterCall)) + processBytesDelta = processBytesAfterCall - processBytesBeforeCall + print("Memory delta: " + str(processBytesDelta)) + + bytesAllocatedPerIteration = pow(2, 20) # 1MB + bytesLeakedPerIteration = processBytesDelta / iterations + + # Allow 50% threshold - this shows the original issue is fixed, which leaks the full allocated bytes per iteration + failThresholdBytesLeakedPerIteration = bytesAllocatedPerIteration / 2 + + assert bytesLeakedPerIteration < failThresholdBytesLeakedPerIteration + +def test_getting_method_overloads_binding_does_not_leak_ref_count(): + """Test that managed object is freed after calling overloaded method. Issue #691""" + + from PlainOldNamespace import PlainOldClass + + import sys + + refCount = sys.getrefcount(PlainOldClass().OverloadedMethod.Overloads) + assert refCount == 1 + +def test_getting_method_overloads_binding_does_not_leak_memory(): + """Test that managed object is freed after calling overloaded method. Issue #691""" + + from PlainOldNamespace import PlainOldClass + + import psutil, os, gc, clr + + process = psutil.Process(os.getpid()) + processBytesBeforeCall = process.memory_info().rss + print("\n\nMemory consumption (bytes) at start of test: " + str(processBytesBeforeCall)) + + iterations = 500 + for i in range(iterations): + PlainOldClass().OverloadedMethod.Overloads + + gc.collect() + clr.System.GC.Collect() + + processBytesAfterCall = process.memory_info().rss + print("Memory consumption (bytes) at end of test: " + str(processBytesAfterCall)) + processBytesDelta = processBytesAfterCall - processBytesBeforeCall + print("Memory delta: " + str(processBytesDelta)) + + bytesAllocatedPerIteration = pow(2, 20) # 1MB + bytesLeakedPerIteration = processBytesDelta / iterations + + # Allow 50% threshold - this shows the original issue is fixed, which leaks the full allocated bytes per iteration + failThresholdBytesLeakedPerIteration = bytesAllocatedPerIteration / 2 + + assert bytesLeakedPerIteration < failThresholdBytesLeakedPerIteration + +def test_getting_overloaded_constructor_binding_does_not_leak_ref_count(): + """Test that managed object is freed after calling overloaded constructor, constructorbinding.cs mp_subscript. Issue #691""" + + from PlainOldNamespace import PlainOldClass + + import sys + + # simple test + refCount = sys.getrefcount(PlainOldClass.Overloads[int]) + assert refCount == 1 diff --git a/src/tests/test_module.py b/src/tests/test_module.py index 2255ea411..62d79b9ab 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 @@ -367,3 +387,9 @@ def test_assembly_load_thread_safety(): from System.Collections.Generic import Dictionary _ = Dictionary[Guid, DateTime]() ModuleTest.JoinThreads() + +def test_assembly_load_recursion_bug(): + """Test fix for recursion bug documented in #627""" + from System.Configuration import ConfigurationManager + content = dir(ConfigurationManager) + assert len(content) > 5, content diff --git a/src/tests/test_subclass.py b/src/tests/test_subclass.py index 8e862a56d..ab440d429 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,42 @@ def test_derived_class(): assert id(x) == id(ob) -@pytest.mark.skip(reason="FIXME: test randomly pass/fails") +def test_derived_traceback(): + """Test python exception traceback in class derived from managed base""" + class DerivedClass(SubClassTest): + __namespace__ = "Python.Test.traceback" + + def foo(self): + print (xyzname) + return None + + import sys,traceback + ob = DerivedClass() + + # direct call + try: + ob.foo() + assert False + except: + e = sys.exc_info() + assert "xyzname" in str(e[1]) + location = traceback.extract_tb(e[2])[-1] + assert location[2] == "foo" + + # call through managed code + try: + FunctionsTest.test_foo(ob) + assert False + except: + e = sys.exc_info() + assert "xyzname" in str(e[1]) + location = traceback.extract_tb(e[2])[-1] + assert location[2] == "foo" + + 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 +174,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 +185,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 +197,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 +221,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/geninterop/fake_libc_include/crypt.h b/tools/geninterop/fake_libc_include/crypt.h new file mode 100644 index 000000000..3b16d481f --- /dev/null +++ b/tools/geninterop/fake_libc_include/crypt.h @@ -0,0 +1 @@ +#include "features.h" \ No newline at end of file diff --git a/tools/geninterop/geninterop.py b/tools/geninterop/geninterop.py index bf5fdb96b..f8ef8e561 100644 --- a/tools/geninterop/geninterop.py +++ b/tools/geninterop/geninterop.py @@ -173,7 +173,8 @@ def preprocess_python_headers(): "-D", "__attribute__(x)=", "-D", "__inline__=inline", "-D", "__asm__=;#pragma asm", - "-D", "__int64=long long" + "-D", "__int64=long long", + "-D", "_POSIX_THREADS" ] if hasattr(sys, "abiflags"): @@ -185,7 +186,7 @@ def preprocess_python_headers(): defines.extend(("-D", "PYTHON_WITH_WIDE_UNICODE")) python_h = os.path.join(include_py, "Python.h") - cmd = ["clang", "-I"] + include_dirs + defines + ["-E", python_h] + cmd = ["clang", "-pthread", "-I"] + include_dirs + defines + ["-E", python_h] # normalize as the parser doesn't like windows line endings. lines = [] 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