diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 30af44995..ac1d31324 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.3.0 +current_version = 2.4.0.dev0 parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\.(?P[a-z]+)(?P\d+))? serialize = {major}.{minor}.{patch}.{release}{dev} diff --git a/.mention-bot b/.mention-bot new file mode 100644 index 000000000..f07fa548e --- /dev/null +++ b/.mention-bot @@ -0,0 +1,35 @@ +{ + "maxReviewers": 5, + "numFilesToCheck": 10, + "message": "@pullRequester, thanks! @reviewers, please review this.", + "alwaysNotifyForPaths": [ + { + "name": "ghuser", + "files": ["src/js/**/*.js"], + "skipTeamPrs": false + } + ], + "fallbackNotifyForPaths": [ + { + "name": "ghuser", + "files": ["src/js/**/*.js"], + "skipTeamPrs": false + } + ], + "findPotentialReviewers": true, + "fileBlacklist": ["*.md"], + "userBlacklist": [], + "userBlacklistForPR": [], + "requiredOrgs": [], + "actions": ["opened"], + "skipAlreadyAssignedPR": false, + "skipAlreadyMentionedPR": false, + "assignToReviewer": false, + "createReviewRequest": false, + "createComment": true, + "skipTitle": "", + "withLabel": "", + "delayed": false, + "delayedUntil": "3d", + "skipCollaboratorPR": false +} diff --git a/.travis.yml b/.travis.yml index bf336dc9f..d059bdcde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,85 @@ +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.4 + env: *xplat-env + addons: *xplat-addons + + - python: 3.5 + env: *xplat-env + addons: *xplat-addons + + - python: 3.6 + env: *xplat-env + addons: *xplat-addons + + - python: 3.7 + 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.4 + env: *classic-env + + - 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 +91,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 +103,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..fe2d2b172 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -12,31 +12,50 @@ ## 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)) -- Jeff Reback ([@jreback](https://github.com/jreback)) +-   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)) +- 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)) - ([@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..a3816cd0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,74 +5,122 @@ This project adheres to [Semantic Versioning][]. This document follows the conventions laid out in [Keep a CHANGELOG][]. +## [unreleased][] + +### Added + +- Added support for embedding python into dotnet core 2.0 (NetStandard 2.0) +- Added new build system (pythonnet.15.sln) based on dotnetcore-sdk/xplat(crossplatform msbuild). + Currently there two side-by-side build systems that produces the same output (net40) from the same sources. + After a some transition time, current (mono/ msbuild 14.0) build system will be removed. +- NUnit upgraded to 3.7 (eliminates travis-ci random bug) +- Added 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 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 + +- 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]) + +### 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 +128,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 +606,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/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 index 2794298a6..7e859481d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # pythonnet - Python for .NET +[![Join the chat at https://gitter.im/pythonnet/pythonnet](https://badges.gitter.im/pythonnet/pythonnet.svg)](https://gitter.im/pythonnet/pythonnet?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![appveyor shield][]](https://ci.appveyor.com/project/pythonnet/pythonnet/branch/master) [![travis shield][]](https://travis-ci.org/pythonnet/pythonnet) [![codecov shield][]](https://codecov.io/github/pythonnet/pythonnet) @@ -42,7 +44,7 @@ from System.Windows.Forms import Form a `using (Py.GIL()) {/* Your code here */}` block. - Import python modules using `dynamic mod = Py.Import("mod")`, then you can call functions as normal, eg `mod.func(args)`. -- Use `mod.func(args, Py.kw("keywordargname", keywordargvalue))` +- Use `mod.func(args, Py.kw("keywordargname", keywordargvalue))` or `mod.func(args, keywordargname: keywordargvalue)` to apply keyword arguments. - All python objects should be declared as `dynamic` type. - Mathematical operations involving python and literal/managed types must @@ -63,15 +65,14 @@ static void Main(string[] args) double c = np.cos(5) + sin(5); Console.WriteLine(c); - /* this block is temporarily disabled due to regression #249 + dynamic a = np.array(new List { 1, 2, 3 }); Console.WriteLine(a.dtype); - dynamic b = np.array(new List { 6, 5, 4 }, Py.kw("dtype", np.int32)); + dynamic b = np.array(new List { 6, 5, 4 }, dtype: np.int32); Console.WriteLine(b.dtype); Console.WriteLine(a * b); - */ Console.ReadKey(); } } @@ -88,6 +89,10 @@ int32 [ 6. 10. 12.] ``` +Information on installation, FAQ, troubleshooting, debugging, and projects using pythonnet can be found in the Wiki: + +https://github.com/pythonnet/pythonnet/wiki + [appveyor shield]: https://img.shields.io/appveyor/ci/pythonnet/pythonnet/master.svg?label=AppVeyor [codecov shield]: https://img.shields.io/codecov/c/github/pythonnet/pythonnet/master.svg?label=Codecov diff --git a/appveyor.yml b/appveyor.yml index c108801e7..ce345cbf8 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,27 @@ environment: CODECOV_ENV: PYTHON_VERSION, PLATFORM matrix: + - PYTHON_VERSION: 2.7 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.4 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.5 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.6 + BUILD_OPTS: --xplat + - PYTHON_VERSION: 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 +47,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 +57,7 @@ build_script: # Create clean `sdist`. Only used for releases - python setup.py --quiet sdist # Build `wheel` with coverage of `setup.py` - - coverage run setup.py bdist_wheel + - coverage run setup.py bdist_wheel %BUILD_OPTS% test_script: - pip install --no-index --find-links=.\dist\ pythonnet @@ -47,11 +67,11 @@ test_script: on_finish: # Temporary disable multiple upload due to codecov limit of 20 per commit. # https://docs.codecov.io/blog/week-8-2017 - # - coverage xml -i + - coverage xml -i # - codecov --file coverage.xml --flags setup_windows # - codecov --file py.coverage --flags python_tests # - codecov --file cs.coverage --flags embedded_tests - - codecov --flags setup_windows + - codecov --file py.coverage cs.coverage coverage.xml --flags setup_windows artifacts: - path: dist\* diff --git a/ci/appveyor_build_recipe.ps1 b/ci/appveyor_build_recipe.ps1 index 52520f4c2..84e0bc7c6 100644 --- a/ci/appveyor_build_recipe.ps1 +++ b/ci/appveyor_build_recipe.ps1 @@ -11,7 +11,7 @@ if ($env:PLATFORM -eq "x86"){ $env:CONDA_BLD = "$env:CONDA_BLD" + "-x64" } -if ($env:APPVEYOR_PULL_REQUEST_NUMBER -or $env:FORCE_CONDA_BUILD -eq "True") { +if ($env:APPVEYOR_PULL_REQUEST_NUMBER -or $env:APPVEYOR_REPO_TAG_NAME -or $env:FORCE_CONDA_BUILD -eq "True") { # Update PATH, and keep a copy to restore at end of this PowerShell script $old_path = $env:path $env:path = "$env:CONDA_BLD;$env:CONDA_BLD\Scripts;" + $env:path diff --git a/ci/appveyor_run_tests.ps1 b/ci/appveyor_run_tests.ps1 index 4245d1577..b45440fbe 100644 --- a/ci/appveyor_run_tests.ps1 +++ b/ci/appveyor_run_tests.ps1 @@ -11,7 +11,12 @@ if ($FALSE -and $env:PLATFORM -eq "x86"){ # Executable paths for OpenCover # Note if OpenCover fails, it won't affect the exit codes. $OPENCOVER = Resolve-Path .\packages\OpenCover.*\tools\OpenCover.Console.exe -$CS_RUNNER = Resolve-Path .\packages\NUnit.*\tools\"$CS_RUNNER".exe +if ($env:BUILD_OPTS -eq "--xplat"){ + $CS_RUNNER = Resolve-Path $env:USERPROFILE\.nuget\packages\nunit.consolerunner\*\tools\"$CS_RUNNER".exe +} +else{ + $CS_RUNNER = Resolve-Path .\packages\NUnit.*\tools\"$CS_RUNNER".exe +} $PY = Get-Command python # Can't use ".\build\*\Python.EmbeddingTest.dll". Missing framework files. @@ -39,6 +44,23 @@ if ($CS_STATUS -ne 0) { Write-Host "Embedded tests failed" -ForegroundColor "Red" } +if ($env:BUILD_OPTS -eq "--xplat"){ + if ($env:PLATFORM -eq "x64") { + $DOTNET_CMD = "dotnet" + } + else{ + $DOTNET_CMD = "c:\Program Files (x86)\dotnet\dotnet" + } + + # Run Embedded tests for netcoreapp2.0 (OpenCover currently does not supports dotnet core) + Write-Host ("Starting embedded tests for netcoreapp2.0") -ForegroundColor "Green" + &$DOTNET_CMD .\src\embed_tests\bin\netcoreapp2.0_publish\Python.EmbeddingTest.dll + $CS_STATUS = $LastExitCode + if ($CS_STATUS -ne 0) { + Write-Host "Embedded tests for netcoreapp2.0 failed" -ForegroundColor "Red" + } +} + # Set exit code to fail if either Python or Embedded tests failed if ($PYTHON_STATUS -ne 0 -or $CS_STATUS -ne 0) { Write-Host "Tests failed" -ForegroundColor "Red" diff --git a/conda.recipe/bld.bat b/conda.recipe/bld.bat index 1495f877d..27b55f2de 100644 --- a/conda.recipe/bld.bat +++ b/conda.recipe/bld.bat @@ -1,6 +1,6 @@ :: build it :: set path to modern MSBuild -set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH% +set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;C:\Program Files (x86)\MSBuild\15.0\Bin;%PATH% %PYTHON% setup.py install diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index 98602481f..3641185bb 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -1,6 +1,6 @@ package: name: pythonnet - version: "2.3.0" + version: "2.4.0.dev0" build: skip: True # [not win] diff --git a/demo/DynamicGrid.py b/demo/DynamicGrid.py new file mode 100644 index 000000000..951a6c248 --- /dev/null +++ b/demo/DynamicGrid.py @@ -0,0 +1,23 @@ +import clr +import sys +if sys.platform.lower() not in ['cli','win32']: + print("only windows is supported for wpf") +clr.AddReference(r"wpf\PresentationFramework") +from System.IO import StreamReader +from System.Windows.Markup import XamlReader +from System.Threading import Thread, ThreadStart, ApartmentState +from System.Windows import Application, Window + + +class MyWindow(Window): + def __init__(self): + stream = StreamReader("DynamicGrid.xaml") + window = XamlReader.Load(stream.BaseStream) + Application().Run(window) + + +if __name__ == '__main__': + thread = Thread(ThreadStart(MyWindow)) + thread.SetApartmentState(ApartmentState.STA) + thread.Start() + thread.Join() diff --git a/demo/DynamicGrid.xaml b/demo/DynamicGrid.xaml new file mode 100644 index 000000000..3c82eb16d --- /dev/null +++ b/demo/DynamicGrid.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/pythonnet.15.sln b/pythonnet.15.sln new file mode 100644 index 000000000..f2015e480 --- /dev/null +++ b/pythonnet.15.sln @@ -0,0 +1,194 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Runtime.15", "src/runtime/Python.Runtime.15.csproj", "{2759F4FF-716B-4828-916F-50FA86613DFC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.EmbeddingTest.15", "src/embed_tests/Python.EmbeddingTest.15.csproj", "{66B8D01A-9906-452A-B09E-BF75EA76468F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "clrmodule.15", "src/clrmodule/clrmodule.15.csproj", "{E08678D4-9A52-4AD5-B63D-8EBC7399981B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.15", "src/console/Console.15.csproj", "{CDAD305F-8E72-492C-A314-64CF58D472A0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Test.15", "src/testing/Python.Test.15.csproj", "{F94B547A-E97E-4500-8D53-B4D64D076E5F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + DebugMono|x64 = DebugMono|x64 + DebugMono|x86 = DebugMono|x86 + DebugMonoPY3|x64 = DebugMonoPY3|x64 + DebugMonoPY3|x86 = DebugMonoPY3|x86 + DebugWin|x64 = DebugWin|x64 + DebugWin|x86 = DebugWin|x86 + DebugWinPY3|x64 = DebugWinPY3|x64 + DebugWinPY3|x86 = DebugWinPY3|x86 + ReleaseMono|x64 = ReleaseMono|x64 + ReleaseMono|x86 = ReleaseMono|x86 + ReleaseMonoPY3|x64 = ReleaseMonoPY3|x64 + ReleaseMonoPY3|x86 = ReleaseMonoPY3|x86 + ReleaseWin|x64 = ReleaseWin|x64 + ReleaseWin|x86 = ReleaseWin|x86 + ReleaseWinPY3|x64 = ReleaseWinPY3|x64 + ReleaseWinPY3|x86 = ReleaseWinPY3|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x64.ActiveCfg = DebugMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x64.Build.0 = DebugMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x86.ActiveCfg = DebugMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x86.Build.0 = DebugMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x64.ActiveCfg = DebugWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x64.Build.0 = DebugWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x86.ActiveCfg = DebugWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x86.Build.0 = DebugWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x64.Build.0 = DebugWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x86.Build.0 = DebugWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x64.ActiveCfg = ReleaseMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x64.Build.0 = ReleaseMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x86.ActiveCfg = ReleaseMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x86.Build.0 = ReleaseMono|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x64.ActiveCfg = ReleaseWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x64.Build.0 = ReleaseWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x86.ActiveCfg = ReleaseWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x86.Build.0 = ReleaseWin|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|Any CPU + {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|Any CPU + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x64.ActiveCfg = DebugMono|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x64.Build.0 = DebugMono|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x86.ActiveCfg = DebugMono|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x86.Build.0 = DebugMono|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x64.ActiveCfg = DebugWin|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x64.Build.0 = DebugWin|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x86.ActiveCfg = DebugWin|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x86.Build.0 = DebugWin|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 + {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMono|x64.ActiveCfg = DebugMono|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMono|x86.ActiveCfg = DebugMono|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x64.ActiveCfg = DebugWin|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x64.Build.0 = DebugWin|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x86.ActiveCfg = DebugWin|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x86.Build.0 = DebugWin|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 + {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x64.ActiveCfg = DebugMono|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x64.Build.0 = DebugMono|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x86.ActiveCfg = DebugMono|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x86.Build.0 = DebugMono|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x64.ActiveCfg = DebugWin|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x64.Build.0 = DebugWin|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x86.ActiveCfg = DebugWin|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x86.Build.0 = DebugWin|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 + {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x64.ActiveCfg = DebugMono|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x64.Build.0 = DebugMono|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x86.ActiveCfg = DebugMono|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x86.Build.0 = DebugMono|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x64.ActiveCfg = DebugWin|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x64.Build.0 = DebugWin|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x86.ActiveCfg = DebugWin|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x86.Build.0 = DebugWin|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 + {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A6347B90-BBE6-4E45-90BF-1BD8B76069E3} + EndGlobalSection +EndGlobal diff --git a/requirements.txt b/requirements.txt index bcceedf25..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..1b6f07ea6 100644 --- a/setup.py +++ b/setup.py @@ -14,14 +14,15 @@ 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 DEVTOOLS = "MsDev" if sys.platform == "win32" else "Mono" @@ -39,6 +40,12 @@ kits_suffix = os.path.join("bin", ARCH) WIN_SDK_KEYS = ( + RegKey(sdk_name="Windows Kit 10.0", key=kits_root, + value_name="KitsRoot10", suffix=os.path.join("bin", "10.0.16299.0", ARCH)), + + RegKey(sdk_name="Windows Kit 10.0", key=kits_root, + value_name="KitsRoot10", suffix=os.path.join("bin", "10.0.15063.0", ARCH)), + RegKey(sdk_name="Windows Kit 10.0", key=kits_root, value_name="KitsRoot10", suffix=kits_suffix), @@ -65,6 +72,9 @@ ) VS_KEYS = ( + RegKey(sdk_name="MSBuild 15", key=vs_root.format("15.0"), + value_name="MSBuildToolsPath", suffix=""), + RegKey(sdk_name="MSBuild 14", key=vs_root.format("14.0"), value_name="MSBuildToolsPath", suffix=""), @@ -128,9 +138,28 @@ def _get_long_description(): except ImportError: return '.Net and Mono integration for Python' +def _update_xlat_devtools(): + global DEVTOOLS + if DEVTOOLS == "MsDev": + DEVTOOLS = "MsDev15" + elif DEVTOOLS == "Mono": + DEVTOOLS = "dotnet" class BuildExtPythonnet(build_ext.build_ext): + user_options = build_ext.build_ext.user_options + [ + ('xplat', None, None) + ] + def initialize_options(self): + build_ext.build_ext.initialize_options(self) + self.xplat = None + + def finalize_options(self): + build_ext.build_ext.finalize_options(self) + def build_extension(self, ext): + if self.xplat: + _update_xlat_devtools() + """Builds the .pyd file using msbuild or xbuild""" if ext.name != "clr": return build_ext.build_ext.build_extension(self, ext) @@ -161,7 +190,7 @@ def build_extension(self, ext): if CONFIG == "Debug": defines.extend(["DEBUG", "TRACE"]) - if sys.platform != "win32" and DEVTOOLS == "Mono": + if sys.platform != "win32" and (DEVTOOLS == "Mono" or DEVTOOLS == "dotnet"): on_darwin = sys.platform == "darwin" defines.append("MONO_OSX" if on_darwin else "MONO_LINUX") @@ -193,20 +222,33 @@ def build_extension(self, ext): if DEVTOOLS == "MsDev": _xbuild = '"{0}"'.format(self._find_msbuild_tool("msbuild.exe")) _config = "{0}Win".format(CONFIG) - + _solution_file = 'pythonnet.sln' + _custom_define_constants = False + elif DEVTOOLS == "MsDev15": + _xbuild = '"{0}"'.format(self._find_msbuild_tool_15()) + _config = "{0}Win".format(CONFIG) + _solution_file = 'pythonnet.15.sln' + _custom_define_constants = True elif DEVTOOLS == "Mono": - _xbuild = "xbuild" + _xbuild = 'xbuild' _config = "{0}Mono".format(CONFIG) + _solution_file = 'pythonnet.sln' + _custom_define_constants = False + elif DEVTOOLS == "dotnet": + _xbuild = 'dotnet msbuild' + _config = "{0}Mono".format(CONFIG) + _solution_file = 'pythonnet.15.sln' + _custom_define_constants = True else: raise NotImplementedError( - "DevTool {0} not supported (use MsDev/Mono)".format(DEVTOOLS)) + "DevTool {0} not supported (use MsDev/MsDev15/Mono/dotnet)".format(DEVTOOLS)) cmd = [ _xbuild, - 'pythonnet.sln', + _solution_file, '/p:Configuration={}'.format(_config), '/p:Platform={}'.format(ARCH), - '/p:DefineConstants="{}"'.format(','.join(defines)), + '/p:{}DefineConstants="{}"'.format('Custom' if _custom_define_constants else '','%3B'.join(defines)), '/p:PythonBuildDir="{}"'.format(os.path.abspath(dest_dir)), '/p:PythonInteropFile="{}"'.format(os.path.basename(interop_file)), '/verbosity:{}'.format(VERBOSITY), @@ -217,15 +259,17 @@ def build_extension(self, ext): cmd.append('/p:PythonManifest="{0}"'.format(manifest)) self.debug_print("Building: {0}".format(" ".join(cmd))) - use_shell = True if DEVTOOLS == "Mono" else False + use_shell = True if DEVTOOLS == "Mono" or DEVTOOLS == "dotnet" else False + subprocess.check_call(" ".join(cmd + ["/t:Clean"]), shell=use_shell) subprocess.check_call(" ".join(cmd + ["/t:Build"]), shell=use_shell) - - if DEVTOOLS == "Mono": + if DEVTOOLS == "MsDev15" or DEVTOOLS == "dotnet": + subprocess.check_call(" ".join(cmd + ['"/t:Console_15:publish;Python_EmbeddingTest_15:publish"', "/p:TargetFramework=netcoreapp2.0"]), shell=use_shell) + if DEVTOOLS == "Mono" or DEVTOOLS == "dotnet": self._build_monoclr() def _get_manifest(self, build_dir): - if DEVTOOLS != "MsDev": + if DEVTOOLS != "MsDev" and DEVTOOLS != "MsDev15": return mt = self._find_msbuild_tool("mt.exe", use_windows_sdk=True) manifest = os.path.abspath(os.path.join(build_dir, "app.manifest")) @@ -236,7 +280,13 @@ def _get_manifest(self, build_dir): 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) @@ -258,22 +308,54 @@ def _build_monoclr(self): def _install_packages(self): """install packages using nuget""" - nuget = os.path.join("tools", "nuget", "nuget.exe") - use_shell = False - if DEVTOOLS == "Mono": - nuget = "mono {0}".format(nuget) - use_shell = True + use_shell = DEVTOOLS == "Mono" or DEVTOOLS == "dotnet" + + if DEVTOOLS == "MsDev15" or DEVTOOLS == "dotnet": + if DEVTOOLS == "MsDev15": + _config = "{0}Win".format(CONFIG) + elif DEVTOOLS == "dotnet": + _config = "{0}Mono".format(CONFIG) + + cmd = "dotnet msbuild /t:Restore pythonnet.15.sln /p:Configuration={0} /p:Platform={1}".format(_config, ARCH) + self.debug_print("Updating packages with xplat: {0}".format(cmd)) + subprocess.check_call(cmd, shell=use_shell) + else: + nuget = os.path.join("tools", "nuget", "nuget.exe") - cmd = "{0} update -self".format(nuget) - self.debug_print("Updating NuGet: {0}".format(cmd)) - subprocess.check_call(cmd, shell=use_shell) + if DEVTOOLS == "Mono": + nuget = "mono {0}".format(nuget) - cmd = "{0} restore pythonnet.sln -o packages".format(nuget) - self.debug_print("Installing packages: {0}".format(cmd)) - subprocess.check_call(cmd, shell=use_shell) + cmd = "{0} update -self".format(nuget) + self.debug_print("Updating NuGet: {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: @@ -316,6 +398,20 @@ def _find_msbuild_tool(self, tool="msbuild.exe", use_windows_sdk=False): raise RuntimeError("{0} could not be found".format(tool)) + def _find_msbuild_tool_15(self): + """Return full path to one of the Microsoft build tools""" + try: + basePathes = subprocess.check_output( + ["tools\\vswhere\\vswhere.exe", "-latest", + "-version", "[15.0, 16.0)", + "-requires", "Microsoft.Component.MSBuild", + "-property", "InstallationPath"]).splitlines() + if len(basePathes): + return os.path.join(basePathes[0].decode(sys.stdout.encoding or "utf-8"), "MSBuild", "15.0", "Bin", "MSBuild.exe") + else: + raise RuntimeError("MSBuild >=15.0 could not be found.") + except subprocess.CalledProcessError as e: + raise RuntimeError("MSBuild >=15.0 could not be found. {0}".format(e.output)) class InstallLibPythonnet(install_lib.install_lib): def install(self): @@ -353,8 +449,39 @@ def run(self): return install_data.install_data.run(self) +class InstallPythonnet(install.install): + user_options = install.install.user_options + [ + ('xplat', None, None) + ] + def initialize_options(self): + install.install.initialize_options(self) + self.xplat = None -############################################################################### + def finalize_options(self): + install.install.finalize_options(self) + + def run(self): + if self.xplat: + _update_xlat_devtools() + return install.install.run(self) + +class BDistWheelPythonnet(bdist_wheel.bdist_wheel): + user_options = bdist_wheel.bdist_wheel.user_options + [ + ('xplat', None, None) + ] + def initialize_options(self): + bdist_wheel.bdist_wheel.initialize_options(self) + self.xplat = None + + def finalize_options(self): + bdist_wheel.bdist_wheel.finalize_options(self) + + def run(self): + if self.xplat: + _update_xlat_devtools() + return bdist_wheel.bdist_wheel.run(self) + + ############################################################################### setupdir = os.path.dirname(__file__) if setupdir: os.chdir(setupdir) @@ -365,7 +492,7 @@ def run(self): setup( name="pythonnet", - version="2.3.0", + version="2.4.0.dev0", description=".Net and Mono integration for Python", url='https://pythonnet.github.io/', license='MIT', @@ -382,9 +509,11 @@ def run(self): ]), ], cmdclass={ + "install": InstallPythonnet, "build_ext": BuildExtPythonnet, "install_lib": InstallLibPythonnet, "install_data": InstallDataPythonnet, + "bdist_wheel": BDistWheelPythonnet }, classifiers=[ 'Development Status :: 5 - Production/Stable', @@ -394,10 +523,10 @@ def run(self): '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', + 'Programming Language :: Python :: 3.7', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', 'Operating System :: MacOS :: MacOS X', diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs index 24ba26862..c164e75d6 100644 --- a/src/SharedAssemblyInfo.cs +++ b/src/SharedAssemblyInfo.cs @@ -25,4 +25,4 @@ // Version Information. Keeping it simple. May need to revisit for Nuget // See: https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/ // AssemblyVersion can only be numeric -[assembly: AssemblyVersion("2.3.0")] +[assembly: AssemblyVersion("2.4.0")] diff --git a/src/clrmodule/ClrModule.cs b/src/clrmodule/ClrModule.cs index d24376a1f..3bb3a533b 100644 --- a/src/clrmodule/ClrModule.cs +++ b/src/clrmodule/ClrModule.cs @@ -53,7 +53,7 @@ public static void initclr() { #if USE_PYTHON_RUNTIME_VERSION // Has no effect until SNK works. Keep updated anyways. - Version = new Version("2.3.0"), + Version = new Version("2.4.0"), #endif CultureInfo = CultureInfo.InvariantCulture }; diff --git a/src/clrmodule/clrmodule.15.csproj b/src/clrmodule/clrmodule.15.csproj new file mode 100644 index 000000000..2585ffdd2 --- /dev/null +++ b/src/clrmodule/clrmodule.15.csproj @@ -0,0 +1,95 @@ + + + + + + net40 + x64;x86 + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + clrmodule + clrmodule + clrmodule + 2.4.0 + false + false + false + false + false + false + bin\clrmodule.xml + bin\ + false + 1591 + ..\..\ + $(SolutionDir)\bin\ + $(PythonBuildDir)\$(TargetFramework)\ + 6 + prompt + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);TRACE;DEBUG + + + + x86 + + + x64 + + + + false + full + + + true + pdbonly + + + + $(DefineConstants);PYTHON2;TRACE;DEBUG + + + $(DefineConstants);PYTHON2 + + + $(DefineConstants);PYTHON2;TRACE;DEBUG + + + $(DefineConstants);PYTHON2 + + + $(DefineConstants);PYTHON3;TRACE;DEBUG + + + $(DefineConstants);PYTHON3 + + + $(DefineConstants);PYTHON3;TRACE;DEBUG + + + $(DefineConstants);PYTHON3 + + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + + diff --git a/src/console/Console.15.csproj b/src/console/Console.15.csproj new file mode 100644 index 000000000..ec5008036 --- /dev/null +++ b/src/console/Console.15.csproj @@ -0,0 +1,94 @@ + + + + net40;netcoreapp2.0 + x64;x86 + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + Exe + nPython + Python.Runtime + nPython + 2.4.0 + false + false + false + false + false + false + bin\ + false + $(OutputPath)\$(AssemblyName).xml + $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml + 1591 + ..\..\ + $(SolutionDir)\bin\ + $(PythonBuildDir)\$(TargetFramework)\ + 6 + python-clear.ico + prompt + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);TRACE;DEBUG + $(NuGetPackageRoot)\microsoft.targetingpack.netframework.v4.5\1.0.1\lib\net45\ + + + x86 + + + x64 + + + + false + full + + + true + pdbonly + + + true + false + full + + + true + true + portable + + + + $(DefineConstants);DEBUG;TRACE + + + $(DefineConstants) + + + + $(PythonManifest) + + + + + + + Properties\SharedAssemblyInfo.cs + + + + + + Python.Runtime.dll + + + + + + + + + + + + diff --git a/src/console/pythonconsole.cs b/src/console/pythonconsole.cs index e9bb31e69..912e9bb0d 100644 --- a/src/console/pythonconsole.cs +++ b/src/console/pythonconsole.cs @@ -16,8 +16,9 @@ namespace Python.Runtime /// public sealed class PythonConsole { +#if NET40 private static AssemblyLoader assemblyLoader = new AssemblyLoader(); - +#endif private PythonConsole() { } @@ -25,9 +26,11 @@ private PythonConsole() [STAThread] public static int Main(string[] args) { + // Only net40 is capable to safely inject python.runtime.dll into resources. +#if NET40 // reference the static assemblyLoader to stop it being optimized away AssemblyLoader a = assemblyLoader; - +#endif string[] cmd = Environment.GetCommandLineArgs(); PythonEngine.Initialize(); @@ -37,6 +40,7 @@ public static int Main(string[] args) return i; } +#if NET40 // Register a callback function to load embedded assemblies. // (Python.Runtime.dll is included as a resource) private sealed class AssemblyLoader @@ -73,5 +77,6 @@ public AssemblyLoader() }; } } +#endif } } diff --git a/src/embed_tests/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.15.csproj b/src/embed_tests/Python.EmbeddingTest.15.csproj new file mode 100644 index 000000000..a741a589e --- /dev/null +++ b/src/embed_tests/Python.EmbeddingTest.15.csproj @@ -0,0 +1,116 @@ + + + + + net40;netcoreapp2.0 + x64;x86 + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + Exe + false + Python.EmbeddingTest + Python.EmbeddingTest + Python.EmbeddingTest + 2.4.0 + false + false + false + false + bin\ + false + $(OutputPath)\$(AssemblyName).xml + $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml + 1591 + ..\..\ + $(SolutionDir)\bin\ + $(OutputPath)\$(TargetFramework)_publish + 6 + prompt + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);NETCOREAPP + $(DefineConstants);NETSTANDARD + $(DefineConstants);TRACE;DEBUG + $(NuGetPackageRoot)\microsoft.targetingpack.netframework.v4.5\1.0.1\lib\net45\ + + + x86 + + + x64 + + + + false + full + + + true + pdbonly + + + true + false + full + + + true + true + portable + + + + $(DefineConstants);DEBUG;TRACE + + + $(DefineConstants) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + + diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj index 9c417cd27..6aa48becc 100644 --- a/src/embed_tests/Python.EmbeddingTest.csproj +++ b/src/embed_tests/Python.EmbeddingTest.csproj @@ -70,8 +70,8 @@ - - ..\..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll + + ..\..\packages\NUnit.3.7.1\lib\net40\nunit.framework.dll @@ -84,19 +84,28 @@ + + + + + + + + + @@ -114,6 +123,6 @@ - + diff --git a/src/embed_tests/TestConverter.cs b/src/embed_tests/TestConverter.cs new file mode 100644 index 000000000..caaec311b --- /dev/null +++ b/src/embed_tests/TestConverter.cs @@ -0,0 +1,48 @@ +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class TestConverter + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + [Test] + public void TestConvertSingleToManaged( + [Values(float.PositiveInfinity, float.NegativeInfinity, float.MinValue, float.MaxValue, float.NaN, + float.Epsilon)] float testValue) + { + var pyFloat = new PyFloat(testValue); + + object convertedValue; + var converted = Converter.ToManaged(pyFloat.Handle, typeof(float), out convertedValue, false); + + Assert.IsTrue(converted); + Assert.IsTrue(((float) convertedValue).Equals(testValue)); + } + + [Test] + public void TestConvertDoubleToManaged( + [Values(double.PositiveInfinity, double.NegativeInfinity, double.MinValue, double.MaxValue, double.NaN, + double.Epsilon)] double testValue) + { + var pyFloat = new PyFloat(testValue); + + object convertedValue; + var converted = Converter.ToManaged(pyFloat.Handle, typeof(double), out convertedValue, false); + + Assert.IsTrue(converted); + Assert.IsTrue(((double) convertedValue).Equals(testValue)); + } + } +} diff --git a/src/embed_tests/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/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/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..49c15a3a1 --- /dev/null +++ b/src/embed_tests/TestPyScope.cs @@ -0,0 +1,372 @@ +using System; +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class PyScopeTest + { + private PyScope ps; + + [SetUp] + public void SetUp() + { + using (Py.GIL()) + { + ps = Py.CreateScope("test"); + } + } + + [TearDown] + public void Dispose() + { + using (Py.GIL()) + { + ps.Dispose(); + ps = null; + } + } + + /// + /// Eval a Python expression and obtain its return value. + /// + [Test] + public void TestEval() + { + using (Py.GIL()) + { + ps.Set("a", 1); + var result = ps.Eval("a + 2"); + Assert.AreEqual(3, result); + } + } + + /// + /// Exec Python statements and obtain the variables created. + /// + [Test] + public void TestExec() + { + using (Py.GIL()) + { + ps.Set("bb", 100); //declare a global variable + ps.Set("cc", 10); //declare a local variable + ps.Exec("aa = bb + cc + 3"); + var result = ps.Get("aa"); + Assert.AreEqual(113, result); + } + } + + /// + /// Compile an expression into an ast object; + /// Execute the ast and obtain its return value. + /// + [Test] + public void TestCompileExpression() + { + using (Py.GIL()) + { + ps.Set("bb", 100); //declare a global variable + ps.Set("cc", 10); //declare a local variable + PyObject script = PythonEngine.Compile("bb + cc + 3", "", RunFlagType.Eval); + var result = ps.Execute(script); + Assert.AreEqual(113, result); + } + } + + /// + /// Compile Python statements into an ast object; + /// Execute the ast; + /// Obtain the local variables created. + /// + [Test] + public void TestCompileStatements() + { + using (Py.GIL()) + { + ps.Set("bb", 100); //declare a global variable + ps.Set("cc", 10); //declare a local variable + PyObject script = PythonEngine.Compile("aa = bb + cc + 3", "", RunFlagType.File); + ps.Execute(script); + var result = ps.Get("aa"); + Assert.AreEqual(113, result); + } + } + + /// + /// Create a function in the scope, then the function can read variables in the scope. + /// It cannot write the variables unless it uses the 'global' keyword. + /// + [Test] + public void TestScopeFunction() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + ps.Set("cc", 10); + ps.Exec( + "def func1():\n" + + " bb = cc + 10\n"); + dynamic func1 = ps.Get("func1"); + func1(); //call the function, it can be called any times + var result = ps.Get("bb"); + Assert.AreEqual(100, result); + + ps.Set("bb", 100); + ps.Set("cc", 10); + ps.Exec( + "def func2():\n" + + " global bb\n" + + " bb = cc + 10\n"); + dynamic func2 = ps.Get("func2"); + func2(); + result = ps.Get("bb"); + Assert.AreEqual(20, result); + } + } + + /// + /// Create a class in the scope, the class can read variables in the scope. + /// Its methods can write the variables with the help of 'global' keyword. + /// + [Test] + public void TestScopeClass() + { + using (Py.GIL()) + { + dynamic _ps = ps; + _ps.bb = 100; + ps.Exec( + "class Class1():\n" + + " def __init__(self, value):\n" + + " self.value = value\n" + + " def call(self, arg):\n" + + " return self.value + bb + arg\n" + //use scope variables + " def update(self, arg):\n" + + " global bb\n" + + " bb = self.value + arg\n" //update scope variable + ); + dynamic obj1 = _ps.Class1(20); + var result = obj1.call(10).As(); + Assert.AreEqual(130, result); + + obj1.update(10); + result = ps.Get("bb"); + Assert.AreEqual(30, result); + } + } + + /// + /// Import a python module into the session. + /// Equivalent to the Python "import" statement. + /// + [Test] + public void TestImportModule() + { + using (Py.GIL()) + { + dynamic sys = ps.Import("sys"); + Assert.IsTrue(ps.Contains("sys")); + + ps.Exec("sys.attr1 = 2"); + var value1 = ps.Eval("sys.attr1"); + var value2 = sys.attr1.As(); + Assert.AreEqual(2, value1); + Assert.AreEqual(2, value2); + + //import as + ps.Import("sys", "sys1"); + Assert.IsTrue(ps.Contains("sys1")); + } + } + + /// + /// Create a scope and import variables from a scope, + /// exec Python statements in the scope then discard it. + /// + [Test] + public void TestImportScope() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + ps.Set("cc", 10); + + using (var scope = Py.CreateScope()) + { + scope.Import(ps, "ps"); + scope.Exec("aa = ps.bb + ps.cc + 3"); + var result = scope.Get("aa"); + Assert.AreEqual(113, result); + } + + Assert.IsFalse(ps.Contains("aa")); + } + } + + /// + /// Create a scope and import variables from a scope, + /// exec Python statements in the scope then discard it. + /// + [Test] + public void TestImportAllFromScope() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + ps.Set("cc", 10); + + using (var scope = ps.NewScope()) + { + scope.Exec("aa = bb + cc + 3"); + var result = scope.Get("aa"); + Assert.AreEqual(113, result); + } + + Assert.IsFalse(ps.Contains("aa")); + } + } + + /// + /// Create a scope and import variables from a scope, + /// call the function imported. + /// + [Test] + public void TestImportScopeFunction() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + ps.Set("cc", 10); + ps.Exec( + "def func1():\n" + + " return cc + bb\n"); + + using (PyScope scope = ps.NewScope()) + { + //'func1' is imported from the origion scope + scope.Exec( + "def func2():\n" + + " return func1() - cc - bb\n"); + dynamic func2 = scope.Get("func2"); + + var result1 = func2().As(); + Assert.AreEqual(0, result1); + + scope.Set("cc", 20);//it has no effect on the globals of 'func1' + var result2 = func2().As(); + Assert.AreEqual(-10, result2); + scope.Set("cc", 10); //rollback + + ps.Set("cc", 20); + var result3 = func2().As(); + Assert.AreEqual(10, result3); + ps.Set("cc", 10); //rollback + } + } + } + + /// + /// Import a python module into the session with a new name. + /// Equivalent to the Python "import .. as .." statement. + /// + [Test] + public void TestImportScopeByName() + { + using (Py.GIL()) + { + ps.Set("bb", 100); + + using (var scope = Py.CreateScope()) + { + scope.ImportAll("test"); + //scope.ImportModule("test"); + + Assert.IsTrue(scope.Contains("bb")); + } + } + } + + /// + /// Use the locals() and globals() method just like in python module + /// + [Test] + public void TestVariables() + { + (ps.Variables() as dynamic)["ee"] = new PyInt(200); + var a0 = ps.Get("ee"); + Assert.AreEqual(200, a0); + + ps.Exec("locals()['ee'] = 210"); + var a1 = ps.Get("ee"); + Assert.AreEqual(210, a1); + + ps.Exec("globals()['ee'] = 220"); + var a2 = ps.Get("ee"); + Assert.AreEqual(220, a2); + + using (var item = ps.Variables()) + { + item["ee"] = new PyInt(230); + } + var a3 = ps.Get("ee"); + Assert.AreEqual(230, a3); + } + + /// + /// Share a pyscope by multiple threads. + /// + [Test] + public void TestThread() + { + //After the proposal here https://github.com/pythonnet/pythonnet/pull/419 complished, + //the BeginAllowThreads statement blow and the last EndAllowThreads statement + //should be removed. + dynamic _ps = ps; + var ts = PythonEngine.BeginAllowThreads(); + using (Py.GIL()) + { + _ps.res = 0; + _ps.bb = 100; + _ps.th_cnt = 0; + //add function to the scope + //can be call many times, more efficient than ast + ps.Exec( + "def update():\n" + + " global res, th_cnt\n" + + " res += bb + 1\n" + + " th_cnt += 1\n" + ); + } + int th_cnt = 3; + for (int i =0; i< th_cnt; i++) + { + System.Threading.Thread th = new System.Threading.Thread(()=> + { + using (Py.GIL()) + { + //ps.GetVariable("update")(); //call the scope function dynamicly + _ps.update(); + } + }); + th.Start(); + } + //equivalent to Thread.Join, make the main thread join the GIL competition + int cnt = 0; + while(cnt != th_cnt) + { + using (Py.GIL()) + { + cnt = ps.Get("th_cnt"); + } + System.Threading.Thread.Sleep(10); + } + using (Py.GIL()) + { + var result = ps.Get("res"); + Assert.AreEqual(101* th_cnt, result); + } + PythonEngine.EndAllowThreads(ts); + } + } +} diff --git a/src/embed_tests/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..a4ef86913 --- /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..794645994 --- /dev/null +++ b/src/runtime/Python.Runtime.15.csproj @@ -0,0 +1,140 @@ + + + + net40;netstandard2.0 + AnyCPU + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + net45 + Python.Runtime + Python.Runtime + Python.Runtime + 2.4.0 + false + false + false + false + false + false + bin\ + false + $(OutputPath)\$(AssemblyName).xml + $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml + 1591;NU1701 + ..\..\ + $(SolutionDir)\bin\ + $(PythonBuildDir)\$(TargetFramework)\ + 6 + True + ..\pythonnet.snk + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);NETSTANDARD + $(DefineConstants);TRACE;DEBUG + $(NuGetPackageRoot)\microsoft.targetingpack.netframework.v4.5\1.0.1\lib\net45\ + $(PYTHONNET_PY2_VERSION) + PYTHON27 + $(PYTHONNET_PY3_VERSION) + 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);TRACE;DEBUG + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonMonoDefineConstants);TRACE;DEBUG + + + $(DefineConstants);PYTHON2;$(Python2Version);$(PythonWinDefineConstants) + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonWinDefineConstants) + + + $(DefineConstants);PYTHON2;$(Python2Version);$(PythonWinDefineConstants);TRACE;DEBUG + + + $(DefineConstants);PYTHON3;$(Python3Version);$(PythonWinDefineConstants);TRACE;DEBUG + + + + + + + + + + + + + + + + + + + + + + clr.py + + + + + + + + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + + + + + diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 8580b7f61..fc155ca91 100644 --- a/src/runtime/Python.Runtime.csproj +++ b/src/runtime/Python.Runtime.csproj @@ -20,19 +20,21 @@ false ..\pythonnet.snk - + + + PYTHON2;PYTHON27;UCS4 true pdbonly - PYTHON3;PYTHON36;UCS4 + PYTHON3;PYTHON37;UCS4 true pdbonly @@ -44,7 +46,7 @@ true - PYTHON3;PYTHON36;UCS4;TRACE;DEBUG + PYTHON3;PYTHON37;UCS4;TRACE;DEBUG false full @@ -54,7 +56,7 @@ pdbonly - PYTHON3;PYTHON36;UCS2 + PYTHON3;PYTHON37;UCS2 true pdbonly @@ -66,7 +68,7 @@ true - PYTHON3;PYTHON36;UCS2;TRACE;DEBUG + PYTHON3;PYTHON37;UCS2;TRACE;DEBUG false full @@ -127,6 +129,7 @@ + @@ -135,16 +138,17 @@ + - + @@ -161,6 +165,6 @@ - + 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..11c67bf82 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 { @@ -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 @@ -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..7632816d1 100644 --- a/src/runtime/delegatemanager.cs +++ b/src/runtime/delegatemanager.cs @@ -195,6 +195,10 @@ public Dispatcher(IntPtr target, Type dtype) ~Dispatcher() { + // We needs to disable Finalizers until it's valid implementation. + // Current implementation can produce low probability floating bugs. + return; + // Note: the managed GC thread can run and try to free one of // these *after* the Python runtime has been finalized! if (Runtime.Py_IsInitialized() > 0) diff --git a/src/runtime/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/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..5e800c36f 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -279,7 +279,7 @@ 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); + var pynargs = (int)Runtime.PyTuple_Size(args); object arg; var isGeneric = false; ArrayList defaultArgList = null; @@ -301,9 +301,9 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth isGeneric = true; } ParameterInfo[] pi = mi.GetParameters(); - int clrnargs = pi.Length; + var clrnargs = pi.Length; var match = false; - int arrayStart = -1; + var arrayStart = -1; var outs = 0; if (pynargs == clrnargs) @@ -314,7 +314,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth { match = true; defaultArgList = new ArrayList(); - for (int v = pynargs; v < clrnargs; v++) + for (var v = pynargs; v < clrnargs; v++) { if (pi[v].DefaultValue == DBNull.Value) { @@ -338,7 +338,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth { var margs = new object[clrnargs]; - for (var n = 0; n < clrnargs; n++) + for (int n = 0; n < clrnargs; n++) { IntPtr op; if (n < pynargs) @@ -507,7 +507,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 +601,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..8af722d29 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 @@ -395,6 +413,14 @@ public static Assembly AddReference(string name) { assembly = AssemblyManager.LoadAssemblyFullPath(name); } + if (System.IO.File.Exists(name)) + { + var zone = System.Security.Policy.Zone.CreateFromUrl(name); + if (zone.SecurityZone != System.Security.SecurityZone.MyComputer) + { + throw new Exception($"File is blocked (NTFS Security)"); + } + } if (assembly == null) { throw new FileNotFoundException($"Unable to find assembly '{name}'."); @@ -403,6 +429,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/pyobject.cs b/src/runtime/pyobject.cs index 47f413409..3b8c71efa 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Dynamic; using System.Linq.Expressions; @@ -12,7 +13,7 @@ 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, IDisposable { protected internal IntPtr obj = IntPtr.Zero; private bool disposed = false; @@ -43,6 +44,10 @@ protected PyObject() ~PyObject() { + // We needs to disable Finalizers until it's valid implementation. + // Current implementation can produce low probability floating bugs. + return; + Dispose(); } @@ -96,6 +101,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; + } /// @@ -493,9 +519,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 +807,7 @@ public bool IsCallable() /// public bool IsIterable() { - return Runtime.PyIter_Check(obj); + return Runtime.PyObject_IsIterable(obj); } @@ -884,7 +910,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 +941,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 +979,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 +1001,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 +1035,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 +1065,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 @@ -1171,5 +1239,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..8e6957855 --- /dev/null +++ b/src/runtime/pyscope.cs @@ -0,0 +1,662 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Dynamic; + +namespace Python.Runtime +{ + public class PyScopeException : Exception + { + public PyScopeException(string message) + : base(message) + { + + } + } + + /// + /// Classes/methods have this attribute must be used with GIL obtained. + /// + public class PyGILAttribute : Attribute + { + } + + [PyGIL] + public class PyScope : DynamicObject, IDisposable + { + public readonly string Name; + + /// + /// the python Module object the scope associated with. + /// + internal readonly IntPtr obj; + + /// + /// the variable dict of the scope. + /// + internal readonly IntPtr variables; + + private bool _isDisposed; + + /// + /// The Manager this scope associated with. + /// It provides scopes this scope can import. + /// + internal readonly PyScopeManager Manager; + + /// + /// event which will be triggered after the scope disposed. + /// + public event Action OnDispose; + + /// + /// Constructor + /// + /// + /// Create a scope based on a Python Module. + /// + internal PyScope(IntPtr ptr, PyScopeManager manager) + { + if (!Runtime.PyType_IsSubtype(Runtime.PyObject_TYPE(ptr), Runtime.PyModuleType)) + { + throw new PyScopeException("object is not a module"); + } + Manager = manager ?? PyScopeManager.Global; + obj = ptr; + //Refcount of the variables not increase + variables = Runtime.PyModule_GetDict(obj); + Runtime.CheckExceptionOccurred(); + + Runtime.PyDict_SetItemString( + variables, "__builtins__", + Runtime.PyEval_GetBuiltins() + ); + this.Name = this.Get("__name__"); + } + + /// + /// return the variable dict of the scope. + /// + /// + public PyDict Variables() + { + Runtime.XIncref(variables); + return new PyDict(variables); + } + + /// + /// Create a scope, and import all from this scope + /// + /// + public PyScope NewScope() + { + var scope = Manager.Create(); + scope.ImportAll(this); + return scope; + } + + /// + /// Import method + /// + /// + /// Import a scope or a module of given name, + /// scope will be looked up first. + /// + public dynamic Import(string name, string asname = null) + { + Check(); + if (String.IsNullOrEmpty(asname)) + { + asname = name; + } + PyScope scope; + Manager.TryGet(name, out scope); + if (scope != null) + { + Import(scope, asname); + return scope; + } + else + { + PyObject module = PythonEngine.ImportModule(name); + Import(module, asname); + return module; + } + } + + /// + /// Import method + /// + /// + /// Import a scope as a variable of given name. + /// + public void Import(PyScope scope, string asname) + { + this.Set(asname, scope.obj); + } + + /// + /// Import Method + /// + /// + /// The 'import .. as ..' statement in Python. + /// Import a module as a variable into the scope. + /// + public void Import(PyObject module, string asname = null) + { + if (String.IsNullOrEmpty(asname)) + { + asname = module.GetAttr("__name__").As(); + } + Set(asname, module); + } + + /// + /// ImportAll Method + /// + /// + /// The 'import * from ..' statement in Python. + /// Import all content of a scope/module of given name into the scope, scope will be looked up first. + /// + public void ImportAll(string name) + { + PyScope scope; + Manager.TryGet(name, out scope); + if (scope != null) + { + ImportAll(scope); + return; + } + else + { + PyObject module = PythonEngine.ImportModule(name); + ImportAll(module); + } + } + + /// + /// ImportAll Method + /// + /// + /// Import all variables of the scope into this scope. + /// + public void ImportAll(PyScope scope) + { + int result = Runtime.PyDict_Update(variables, scope.variables); + if (result < 0) + { + throw new PythonException(); + } + } + + /// + /// ImportAll Method + /// + /// + /// Import all variables of the module into this scope. + /// + public void ImportAll(PyObject module) + { + if (Runtime.PyObject_Type(module.obj) != Runtime.PyModuleType) + { + throw new PyScopeException("object is not a module"); + } + var module_dict = Runtime.PyModule_GetDict(module.obj); + int result = Runtime.PyDict_Update(variables, module_dict); + if (result < 0) + { + throw new PythonException(); + } + } + + /// + /// ImportAll Method + /// + /// + /// Import all variables in the dictionary into this scope. + /// + public void ImportAll(PyDict dict) + { + int result = Runtime.PyDict_Update(variables, dict.obj); + if (result < 0) + { + throw new PythonException(); + } + } + + /// + /// Execute method + /// + /// + /// Execute a Python ast and return the result as a PyObject. + /// The ast can be either an expression or stmts. + /// + public PyObject Execute(PyObject script, PyDict locals = null) + { + Check(); + IntPtr _locals = locals == null ? variables : locals.obj; + IntPtr ptr = Runtime.PyEval_EvalCode(script.Handle, variables, _locals); + Runtime.CheckExceptionOccurred(); + if (ptr == Runtime.PyNone) + { + Runtime.XDecref(ptr); + return null; + } + return new PyObject(ptr); + } + + /// + /// Execute method + /// + /// + /// Execute a Python ast and return the result as a PyObject, + /// and convert the result to a Managed Object of given type. + /// The ast can be either an expression or stmts. + /// + public T Execute(PyObject script, PyDict locals = null) + { + Check(); + PyObject pyObj = Execute(script, locals); + if (pyObj == null) + { + return default(T); + } + var obj = pyObj.As(); + return obj; + } + + /// + /// Eval method + /// + /// + /// Evaluate a Python expression and return the result as a PyObject + /// or null if an exception is raised. + /// + public PyObject Eval(string code, PyDict locals = null) + { + Check(); + IntPtr _locals = locals == null ? variables : locals.obj; + var flag = (IntPtr)Runtime.Py_eval_input; + IntPtr ptr = Runtime.PyRun_String( + code, flag, variables, _locals + ); + Runtime.CheckExceptionOccurred(); + return new PyObject(ptr); + } + + /// + /// Evaluate a Python expression + /// + /// + /// Evaluate a Python expression + /// and convert the result to a Managed Object of given type. + /// + public T Eval(string code, PyDict locals = null) + { + Check(); + PyObject pyObj = Eval(code, locals); + var obj = pyObj.As(); + return obj; + } + + /// + /// Exec Method + /// + /// + /// Exec a Python script and save its local variables in the current local variable dict. + /// + public void Exec(string code, PyDict locals = null) + { + Check(); + IntPtr _locals = locals == null ? variables : locals.obj; + Exec(code, variables, _locals); + } + + private void Exec(string code, IntPtr _globals, IntPtr _locals) + { + var flag = (IntPtr)Runtime.Py_file_input; + IntPtr ptr = Runtime.PyRun_String( + code, flag, _globals, _locals + ); + Runtime.CheckExceptionOccurred(); + if (ptr != Runtime.PyNone) + { + throw new PythonException(); + } + Runtime.XDecref(ptr); + } + + /// + /// Set Variable Method + /// + /// + /// Add a new variable to the variables dict if it not exist + /// or update its value if the variable exists. + /// + public void Set(string name, object value) + { + IntPtr _value = Converter.ToPython(value, value?.GetType()); + Set(name, _value); + Runtime.XDecref(_value); + } + + private void Set(string name, IntPtr value) + { + Check(); + using (var pyKey = new PyString(name)) + { + int r = Runtime.PyObject_SetItem(variables, pyKey.obj, value); + if (r < 0) + { + throw new PythonException(); + } + } + } + + /// + /// Remove Method + /// + /// + /// Remove a variable from the variables dict. + /// + public void Remove(string name) + { + Check(); + using (var pyKey = new PyString(name)) + { + int r = Runtime.PyObject_DelItem(variables, pyKey.obj); + if (r < 0) + { + throw new PythonException(); + } + } + } + + /// + /// Contains Method + /// + /// + /// Returns true if the variable exists in the scope. + /// + public bool Contains(string name) + { + Check(); + using (var pyKey = new PyString(name)) + { + return Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0; + } + } + + /// + /// Get Method + /// + /// + /// Returns the value of the variable of given name. + /// If the variable does not exist, throw an Exception. + /// + public PyObject Get(string name) + { + PyObject scope; + var state = TryGet(name, out scope); + if (!state) + { + throw new PyScopeException($"The scope of name '{Name}' has no attribute '{name}'"); + } + return scope; + } + + /// + /// TryGet Method + /// + /// + /// Returns the value of the variable, local variable first. + /// If the variable does not exist, return null. + /// + public bool TryGet(string name, out PyObject value) + { + Check(); + using (var pyKey = new PyString(name)) + { + if (Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0) + { + IntPtr op = Runtime.PyObject_GetItem(variables, pyKey.obj); + if (op == IntPtr.Zero) + { + throw new PythonException(); + } + if (op == Runtime.PyNone) + { + Runtime.XDecref(op); + value = null; + return true; + } + value = new PyObject(op); + return true; + } + else + { + value = null; + return false; + } + } + } + + /// + /// Get Method + /// + /// + /// Obtain the value of the variable of given name, + /// and convert the result to a Managed Object of given type. + /// If the variable does not exist, throw an Exception. + /// + public T Get(string name) + { + Check(); + PyObject pyObj = Get(name); + if (pyObj == null) + { + return default(T); + } + return pyObj.As(); + } + + /// + /// TryGet Method + /// + /// + /// Obtain the value of the variable of given name, + /// and convert the result to a Managed Object of given type. + /// If the variable does not exist, return false. + /// + public bool TryGet(string name, out T value) + { + Check(); + PyObject pyObj; + var result = TryGet(name, out pyObj); + if (!result) + { + value = default(T); + return false; + } + if (pyObj == null) + { + if (typeof(T).IsValueType) + { + throw new PyScopeException($"The value of the attribute '{name}' is None which cannot be convert to '{typeof(T).ToString()}'"); + } + else + { + value = default(T); + return true; + } + } + value = pyObj.As(); + return true; + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + result = this.Get(binder.Name); + return true; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + this.Set(binder.Name, value); + return true; + } + + private void Check() + { + if (_isDisposed) + { + throw new PyScopeException($"The scope of name '{Name}' object has been disposed"); + } + } + + public void Dispose() + { + if (_isDisposed) + { + return; + } + _isDisposed = true; + Runtime.XDecref(obj); + this.OnDispose?.Invoke(this); + } + + ~PyScope() + { + // We needs to disable Finalizers until it's valid implementation. + // Current implementation can produce low probability floating bugs. + return; + + Dispose(); + } + } + + public class PyScopeManager + { + public 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..c1b663d22 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); @@ -223,6 +234,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 +310,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 +500,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 +574,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 +592,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 +619,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 +724,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..ded7fbeb5 100644 --- a/src/runtime/pythonexception.cs +++ b/src/runtime/pythonexception.cs @@ -13,6 +13,7 @@ public class PythonException : System.Exception private IntPtr _pyTB = IntPtr.Zero; private string _tb = ""; private string _message = ""; + private string _pythonTypeName = ""; private bool disposed = false; public PythonException() @@ -33,6 +34,8 @@ public PythonException() type = pyTypeName.ToString(); } + _pythonTypeName = type; + Runtime.XIncref(_pyValue); using (var pyValue = new PyObject(_pyValue)) { @@ -57,6 +60,10 @@ public PythonException() ~PythonException() { + // We needs to disable Finalizers until it's valid implementation. + // Current implementation can produce low probability floating bugs. + return; + Dispose(); } @@ -128,6 +135,13 @@ public override string StackTrace get { return _tb; } } + /// + /// Python error type name. + /// + public string PythonTypeName + { + get { return _pythonTypeName; } + } /// /// Dispose Method diff --git a/src/runtime/resources/clr.py b/src/runtime/resources/clr.py index 91f1f6b15..e708a54ac 100644 --- a/src/runtime/resources/clr.py +++ b/src/runtime/resources/clr.py @@ -2,7 +2,7 @@ Code in this module gets loaded into the main clr module. """ -__version__ = "2.3.0" +__version__ = "2.4.0.dev0" class clrproperty(object): diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 346ea745f..863eb4034 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -2,6 +2,7 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; +using System.Collections.Generic; namespace Python.Runtime { @@ -9,6 +10,27 @@ namespace Python.Runtime internal static class NativeMethods { #if MONO_LINUX || MONO_OSX +#if NETSTANDARD + private static int RTLD_NOW = 0x2; +#if MONO_LINUX + private static int RTLD_GLOBAL = 0x100; + private static IntPtr RTLD_DEFAULT = IntPtr.Zero; + private const string NativeDll = "libdl.so"; + public static IntPtr LoadLibrary(string fileName) + { + return dlopen($"lib{fileName}.so", RTLD_NOW | RTLD_GLOBAL); + } +#elif MONO_OSX + private static int RTLD_GLOBAL = 0x8; + private const string NativeDll = "/usr/lib/libSystem.dylib"; + private static IntPtr RTLD_DEFAULT = new IntPtr(-2); + + public static IntPtr LoadLibrary(string fileName) + { + return dlopen($"lib{fileName}.dylib", RTLD_NOW | RTLD_GLOBAL); + } +#endif +#else private static int RTLD_NOW = 0x2; private static int RTLD_SHARED = 0x20; #if MONO_OSX @@ -23,6 +45,8 @@ public static IntPtr LoadLibrary(string fileName) { return dlopen(fileName, RTLD_NOW | RTLD_SHARED); } +#endif + public static void FreeLibrary(IntPtr handle) { @@ -48,16 +72,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 +104,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 +119,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 +130,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 +173,107 @@ public class Runtime internal const string dllWithPyMalloc = ""; #endif -#if PYTHON_WITHOUT_ENABLE_SHARED - public const string PythonDll = "__Internal"; + // C# compiler copies constants to the assemblies that references this library. + // We needs to replace all public constants to static readonly fields to allow + // binary substitution of different Python.Runtime.dll builds in a target application. + + public static readonly string PythonDLL = _PythonDll; + +#if PYTHON_WITHOUT_ENABLE_SHARED && !NETSTANDARD + internal const string _PythonDll = "__Internal"; #else - public const string PythonDll = dllBase + dllWithPyDebug + dllWithPyMalloc; + internal const string _PythonDll = dllBase + dllWithPyDebug + dllWithPyMalloc; #endif - public static readonly int pyversionnumber = Convert.ToInt32(pyver); + public static readonly int pyversionnumber = Convert.ToInt32(_pyver); // set to true when python is finalizing internal static object IsFinalizingLock = new object(); internal static bool IsFinalizing; internal static bool Is32Bit = IntPtr.Size == 4; + + // .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; + + /// + /// 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, + 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, + }; + + /// + /// 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; } + internal static bool IsPython2 = pyversionnumber < 30; internal static bool IsPython3 = pyversionnumber >= 30; /// /// Encoding to use to convert Unicode to/from Managed to Native /// - internal static readonly Encoding PyEncoding = UCS == 2 ? Encoding.Unicode : Encoding.UTF32; + internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32; /// /// Initialize the runtime... /// - internal static void Initialize() + internal static void Initialize(bool initSigs = false) { if (Py_IsInitialized() == 0) { - Py_Initialize(); + Py_InitializeEx(initSigs ? 1 : 0); } if (PyEval_ThreadsInitialized() == 0) @@ -175,6 +281,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 +389,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,6 +424,53 @@ internal static void Initialize() AssemblyManager.UpdatePath(); } + /// + /// 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; + } + internal static void Shutdown() { AssemblyManager.Shutdown(); @@ -349,8 +515,8 @@ internal static int AtExit() #if PYTHON3 internal static IntPtr PyBytesType; - internal static IntPtr _PyObject_NextNotImplemented; #endif + internal static IntPtr _PyObject_NextNotImplemented; internal static IntPtr PyNotImplemented; internal const int Py_LT = 0; @@ -374,38 +540,15 @@ internal static int AtExit() /// internal static void CheckExceptionOccurred() { - if (PyErr_Occurred() != 0) + if (PyErr_Occurred() != IntPtr.Zero) { throw new PythonException(); } } - internal static IntPtr GetBoundArgTuple(IntPtr obj, IntPtr args) - { - if (PyObject_TYPE(args) != PyTupleType) - { - Exceptions.SetError(Exceptions.TypeError, "tuple expected"); - return IntPtr.Zero; - } - int size = PyTuple_Size(args); - IntPtr items = PyTuple_New(size + 1); - PyTuple_SetItem(items, 0, obj); - XIncref(obj); - - for (var i = 0; i < size; i++) - { - IntPtr item = PyTuple_GetItem(args, i); - XIncref(item); - PyTuple_SetItem(items, i + 1, item); - } - - return items; - } - - internal static IntPtr ExtendTuple(IntPtr t, params IntPtr[] args) { - int size = PyTuple_Size(t); + var size = PyTuple_Size(t); int add = args.Length; IntPtr item; @@ -448,7 +591,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; @@ -499,7 +642,7 @@ internal static Type[] PythonArgsToTypeArray(IntPtr arg, bool mangleObjects) /// internal static unsafe void XIncref(IntPtr op) { -#if PYTHON_WITH_PYDEBUG +#if PYTHON_WITH_PYDEBUG || NETSTANDARD Py_IncRef(op); return; #else @@ -520,7 +663,7 @@ internal static unsafe void XIncref(IntPtr op) internal static unsafe void XDecref(IntPtr op) { -#if PYTHON_WITH_PYDEBUG +#if PYTHON_WITH_PYDEBUG || NETSTANDARD Py_DecRef(op); return; #else @@ -570,7 +713,7 @@ internal static unsafe long Refcount(IntPtr op) /// Limit this function usage for Testing and Py_Debug builds /// /// PyObject Ptr - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_IncRef(IntPtr ob); /// @@ -578,157 +721,163 @@ internal static unsafe long Refcount(IntPtr op) /// Limit this function usage for Testing and Py_Debug builds /// /// PyObject Ptr - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_DecRef(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_Initialize(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + internal static extern void Py_InitializeEx(int initsigs); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int Py_IsInitialized(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_Finalize(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_NewInterpreter(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_EndInterpreter(IntPtr threadState); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyThreadState_New(IntPtr istate); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyThreadState_Get(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyThread_get_key_value(IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyThread_get_thread_ident(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyThread_set_key_value(IntPtr key, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyThreadState_Swap(IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyGILState_Ensure(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyGILState_Release(IntPtr gs); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyGILState_GetThisThreadState(); #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] public static extern int Py_Main( int argc, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StrArrayMarshaler))] string[] argv ); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] public static extern int Py_Main(int argc, string[] argv); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_InitThreads(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyEval_ThreadsInitialized(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_AcquireLock(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_ReleaseLock(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_AcquireThread(IntPtr tstate); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_ReleaseThread(IntPtr tstate); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyEval_SaveThread(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyEval_RestoreThread(IntPtr tstate); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyEval_GetBuiltins(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyEval_GetGlobals(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyEval_GetLocals(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetProgramName(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_SetProgramName(IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetPythonHome(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_SetPythonHome(IntPtr home); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetPath(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void Py_SetPath(IntPtr home); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetVersion(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetPlatform(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetCopyright(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetCompiler(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_GetBuildInfo(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyRun_SimpleString(string code); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr Py_CompileString(string code, string file, IntPtr tok); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_ExecCodeModule(string name, IntPtr code); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyCFunction_NewEx(IntPtr ml, IntPtr self, IntPtr mod); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyCFunction_Call(IntPtr func, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyClass_New(IntPtr bases, IntPtr dict, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyInstance_New(IntPtr cls, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyInstance_NewRaw(IntPtr cls, IntPtr dict); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyMethod_New(IntPtr func, IntPtr self, IntPtr cls); @@ -777,44 +926,59 @@ internal static string PyObject_GetTypeName(IntPtr op) return Marshal.PtrToStringAnsi(ppName); } - [DllImport(PythonDll)] + /// + /// Test whether the Python object is an iterable. + /// + internal static bool PyObject_IsIterable(IntPtr pointer) + { + var ob_type = Marshal.ReadIntPtr(pointer, ObjectOffset.ob_type); +#if PYTHON2 + long tp_flags = Util.ReadCLong(ob_type, TypeOffset.tp_flags); + if ((tp_flags & TypeFlags.HaveIter) == 0) + return false; +#endif + IntPtr tp_iter = Marshal.ReadIntPtr(ob_type, TypeOffset.tp_iter); + return tp_iter != IntPtr.Zero; + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_HasAttrString(IntPtr pointer, string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GetAttrString(IntPtr pointer, string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_SetAttrString(IntPtr pointer, string name, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_HasAttr(IntPtr pointer, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GetAttr(IntPtr pointer, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_SetAttr(IntPtr pointer, IntPtr name, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GetItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_SetItem(IntPtr pointer, IntPtr key, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_DelItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GetIter(IntPtr op); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Call(IntPtr pointer, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_CallObject(IntPtr pointer, IntPtr args); #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_RichCompareBool(IntPtr value1, IntPtr value2, int opid); internal static int PyObject_Compare(IntPtr value1, IntPtr value2) @@ -842,46 +1006,52 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2) return -1; } #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_Compare(IntPtr value1, IntPtr value2); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_IsInstance(IntPtr ob, IntPtr type); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_IsSubclass(IntPtr ob, IntPtr type); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyCallable_Check(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_IsTrue(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_Not(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern int PyObject_Size(IntPtr pointer); + internal static long PyObject_Size(IntPtr pointer) + { + return (long) _PyObject_Size(pointer); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyObject_Size")] + private static extern IntPtr _PyObject_Size(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Hash(IntPtr op); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Repr(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Str(IntPtr pointer); #if PYTHON3 - [DllImport(PythonDll, EntryPoint = "PyObject_Str")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyObject_Str")] internal static extern IntPtr PyObject_Unicode(IntPtr pointer); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Unicode(IntPtr pointer); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_Dir(IntPtr pointer); @@ -890,20 +1060,21 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2) //==================================================================== #if PYTHON3 - [DllImport(PythonDll, EntryPoint = "PyNumber_Long")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyNumber_Long")] internal static extern IntPtr PyNumber_Int(IntPtr ob); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Int(IntPtr ob); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Long(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Float(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern bool PyNumber_Check(IntPtr ob); internal static bool PyInt_Check(IntPtr ob) @@ -929,28 +1100,32 @@ internal static IntPtr PyInt_FromInt64(long value) } #if PYTHON3 - [DllImport(PythonDll, EntryPoint = "PyLong_FromLong")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_FromLong")] private static extern IntPtr PyInt_FromLong(IntPtr value); - [DllImport(PythonDll, EntryPoint = "PyLong_AsLong")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_AsLong")] internal static extern int PyInt_AsLong(IntPtr value); - [DllImport(PythonDll, EntryPoint = "PyLong_FromString")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_FromString")] internal static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); - [DllImport(PythonDll, EntryPoint = "PyLong_GetMax")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyLong_GetMax")] internal static extern int PyInt_GetMax(); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr PyInt_FromLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyInt_AsLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyInt_GetMax(); #endif @@ -959,34 +1134,34 @@ internal static bool PyLong_Check(IntPtr ob) return PyObject_TYPE(ob) == PyLongType; } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromLong(long value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromUnsignedLong(uint value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromDouble(double value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromLongLong(long value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromUnsignedLongLong(ulong value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyLong_FromString(string value, IntPtr end, int radix); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyLong_AsLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern uint PyLong_AsUnsignedLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern long PyLong_AsLongLong(IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern ulong PyLong_AsUnsignedLongLong(IntPtr value); internal static bool PyFloat_Check(IntPtr ob) @@ -994,88 +1169,88 @@ internal static bool PyFloat_Check(IntPtr ob) return PyObject_TYPE(ob) == PyFloatType; } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyFloat_FromDouble(double value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyFloat_FromString(IntPtr value, IntPtr junk); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern double PyFloat_AsDouble(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Add(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Subtract(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Multiply(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Divide(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_And(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Xor(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Or(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Lshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Rshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Power(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Remainder(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceAdd(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceSubtract(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceMultiply(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceDivide(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceAnd(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceXor(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceOr(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceLshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceRshift(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlacePower(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_InPlaceRemainder(IntPtr o1, IntPtr o2); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Negative(IntPtr o1); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Positive(IntPtr o1); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyNumber_Invert(IntPtr o1); @@ -1083,49 +1258,94 @@ internal static bool PyFloat_Check(IntPtr ob) // Python sequence API //==================================================================== - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern bool PySequence_Check(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PySequence_GetItem(IntPtr pointer, int index); + internal static IntPtr PySequence_GetItem(IntPtr pointer, long index) + { + return PySequence_GetItem(pointer, new IntPtr(index)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PySequence_GetItem(IntPtr pointer, IntPtr index); + + internal 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 int PySequence_SetItem(IntPtr pointer, int index, IntPtr value); + internal static int PySequence_DelItem(IntPtr pointer, long index) + { + return PySequence_DelItem(pointer, new IntPtr(index)); + } - [DllImport(PythonDll)] - internal static extern int PySequence_DelItem(IntPtr pointer, int index); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PySequence_DelItem(IntPtr pointer, IntPtr index); - [DllImport(PythonDll)] - internal static extern IntPtr PySequence_GetSlice(IntPtr pointer, int i1, int i2); + internal 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_SetSlice(IntPtr pointer, int i1, int i2, IntPtr v); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PySequence_GetSlice(IntPtr pointer, IntPtr i1, IntPtr i2); - [DllImport(PythonDll)] - internal static extern int PySequence_DelSlice(IntPtr pointer, int i1, int i2); + internal static int PySequence_SetSlice(IntPtr pointer, long i1, long i2, IntPtr v) + { + return PySequence_SetSlice(pointer, new IntPtr(i1), new IntPtr(i2), v); + } - [DllImport(PythonDll)] - internal static extern int PySequence_Size(IntPtr pointer); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PySequence_SetSlice(IntPtr pointer, IntPtr i1, IntPtr i2, IntPtr v); - [DllImport(PythonDll)] + internal static int PySequence_DelSlice(IntPtr pointer, long i1, long i2) + { + return PySequence_DelSlice(pointer, new IntPtr(i1), new IntPtr(i2)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PySequence_DelSlice(IntPtr pointer, IntPtr i1, IntPtr i2); + + internal 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)] internal static extern int PySequence_Contains(IntPtr pointer, IntPtr item); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySequence_Concat(IntPtr pointer, IntPtr other); - [DllImport(PythonDll)] - internal static extern IntPtr PySequence_Repeat(IntPtr pointer, int count); + internal 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)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySequence_Index(IntPtr pointer, IntPtr item); - [DllImport(PythonDll)] - internal static extern int PySequence_Count(IntPtr pointer, IntPtr value); + internal static long PySequence_Count(IntPtr pointer, IntPtr value) + { + return (long) _PySequence_Count(pointer, value); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PySequence_Count")] + private static extern IntPtr _PySequence_Count(IntPtr pointer, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySequence_Tuple(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySequence_List(IntPtr pointer); @@ -1146,37 +1366,62 @@ internal static bool PyString_Check(IntPtr ob) internal 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)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyBytes_FromString(string op); - [DllImport(PythonDll)] - internal static extern int PyBytes_Size(IntPtr op); + internal static long PyBytes_Size(IntPtr op) + { + return (long) _PyBytes_Size(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) { return ob + BytesOffset.ob_sval; } - [DllImport(PythonDll, EntryPoint = "PyUnicode_FromStringAndSize")] - internal static extern IntPtr PyString_FromStringAndSize( + internal static IntPtr PyString_FromStringAndSize(string value, long size) + { + return _PyString_FromStringAndSize(value, new IntPtr(size)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "PyUnicode_FromStringAndSize")] + internal 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); + internal 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); + internal 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)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyString_AsString(IntPtr op); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyString_Size(IntPtr pointer); #endif @@ -1186,52 +1431,78 @@ internal static bool PyUnicode_Check(IntPtr ob) } #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_FromObject(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); - [DllImport(PythonDll)] - internal static extern IntPtr PyUnicode_FromKindAndData( + internal 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) + internal 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); + internal static long PyUnicode_GetSize(IntPtr ob) + { + return (long)_PyUnicode_GetSize(ob); + } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyUnicode_GetSize")] + private static extern IntPtr _PyUnicode_GetSize(IntPtr ob); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_AsUnicode(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyUnicode_FromOrdinal(int c); #elif PYTHON2 - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromObject")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromObject")] internal static extern IntPtr PyUnicode_FromObject(IntPtr ob); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromEncodedObject")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromEncodedObject")] internal static extern IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromUnicode")] - internal static extern IntPtr PyUnicode_FromUnicode( + internal static IntPtr PyUnicode_FromUnicode(string s, long size) + { + return PyUnicode_FromUnicode(s, new IntPtr(size)); + } + + [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); + internal static long PyUnicode_GetSize(IntPtr ob) + { + return (long) _PyUnicode_GetSize(ob); + } - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "AsUnicode")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "GetSize")] + internal static extern IntPtr _PyUnicode_GetSize(IntPtr ob); + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "AsUnicode")] internal static extern IntPtr PyUnicode_AsUnicode(IntPtr ob); - [DllImport(PythonDll, EntryPoint = PyUnicodeEntryPoint + "FromOrdinal")] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, + EntryPoint = PyUnicodeEntryPoint + "FromOrdinal")] internal static extern IntPtr PyUnicode_FromOrdinal(int c); #endif @@ -1267,9 +1538,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); @@ -1288,53 +1559,58 @@ internal static bool PyDict_Check(IntPtr ob) return PyObject_TYPE(ob) == PyDictType; } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_New(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDictProxy_New(IntPtr dict); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_GetItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_GetItemString(IntPtr pointer, string key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_SetItem(IntPtr pointer, IntPtr key, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_SetItemString(IntPtr pointer, string key, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_DelItem(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_DelItemString(IntPtr pointer, string key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyMapping_HasKey(IntPtr pointer, IntPtr key); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_Keys(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_Values(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_Items(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyDict_Copy(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyDict_Update(IntPtr pointer, IntPtr other); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyDict_Clear(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern int PyDict_Size(IntPtr pointer); + internal static long PyDict_Size(IntPtr pointer) + { + return (long) _PyDict_Size(pointer); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyDict_Size")] + internal static extern IntPtr _PyDict_Size(IntPtr pointer); //==================================================================== @@ -1346,39 +1622,73 @@ internal static bool PyList_Check(IntPtr ob) return PyObject_TYPE(ob) == PyListType; } - [DllImport(PythonDll)] - internal static extern IntPtr PyList_New(int size); + internal static IntPtr PyList_New(long size) + { + return PyList_New(new IntPtr(size)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyList_New(IntPtr size); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyList_AsTuple(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PyList_GetItem(IntPtr pointer, int index); + internal static IntPtr PyList_GetItem(IntPtr pointer, long index) + { + return PyList_GetItem(pointer, new IntPtr(index)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyList_GetItem(IntPtr pointer, IntPtr index); + + internal static int PyList_SetItem(IntPtr pointer, long index, IntPtr value) + { + return PyList_SetItem(pointer, new IntPtr(index), value); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PyList_SetItem(IntPtr pointer, IntPtr index, IntPtr value); - [DllImport(PythonDll)] - internal static extern int PyList_SetItem(IntPtr pointer, int index, IntPtr value); + internal static int PyList_Insert(IntPtr pointer, long index, IntPtr value) + { + return PyList_Insert(pointer, new IntPtr(index), value); + } - [DllImport(PythonDll)] - internal static extern int PyList_Insert(IntPtr pointer, int index, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PyList_Insert(IntPtr pointer, IntPtr index, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_Append(IntPtr pointer, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_Reverse(IntPtr pointer); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyList_Sort(IntPtr pointer); - [DllImport(PythonDll)] - internal static extern IntPtr PyList_GetSlice(IntPtr pointer, int start, int end); + internal static IntPtr PyList_GetSlice(IntPtr pointer, long start, long end) + { + return PyList_GetSlice(pointer, new IntPtr(start), new IntPtr(end)); + } - [DllImport(PythonDll)] - internal static extern int PyList_SetSlice(IntPtr pointer, int start, int end, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyList_GetSlice(IntPtr pointer, IntPtr start, IntPtr end); - [DllImport(PythonDll)] - internal static extern int PyList_Size(IntPtr pointer); + internal 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); + + internal 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 @@ -1389,39 +1699,64 @@ internal static bool PyTuple_Check(IntPtr ob) return PyObject_TYPE(ob) == PyTupleType; } - [DllImport(PythonDll)] - internal static extern IntPtr PyTuple_New(int size); + internal 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); + + internal static IntPtr PyTuple_GetItem(IntPtr pointer, long index) + { + return PyTuple_GetItem(pointer, new IntPtr(index)); + } - [DllImport(PythonDll)] - internal static extern IntPtr PyTuple_GetItem(IntPtr pointer, int index); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyTuple_GetItem(IntPtr pointer, IntPtr index); + + internal static int PyTuple_SetItem(IntPtr pointer, long index, IntPtr value) + { + return PyTuple_SetItem(pointer, new IntPtr(index), value); + } - [DllImport(PythonDll)] - internal static extern int PyTuple_SetItem(IntPtr pointer, int index, IntPtr value); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int PyTuple_SetItem(IntPtr pointer, IntPtr index, IntPtr value); - [DllImport(PythonDll)] - internal static extern IntPtr PyTuple_GetSlice(IntPtr pointer, int start, int end); + internal static IntPtr PyTuple_GetSlice(IntPtr pointer, long start, long end) + { + return PyTuple_GetSlice(pointer, new IntPtr(start), new IntPtr(end)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyTuple_GetSlice(IntPtr pointer, IntPtr start, IntPtr end); + + internal static long PyTuple_Size(IntPtr pointer) + { + return (long) _PyTuple_Size(pointer); + } - [DllImport(PythonDll)] - internal static extern int PyTuple_Size(IntPtr 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) { - var ob_type = (IntPtr)Marshal.PtrToStructure(pointer + ObjectOffset.ob_type, typeof(IntPtr)); - IntPtr tp_iternext = ob_type + TypeOffset.tp_iternext; - return tp_iternext != null && tp_iternext != _PyObject_NextNotImplemented; - } + var ob_type = Marshal.ReadIntPtr(pointer, ObjectOffset.ob_type); +#if PYTHON2 + long tp_flags = Util.ReadCLong(ob_type, TypeOffset.tp_flags); + if ((tp_flags & TypeFlags.HaveIter) == 0) + return false; #endif + IntPtr tp_iternext = Marshal.ReadIntPtr(ob_type, TypeOffset.tp_iternext); + return tp_iternext != IntPtr.Zero && tp_iternext != _PyObject_NextNotImplemented; + } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyIter_Next(IntPtr pointer); @@ -1429,47 +1764,47 @@ internal static bool PyIter_Check(IntPtr pointer) // Python module API //==================================================================== - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyModule_New(string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern string PyModule_GetName(IntPtr module); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyModule_GetDict(IntPtr module); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern string PyModule_GetFilename(IntPtr module); #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyModule_Create2(IntPtr module, int apiver); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_Import(IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_ImportModule(string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_ReloadModule(IntPtr module); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_AddModule(string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyImport_GetModuleDict(); #if PYTHON3 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PySys_SetArgvEx( int argc, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StrArrayMarshaler))] string[] argv, int updatepath ); #elif PYTHON2 - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PySys_SetArgvEx( int argc, string[] argv, @@ -1477,10 +1812,10 @@ int updatepath ); #endif - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PySys_GetObject(string name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PySys_SetObject(string name, IntPtr ob); @@ -1493,10 +1828,10 @@ internal static bool PyType_Check(IntPtr ob) return PyObject_TypeCheck(ob, PyTypeType); } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyType_Modified(IntPtr type); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern bool PyType_IsSubtype(IntPtr t1, IntPtr t2); internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) @@ -1505,37 +1840,42 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) return (t == tp) || PyType_IsSubtype(t, tp); } - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw); - [DllImport(PythonDll)] - internal static extern IntPtr PyType_GenericAlloc(IntPtr type, int n); + internal static IntPtr PyType_GenericAlloc(IntPtr type, long n) + { + return PyType_GenericAlloc(type, new IntPtr(n)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyType_Ready(IntPtr type); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr _PyType_Lookup(IntPtr type, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GenericGetAttr(IntPtr obj, IntPtr name); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyObject_GenericSetAttr(IntPtr obj, IntPtr name, IntPtr value); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr _PyObject_GetDictPtr(IntPtr obj); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyObject_GC_New(IntPtr tp); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyObject_GC_Del(IntPtr tp); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyObject_GC_Track(IntPtr tp); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyObject_GC_UnTrack(IntPtr tp); @@ -1543,13 +1883,23 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) // Python memory API //==================================================================== - [DllImport(PythonDll)] - internal static extern IntPtr PyMem_Malloc(int size); + internal static IntPtr PyMem_Malloc(long size) + { + return PyMem_Malloc(new IntPtr(size)); + } + + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PyMem_Malloc(IntPtr size); + + internal static IntPtr PyMem_Realloc(IntPtr ptr, long size) + { + return PyMem_Realloc(ptr, 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_Realloc(IntPtr ptr, IntPtr size); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyMem_Free(IntPtr ptr); @@ -1557,40 +1907,40 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) // Python exception API //==================================================================== - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_SetString(IntPtr ob, string message); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_SetObject(IntPtr ob, IntPtr message); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyErr_SetFromErrno(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_SetNone(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyErr_ExceptionMatches(IntPtr exception); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern int PyErr_GivenExceptionMatches(IntPtr ob, IntPtr val); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_NormalizeException(IntPtr ob, IntPtr val, IntPtr tb); - [DllImport(PythonDll)] - internal static extern int PyErr_Occurred(); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr PyErr_Occurred(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_Fetch(ref IntPtr ob, ref IntPtr val, ref IntPtr tb); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_Restore(IntPtr ob, IntPtr val, IntPtr tb); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_Clear(); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void PyErr_Print(); @@ -1598,10 +1948,10 @@ internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) // Miscellaneous //==================================================================== - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyMethod_Self(IntPtr ob); - [DllImport(PythonDll)] + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr PyMethod_Function(IntPtr ob); } } diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index 6f373f036..d19c8737f 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -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,225 @@ 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 + /// + internal 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; + 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; + } + + /// + /// Platform-dependent mmap and mprotect. + /// + internal 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); + } + } + + internal 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 +677,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 +698,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.15.csproj b/src/testing/Python.Test.15.csproj new file mode 100644 index 000000000..da20ed2ef --- /dev/null +++ b/src/testing/Python.Test.15.csproj @@ -0,0 +1,86 @@ + + + + net40;netstandard2.0 + x64;x86 + DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 + Python.Test + Python.Test + Python.Test + 2.4.0 + bin\ + false + $(OutputPath)\$(AssemblyName).xml + $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml + 1591,0067 + ..\..\ + $(SolutionDir)\bin\ + $(PythonBuildDir)\$(TargetFramework)\ + 6 + false + ..\pythonnet.snk + prompt + $(PYTHONNET_DEFINE_CONSTANTS) + XPLAT + $(DefineConstants);$(CustomDefineConstants);$(BaseDefineConstants); + $(DefineConstants);TRACE;DEBUG + $(NuGetPackageRoot)\microsoft.targetingpack.netframework.v4.5\1.0.1\lib\net45\ + + + x86 + + + x64 + + + + false + full + + + true + pdbonly + + + true + false + full + + + true + true + portable + + + + $(DefineConstants);DEBUG;TRACE + + + $(DefineConstants) + + + + + + + + + + + + + + + + + + + $(TargetPath) + $(TargetDir)$(TargetName).pdb + + + + + + + diff --git a/src/testing/Python.Test.csproj b/src/testing/Python.Test.csproj index ce8dca10d..27639ed5a 100644 --- a/src/testing/Python.Test.csproj +++ b/src/testing/Python.Test.csproj @@ -83,6 +83,7 @@ + @@ -108,6 +109,6 @@ - + - + \ No newline at end of file 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..08b00d77d 100644 --- a/src/tests/test_exceptions.py +++ b/src/tests/test_exceptions.py @@ -289,7 +289,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 +346,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