diff --git a/.editorconfig b/.editorconfig index d64f74bc1..7b07446e3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -34,6 +34,10 @@ csharp_new_line_before_finally = true [*.sln] indent_style = tab +[*.csproj] +charset = utf-8 +insert_final_newline = true + # bumpversion reformats itself after every bump [.bumpversion.cfg] trim_trailing_whitespace = false diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 2758080dc..c232a1186 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,6 +3,7 @@ - Pythonnet version: - Python version: - Operating System: +- .NET Runtime: ### Details diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..2dd75c529 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,77 @@ +name: GitHub Actions + +on: [ pull_request, push ] + +jobs: + build-test: + name: Build and Test + runs-on: ${{ matrix.os }}-latest + timeout-minutes: 5 + + strategy: + fail-fast: false + matrix: + os: [windows, ubuntu, macos] + python: ["3.6", "3.7", "3.8", "3.9"] + platform: [x64] + shutdown_mode: [Normal, Soft] + + env: + PYTHONNET_SHUTDOWN_MODE: ${{ matrix.SHUTDOWN_MODE }} + + steps: + - name: Set Environment on macOS + uses: maxim-lobanov/setup-xamarin@v1 + if: ${{ matrix.os == 'macos' }} + with: + mono-version: latest + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + architecture: ${{ matrix.platform }} + + - name: Install dependencies + run: | + pip install --upgrade -r requirements.txt + + - name: Build and Install + run: | + pip install -v . + + - name: Set Python DLL path (non Windows) + if: ${{ matrix.os != 'windows' }} + run: | + python -m pythonnet.find_libpython --export >> $GITHUB_ENV + + - name: Set Python DLL path (Windows) + if: ${{ matrix.os == 'windows' }} + run: | + python -m pythonnet.find_libpython --export | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Python Tests (Mono) + if: ${{ matrix.os != 'windows' }} + run: pytest --runtime mono + + - name: Python Tests (.NET Core) + run: pytest --runtime netcore + + - name: Python Tests (.NET Framework) + if: ${{ matrix.os == 'windows' }} + run: pytest --runtime netfx + + - name: Embedding tests + run: dotnet test --runtime any-${{ matrix.platform }} src/embed_tests/ + + - name: Python tests run from .NET + run: dotnet test --runtime any-${{ matrix.platform }} src/python_tests_runner/ + + # TODO: Run perf tests + # TODO: Run mono tests on Windows? diff --git a/.github/workflows/nuget-preview.yml b/.github/workflows/nuget-preview.yml new file mode 100644 index 000000000..fcd8ca06e --- /dev/null +++ b/.github/workflows/nuget-preview.yml @@ -0,0 +1,60 @@ +name: NuGet Preview Release + +on: + schedule: + - cron: "5 4 3 */1 *" # once a month, at 4:05 on 3rd + workflow_dispatch: + +jobs: + release: + name: Release Preview + runs-on: ubuntu-latest + environment: NuGet + timeout-minutes: 10 + + env: + PYTHONNET_SHUTDOWN_MODE: Normal + + steps: + - name: Get Date + run: | + echo "DATE_VER=$(date "+%Y-%m-%d")" >> $GITHUB_ENV + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + architecture: x64 + + - name: Install dependencies + run: | + pip install --upgrade -r requirements.txt + + - name: Build and Install + run: | + pip install -v . + + - name: Python Tests + run: pytest + env: + PYTHONNET_PYDLL: libpython3.8.so + + - name: Embedding tests + run: dotnet test --runtime any-ubuntu src/embed_tests/ + env: + PYTHONNET_PYDLL: libpython3.8.so + + - name: Pack + run: dotnet pack --configuration Release --version-suffix preview${{env.DATE_VER}} --output "Release-Preview" + + - name: Publish NuGet + run: dotnet nuget push --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_MONTHLY }} Release-Preview/*.nupkg + + # TODO: Run perf tests + # TODO: Run mono tests on Windows? diff --git a/.gitignore b/.gitignore index 1e494b12d..cdb152157 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ +/src/runtime/interopNative.cs + # General binaries and Build results *.dll *.exe *.pdb +*.deps.json ### JetBrains ### .idea/ @@ -15,6 +18,7 @@ __pycache__/ build/ dist/ *.egg-info/ +.eggs/ # Unit test / coverage reports htmlcov/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e664a4696..000000000 --- a/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -dist: xenial -sudo: false -language: python -python: - - 3.8 - - 3.7 - - 3.6 - -env: - matrix: - - BUILD_OPTS=--xplat NUNIT_PATH="~/.nuget/packages/nunit.consolerunner/3.*/tools/nunit3-console.exe" RUN_TESTS=dotnet EMBED_TESTS_PATH=netcoreapp2.0_publish/ PERF_TESTS_PATH=net461/ - - BUILD_OPTS="" NUNIT_PATH="./packages/NUnit.*/tools/nunit3-console.exe" RUN_TESTS="mono $NUNIT_PATH" EMBED_TESTS_PATH="" PERF_TESTS_PATH="" - - PYTHONNET_SHUTDOWN_MODE="Soft" BUILD_OPTS=--xplat NUNIT_PATH="~/.nuget/packages/nunit.consolerunner/3.*/tools/nunit3-console.exe" RUN_TESTS=dotnet EMBED_TESTS_PATH=netcoreapp2.0_publish/ PERF_TESTS_PATH=net461/ - - PYTHONNET_SHUTDOWN_MODE="Soft" BUILD_OPTS="" NUNIT_PATH="./packages/NUnit.*/tools/nunit3-console.exe" RUN_TESTS="mono $NUNIT_PATH" EMBED_TESTS_PATH="" PERF_TESTS_PATH="" - - global: - - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - - SEGFAULT_SIGNALS=all - - PYTHONUNBUFFERED=True - - CODECOV_ENV=TRAVIS_PYTHON_VERSION - -addons: - 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 http://download.mono-project.com/repo/ubuntu stable-xenial/snapshots/5.20 main - key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF - packages: - - mono-devel - - ca-certificates-mono - - dotnet-hostfxr-2.2 - - dotnet-runtime-2.2 - - dotnet-sdk-2.2 - -before_install: - # Set-up dll path for embedded tests - - PY_LIBDIR=$(python -c 'import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') - - 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 $BUILD_OPTS - -script: - - python -m pytest - - $RUN_TESTS src/embed_tests/bin/$EMBED_TESTS_PATH/Python.EmbeddingTest.dll --labels=All - # does not work on Linux, because NuGet package for 2.3 is Windows only - # - "if [[ $TRAVIS_PYTHON_VERSION == '3.5' && $PERF_TESTS_PATH != '' ]]; then mono $NUNIT_PATH src/perf_tests/bin/$PERF_TESTS_PATH/Python.PerformanceTests.dll; fi" - -after_script: - # Waiting on mono-coverage, SharpCover or xr.Baboon - - coverage xml -i - - codecov --file coverage.xml --flags setup_linux - -notifications: - email: false - slack: - secure: "UiQdSK1/uNnHl8/gQgfLj/F5JGxtJuaT3QYtKNcw3Ddpr3FX8tfXJ/RjsCsSlRQzDm7AdBAeMzcBQmvH4iRIV2y7qVywLyru5MPiwY4ZjMN6fJK/zaaxetOct9fasIBYzHguNPDAtiBGFh2iK1H1MXTY8rkmU3WZvl18b8EsrP0=" diff --git a/AUTHORS.md b/AUTHORS.md index eeafd98e4..6cfa216b1 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -24,6 +24,7 @@ - Benoît Hudson ([@benoithudson](https://github.com/benoithudson)) - Bradley Friedman ([@leith-bartrich](https://github.com/leith-bartrich)) - Callum Noble ([@callumnoble](https://github.com/callumnoble)) +- Christabella Irwanto([@christabella](https://github.com/christabella)) - Christian Heimes ([@tiran](https://github.com/tiran)) - Christoph Gohlke ([@cgohlke](https://github.com/cgohlke)) - Christopher Bremner ([@chrisjbremner](https://github.com/chrisjbremner)) @@ -60,14 +61,15 @@ - Sean Freitag ([@cowboygneox](https://github.com/cowboygneox)) - Serge Weinstock ([@sweinst](https://github.com/sweinst)) - Simon Mourier ([@smourier](https://github.com/smourier)) +- Tom Minka ([@tminka](https://github.com/tminka)) - 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) +- William Sardar ([@williamsardar](https://github.com/williamsardar)) - Xavier Dupré ([@sdpython](https://github.com/sdpython)) - Zane Purvis ([@zanedp](https://github.com/zanedp)) -- ([@amos402]https://github.com/amos402) +- ([@amos402](https://github.com/amos402)) - ([@bltribble](https://github.com/bltribble)) - ([@civilx64](https://github.com/civilx64)) - ([@GSPP](https://github.com/GSPP)) @@ -80,3 +82,4 @@ - ([@testrunner123](https://github.com/testrunner123)) - ([@DanBarzilian](https://github.com/DanBarzilian)) - ([@alxnull](https://github.com/alxnull)) +- ([@gpetrou](https://github.com/gpetrou)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bdf5e32b..9bee653e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,28 +9,63 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. ### Added +- Ability to instantiate new .NET arrays using `Array[T](dim1, dim2, ...)` syntax +- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]). +- Add GetPythonThreadID and Interrupt methods in PythonEngine +- Ability to implement delegates with `ref` and `out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355]) + ### Changed - Drop support for Python 2, 3.4, and 3.5 +- `wchar_t` size aka `Runtime.UCS` is now determined at runtime - `clr.AddReference` may now throw errors besides `FileNotFoundException`, that provide more details about the cause of the failure - `clr.AddReference` no longer adds ".dll" implicitly - `PyIter(PyObject)` constructor replaced with static `PyIter.GetIter(PyObject)` method -- Return values from .NET methods that return an interface are now automatically +- BREAKING: Return values from .NET methods that return an interface are now automatically wrapped in that interface. This is a breaking change for users that rely on being able to access members that are part of the implementation class, but not the interface. Use the new __implementation__ or __raw_implementation__ properties to if you need to "downcast" to the implementation class. +- BREAKING: Parameters marked with `ParameterAttributes.Out` are no longer returned in addition + to the regular method return value (unless they are passed with `ref` or `out` keyword). +- BREAKING: Drop support for the long-deprecated CLR.* prefix. +- `PyObject` now implements `IEnumerable` in addition to `IEnumerable` +- floating point values passed from Python are no longer silently truncated +when .NET expects an integer [#1342][i1342] +- More specific error messages for method argument mismatch +- BREAKING: Methods with `ref` or `out` parameters and void return type return a tuple of only the `ref` and `out` parameters. +- BREAKING: to call Python from .NET `Runtime.PythonDLL` property must be set to Python DLL name +or the DLL must be loaded in advance. This must be done before calling any other Python.NET functions. +- Sign Runtime DLL with a strong name +- Implement loading through `clr_loader` instead of the included `ClrModule`, enables + support for .NET Core ### Fixed -- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash -- Fix incorrect dereference in params array handling -- Fix `object[]` parameters taking precedence when should not in overload resolution -- Fixed a bug where all .NET class instances were considered Iterable -- Fix incorrect choice of method to invoke when using keyword arguments. -- Fix non-delegate types incorrectly appearing as callable. -- Indexers can now be used with interface objects -- Fixed a bug where indexers could not be used if they were inherited +- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash +- Fix incorrect dereference in params array handling +- Fixes issue with function resolution when calling overloaded function with keyword arguments from python ([#1097][i1097]) +- Fix `object[]` parameters taking precedence when should not in overload resolution +- Fixed a bug where all .NET class instances were considered Iterable +- Fix incorrect choice of method to invoke when using keyword arguments. +- Fix non-delegate types incorrectly appearing as callable. +- Indexers can now be used with interface objects +- Fixed a bug where indexers could not be used if they were inherited +- Made it possible to use `__len__` also on `ICollection<>` interface objects +- Fixed issue when calling PythonException.Format where another exception would be raise for unnormalized exceptions +- Made it possible to call `ToString`, `GetHashCode`, and `GetType` on inteface objects +- Fixed objects returned by enumerating `PyObject` being disposed too soon +- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException +- `import` may now raise errors with more detail than "No module named X" +- Exception stacktraces on `PythonException.StackTrace` are now properly formatted +- Providing an invalid type parameter to a generic type or method produces a helpful Python error +- Empty parameter names (as can be generated from F#) do not cause crashes + +### Removed + +- implicit assembly loading (you have to explicitly `clr.AddReference` before doing import) +- support for .NET Framework 4.0-4.6; Mono before 5.4. Python.NET now requires .NET Standard 2.0 +(see [the matrix](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support)) ## [2.5.0][] - 2020-06-14 @@ -790,3 +825,4 @@ This version improves performance on benchmarks significantly compared to 2.3. [i755]: https://github.com/pythonnet/pythonnet/pull/755 [p534]: https://github.com/pythonnet/pythonnet/pull/534 [i449]: https://github.com/pythonnet/pythonnet/issues/449 +[i1342]: https://github.com/pythonnet/pythonnet/issues/1342 diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..edc8ba513 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,21 @@ + + + 3.0.0 + Copyright (c) 2006-2020 The Contributors of the Python.NET Project + pythonnet + Python.NET + 7.3 + false + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/LICENSE b/LICENSE index 19c31a12f..f3a638346 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2006-2020 the contributors of the Python.NET project +Copyright (c) 2006-2021 the contributors of the Python.NET project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index 5210cd6c9..000000000 --- a/NuGet.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/README.rst b/README.rst index 773150105..7c2b27b93 100644 --- a/README.rst +++ b/README.rst @@ -1,12 +1,15 @@ pythonnet - Python.NET =========================== -|Join the chat at https://gitter.im/pythonnet/pythonnet| +|Join the chat at https://gitter.im/pythonnet/pythonnet| |stackexchange shield| -|appveyor shield| |travis shield| |codecov shield| +|gh shield| |appveyor shield| -|license shield| |pypi package version| |conda-forge version| |python supported shield| -|stackexchange shield| +|license shield| + +|pypi package version| |conda-forge version| |python supported shield| + +|nuget preview shield| |nuget release shield| Python.NET is a package that gives Python programmers nearly seamless integration with the .NET Common Language Runtime (CLR) and @@ -42,6 +45,8 @@ module: Embedding Python in .NET ------------------------ +- You must set `Runtime.PythonDLL` or `PYTHONNET_PYDLL` environment variable + starting with version 3.0, otherwise you will receive `TypeInitializationException`. - All calls to python should be inside a ``using (Py.GIL()) {/* Your code here */}`` block. - Import python modules using ``dynamic mod = Py.Import("mod")``, then @@ -131,3 +136,9 @@ This project is supported by the `.NET Foundation :target: http://stackoverflow.com/questions/tagged/python.net .. |conda-forge version| image:: https://img.shields.io/conda/vn/conda-forge/pythonnet.svg :target: https://anaconda.org/conda-forge/pythonnet +.. |nuget preview shield| image:: https://img.shields.io/nuget/vpre/pythonnet + :target: https://www.nuget.org/packages/pythonnet/ +.. |nuget release shield| image:: https://img.shields.io/nuget/v/pythonnet + :target: https://www.nuget.org/packages/pythonnet/ +.. |gh shield| image:: https://github.com/pythonnet/pythonnet/workflows/GitHub%20Actions/badge.svg + :target: https://github.com/pythonnet/pythonnet/actions?query=branch%3Amaster diff --git a/appveyor.yml b/appveyor.yml index d45ab5b36..cc3815c62 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ version: '{branch}-{build}' build: off image: - - Visual Studio 2017 + - Visual Studio 2019 platform: - x86 @@ -10,28 +10,29 @@ platform: environment: global: - PYTHONUNBUFFERED: True + PYTHONUNBUFFERED: 'True' PYTHONWARNINGS: 'ignore:::wheel.pep425tags:' CODECOV_ENV: PYTHON_VERSION, PLATFORM matrix: + - PYTHON_VERSION: 3.9 - PYTHON_VERSION: 3.8 - PYTHON_VERSION: 3.7 - BUILD_OPTS: --xplat - PYTHON_VERSION: 3.6 - BUILD_OPTS: --xplat + - PYTHON_VERSION: 3.9 + PYTHONNET_SHUTDOWN_MODE: Soft - PYTHON_VERSION: 3.8 - - PYTHON_VERSION: 3.7 - - PYTHON_VERSION: 3.6 + PYTHONNET_SHUTDOWN_MODE: Soft - PYTHON_VERSION: 3.7 PYTHONNET_SHUTDOWN_MODE: Soft - - PYTHON_VERSION: 3.8 + - PYTHON_VERSION: 3.6 PYTHONNET_SHUTDOWN_MODE: Soft init: # Update Environment Variables based on matrix/platform - set PY_VER=%PYTHON_VERSION:.=% - set PYTHON=C:\PYTHON%PY_VER% - if %PLATFORM%==x64 (set PYTHON=%PYTHON%-x64) + - set PYTHONNET_PYDLL=%PYTHON%\python%PY_VER%.dll # Put desired Python version first in PATH - set PATH=%PYTHON%;%PYTHON%\Scripts;%PATH% @@ -39,35 +40,17 @@ init: install: - python -m pip install -U pip - pip install --upgrade -r requirements.txt --quiet - - pip install pycparser --quiet - - # Install OpenCover. Can't put on `packages.config`, not Mono compatible - - .\tools\nuget\nuget.exe install OpenCover -OutputDirectory packages -Verbosity quiet 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 %BUILD_OPTS% + - python setup.py bdist_wheel test_script: - pip install --no-index --find-links=.\dist\ pythonnet - - ps: .\ci\appveyor_run_tests.ps1 - -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 - # - codecov --file coverage.xml --flags setup_windows - # - codecov --file py.coverage --flags python_tests - # - codecov --file cs.coverage --flags embedded_tests - - codecov --file py.coverage cs.coverage coverage.xml --flags setup_windows + #- ps: .\ci\appveyor_run_tests.ps1 + - pytest + - dotnet test src/embed_tests/ artifacts: - path: dist\* - - path: '.\src\runtime\bin\*.nupkg' - -notifications: - - provider: Slack - incoming_webhook: - secure: 2S/t6rGHdbwoxehnvn5KgfsHrBFEtwnPD7M5olGErmz70oWFVpqoWd/EvDwh7rKZGdOTjDmpwcukc2xi5VRaGHbBAqFYS3tAdgAMrcaTNWs= diff --git a/ci/appveyor_run_tests.ps1 b/ci/appveyor_run_tests.ps1 index bd90943d5..7d35131f4 100644 --- a/ci/appveyor_run_tests.ps1 +++ b/ci/appveyor_run_tests.ps1 @@ -99,12 +99,12 @@ if ($XPLAT){ $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" + # Run Embedded tests for netcoreapp3.1 (OpenCover currently does not supports dotnet core) + Write-Host ("Starting embedded tests for netcoreapp3.1") -ForegroundColor "Green" + &$DOTNET_CMD ".\src\embed_tests\bin\netcoreapp3.1_publish\Python.EmbeddingTest.dll" $CS_STATUS = $LastExitCode if ($CS_STATUS -ne 0) { - Write-Host "Embedded tests for netcoreapp2.0 failed" -ForegroundColor "Red" + Write-Host "Embedded tests for netcoreapp3.1 failed" -ForegroundColor "Red" ReportTime "" } else { ReportTime ".NET Core 2.0 tests completed" diff --git a/clr.py b/clr.py new file mode 100644 index 000000000..20a975f96 --- /dev/null +++ b/clr.py @@ -0,0 +1,6 @@ +""" +Legacy Python.NET loader for backwards compatibility +""" + +from pythonnet import load +load() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..9bcf734c6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[build-system] +requires = ["setuptools>=42", "wheel", "pycparser"] +build-backend = "setuptools.build_meta" + +[tool.pytest.ini_options] +xfail_strict = true +testpaths = [ + "tests", +] diff --git a/pythonnet.15.sln b/pythonnet.15.sln deleted file mode 100644 index ce863817f..000000000 --- a/pythonnet.15.sln +++ /dev/null @@ -1,398 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29102.190 -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 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.PerformanceTests", "src\perf_tests\Python.PerformanceTests.csproj", "{6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F4C6-4EE4-9AEE-315FD79BE2D5}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitignore = .gitignore - CHANGELOG.md = CHANGELOG.md - README.rst = README.rst - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI", "CI", "{D301657F-5EAF-4534-B280-B858D651B2E5}" - ProjectSection(SolutionItems) = preProject - .travis.yml = .travis.yml - appveyor.yml = appveyor.yml - ci\appveyor_build_recipe.ps1 = ci\appveyor_build_recipe.ps1 - ci\appveyor_run_tests.ps1 = ci\appveyor_run_tests.ps1 - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{57F5D701-F265-4736-A5A2-07249E7A4DA3}" - ProjectSection(SolutionItems) = preProject - setup.py = setup.py - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "conda.recipe", "conda.recipe", "{7FD2404D-0CE8-4645-8DFB-766470E2150E}" - ProjectSection(SolutionItems) = preProject - conda.recipe\bld.bat = conda.recipe\bld.bat - conda.recipe\meta.yaml = conda.recipe\meta.yaml - conda.recipe\README.md = conda.recipe\README.md - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - DebugMono|Any CPU = DebugMono|Any CPU - DebugMono|x64 = DebugMono|x64 - DebugMono|x86 = DebugMono|x86 - DebugMonoPY3|Any CPU = DebugMonoPY3|Any CPU - DebugMonoPY3|x64 = DebugMonoPY3|x64 - DebugMonoPY3|x86 = DebugMonoPY3|x86 - DebugWin|Any CPU = DebugWin|Any CPU - DebugWin|x64 = DebugWin|x64 - DebugWin|x86 = DebugWin|x86 - DebugWinPY3|Any CPU = DebugWinPY3|Any CPU - DebugWinPY3|x64 = DebugWinPY3|x64 - DebugWinPY3|x86 = DebugWinPY3|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - ReleaseMono|Any CPU = ReleaseMono|Any CPU - ReleaseMono|x64 = ReleaseMono|x64 - ReleaseMono|x86 = ReleaseMono|x86 - ReleaseMonoPY3|Any CPU = ReleaseMonoPY3|Any CPU - ReleaseMonoPY3|x64 = ReleaseMonoPY3|x64 - ReleaseMonoPY3|x86 = ReleaseMonoPY3|x86 - ReleaseWin|Any CPU = ReleaseWin|Any CPU - ReleaseWin|x64 = ReleaseWin|x64 - ReleaseWin|x86 = ReleaseWin|x86 - ReleaseWinPY3|Any CPU = ReleaseWinPY3|Any CPU - ReleaseWinPY3|x64 = ReleaseWinPY3|x64 - ReleaseWinPY3|x86 = ReleaseWinPY3|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|Any CPU.ActiveCfg = DebugWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|Any CPU.Build.0 = DebugWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|x64.ActiveCfg = DebugWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|x64.Build.0 = DebugWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|x86.ActiveCfg = DebugWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|x86.Build.0 = DebugWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|Any CPU.ActiveCfg = DebugMono|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|Any CPU.Build.0 = DebugMono|Any CPU - {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|Any CPU.ActiveCfg = DebugMonoPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|Any CPU.Build.0 = DebugMonoPY3|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|Any CPU.ActiveCfg = DebugWin|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|Any CPU.Build.0 = DebugWin|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|Any CPU.ActiveCfg = DebugWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|Any CPU.Build.0 = DebugWinPY3|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}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|Any CPU.Build.0 = ReleaseWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|x64.ActiveCfg = ReleaseWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|x64.Build.0 = ReleaseWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|x86.ActiveCfg = ReleaseWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|x86.Build.0 = ReleaseWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|Any CPU.Build.0 = ReleaseMono|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|Any CPU.ActiveCfg = ReleaseMonoPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|Any CPU.Build.0 = ReleaseMonoPY3|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|Any CPU.ActiveCfg = ReleaseWin|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|Any CPU.Build.0 = ReleaseWin|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|Any CPU.ActiveCfg = ReleaseWinPY3|Any CPU - {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|Any CPU.Build.0 = ReleaseWinPY3|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}.Debug|Any CPU.ActiveCfg = ReleaseWinPY3|x86 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|Any CPU.Build.0 = ReleaseWinPY3|x86 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|x64.ActiveCfg = DebugWinPY3|x64 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|x64.Build.0 = DebugWinPY3|x64 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|x86.ActiveCfg = DebugWinPY3|x86 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|x86.Build.0 = DebugWinPY3|x86 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|Any CPU.ActiveCfg = DebugMono|x86 - {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|Any CPU.ActiveCfg = DebugMonoPY3|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|Any CPU.ActiveCfg = DebugWin|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|Any CPU.ActiveCfg = DebugWinPY3|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}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|x86 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|Any CPU.Build.0 = ReleaseWinPY3|x86 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|x64.ActiveCfg = ReleaseWinPY3|x64 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|x64.Build.0 = ReleaseWinPY3|x64 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|x86.ActiveCfg = ReleaseWinPY3|x86 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|x86.Build.0 = ReleaseWinPY3|x86 - {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|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|Any CPU.ActiveCfg = ReleaseMonoPY3|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|Any CPU.ActiveCfg = ReleaseWin|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|Any CPU.ActiveCfg = ReleaseWinPY3|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}.Debug|Any CPU.ActiveCfg = ReleaseWinPY3|x86 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|Any CPU.Build.0 = ReleaseWinPY3|x86 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|x64.ActiveCfg = DebugWinPY3|x64 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|x64.Build.0 = DebugWinPY3|x64 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|x86.ActiveCfg = DebugWinPY3|x86 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|x86.Build.0 = DebugWinPY3|x86 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMono|Any CPU.ActiveCfg = DebugMono|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|Any CPU.ActiveCfg = DebugMonoPY3|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|Any CPU.ActiveCfg = DebugWin|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|Any CPU.ActiveCfg = DebugWinPY3|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}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|x86 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|Any CPU.Build.0 = ReleaseWinPY3|x86 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|x64.ActiveCfg = ReleaseWinPY3|x64 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|x64.Build.0 = ReleaseWinPY3|x64 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|x86.ActiveCfg = ReleaseWinPY3|x86 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|x86.Build.0 = ReleaseWinPY3|x86 - {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|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|Any CPU.ActiveCfg = ReleaseMonoPY3|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|Any CPU.ActiveCfg = ReleaseWin|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|Any CPU.ActiveCfg = ReleaseWinPY3|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}.Debug|Any CPU.ActiveCfg = ReleaseWinPY3|x86 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|Any CPU.Build.0 = ReleaseWinPY3|x86 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|x64.ActiveCfg = DebugWinPY3|x64 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|x64.Build.0 = DebugWinPY3|x64 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|x86.ActiveCfg = DebugWinPY3|x86 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|x86.Build.0 = DebugWinPY3|x86 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|Any CPU.ActiveCfg = DebugMono|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|Any CPU.ActiveCfg = DebugMonoPY3|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|Any CPU.ActiveCfg = DebugWin|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|Any CPU.ActiveCfg = DebugWinPY3|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}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|x86 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|Any CPU.Build.0 = ReleaseWinPY3|x86 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|x64.ActiveCfg = ReleaseWinPY3|x64 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|x64.Build.0 = ReleaseWinPY3|x64 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|x86.ActiveCfg = ReleaseWinPY3|x86 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|x86.Build.0 = ReleaseWinPY3|x86 - {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|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|Any CPU.ActiveCfg = ReleaseMonoPY3|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|Any CPU.ActiveCfg = ReleaseWin|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|Any CPU.ActiveCfg = ReleaseWinPY3|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}.Debug|Any CPU.ActiveCfg = ReleaseWinPY3|x86 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|Any CPU.Build.0 = ReleaseWinPY3|x86 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|x64.ActiveCfg = DebugWinPY3|x64 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|x64.Build.0 = DebugWinPY3|x64 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|x86.ActiveCfg = DebugWinPY3|x86 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|x86.Build.0 = DebugWinPY3|x86 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|Any CPU.ActiveCfg = DebugMono|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|Any CPU.ActiveCfg = DebugMonoPY3|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|Any CPU.ActiveCfg = DebugWin|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|Any CPU.ActiveCfg = DebugWinPY3|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}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|x86 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|Any CPU.Build.0 = ReleaseWinPY3|x86 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|x64.ActiveCfg = ReleaseWinPY3|x64 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|x64.Build.0 = ReleaseWinPY3|x64 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|x86.ActiveCfg = ReleaseWinPY3|x86 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|x86.Build.0 = ReleaseWinPY3|x86 - {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|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|Any CPU.ActiveCfg = ReleaseMonoPY3|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|Any CPU.ActiveCfg = ReleaseWin|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|Any CPU.ActiveCfg = ReleaseWinPY3|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 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|Any CPU.ActiveCfg = ReleaseWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|Any CPU.Build.0 = ReleaseWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x64.ActiveCfg = DebugWinPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x64.Build.0 = DebugWinPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x86.ActiveCfg = DebugWinPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x86.Build.0 = DebugWinPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|Any CPU.ActiveCfg = DebugMono|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x64.Build.0 = DebugMono|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x86.Build.0 = DebugMono|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|Any CPU.ActiveCfg = DebugMonoPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|Any CPU.ActiveCfg = DebugWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x64.Build.0 = DebugWin|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x86.Build.0 = DebugWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|Any CPU.ActiveCfg = DebugWinPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|Any CPU.ActiveCfg = ReleaseWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|Any CPU.Build.0 = ReleaseWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x64.ActiveCfg = ReleaseWinPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x64.Build.0 = ReleaseWinPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x86.ActiveCfg = ReleaseWinPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x86.Build.0 = ReleaseWinPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|Any CPU.ActiveCfg = ReleaseMonoPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|Any CPU.ActiveCfg = ReleaseWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|Any CPU.ActiveCfg = ReleaseWinPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.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/pythonnet.sln b/pythonnet.sln index c5afd66c3..e02948c18 100644 --- a/pythonnet.sln +++ b/pythonnet.sln @@ -1,202 +1,172 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python.Runtime", "src\runtime\Python.Runtime.csproj", "{097B4AC0-74E9-4C58-BCF8-C69746EC8271}" +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30717.126 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Runtime", "src\runtime\Python.Runtime.csproj", "{4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python.Test", "src\testing\Python.Test.csproj", "{6F401A34-273B-450F-9A4C-13550BE0767B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console", "src\console\Console.csproj", "{E6B01706-00BA-4144-9029-186AC42FBE9A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python.EmbeddingTest", "src\embed_tests\Python.EmbeddingTest.csproj", "{4165C59D-2822-499F-A6DB-EACA4C331EB5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.EmbeddingTest", "src\embed_tests\Python.EmbeddingTest.csproj", "{819E089B-4770-400E-93C6-4F7A35F0EA12}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "src\console\Console.csproj", "{E29DCF0A-5114-4A98-B1DD-71264B6EA349}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Test", "src\testing\Python.Test.csproj", "{14EF9518-5BB7-4F83-8686-015BD2CC788E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "clrmodule", "src\clrmodule\clrmodule.csproj", "{86E834DE-1139-4511-96CC-69636A56E7AC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.PerformanceTests", "src\perf_tests\Python.PerformanceTests.csproj", "{4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.DomainReloadTests", "src\domain_tests\Python.DomainReloadTests.csproj", "{F2FB6DA3-318E-4F30-9A1F-932C667E38C5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F4C6-4EE4-9AEE-315FD79BE2D5}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore + CHANGELOG.md = CHANGELOG.md + LICENSE = LICENSE + README.rst = README.rst + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI", "CI", "{D301657F-5EAF-4534-B280-B858D651B2E5}" + ProjectSection(SolutionItems) = preProject + appveyor.yml = appveyor.yml + ci\appveyor_build_recipe.ps1 = ci\appveyor_build_recipe.ps1 + ci\appveyor_run_tests.ps1 = ci\appveyor_run_tests.ps1 + .github\workflows\main.yml = .github\workflows\main.yml + .github\workflows\nuget-preview.yml = .github\workflows\nuget-preview.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{57F5D701-F265-4736-A5A2-07249E7A4DA3}" + ProjectSection(SolutionItems) = preProject + setup.py = setup.py + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "conda.recipe", "conda.recipe", "{7FD2404D-0CE8-4645-8DFB-766470E2150E}" + ProjectSection(SolutionItems) = preProject + conda.recipe\bld.bat = conda.recipe\bld.bat + conda.recipe\meta.yaml = conda.recipe\meta.yaml + conda.recipe\README.md = conda.recipe\README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{BC426F42-8494-4AA5-82C9-5109ACD97BD1}" + ProjectSection(SolutionItems) = preProject + tools\geninterop\geninterop.py = tools\geninterop\geninterop.py + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.PythonTestsRunner", "src\python_tests_runner\Python.PythonTestsRunner.csproj", "{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{142A6752-C2C2-4F95-B982-193418001B65}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + EndProjectSection 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 + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x64.Build.0 = DebugMono|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMono|x86.Build.0 = DebugMono|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x64.Build.0 = DebugWin|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWin|x86.Build.0 = DebugWin|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {097B4AC0-74E9-4C58-BCF8-C69746EC8271}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMono|x64.Build.0 = DebugMono|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMono|x86.Build.0 = DebugMono|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWin|x64.Build.0 = DebugWin|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWin|x86.Build.0 = DebugWin|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {6F401A34-273B-450F-9A4C-13550BE0767B}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x64.Build.0 = DebugMono|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMono|x86.Build.0 = DebugMono|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x64.Build.0 = DebugWin|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWin|x86.Build.0 = DebugWin|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {4165C59D-2822-499F-A6DB-EACA4C331EB5}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMono|x64.Build.0 = DebugMono|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMono|x86.Build.0 = DebugMono|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWin|x64.Build.0 = DebugWin|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWin|x86.Build.0 = DebugWin|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMono|x64.Build.0 = ReleaseMono|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMono|x86.Build.0 = ReleaseMono|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {E29DCF0A-5114-4A98-B1DD-71264B6EA349}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugMono|x64.ActiveCfg = DebugMono|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugMono|x86.ActiveCfg = DebugMono|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWin|x64.ActiveCfg = DebugWin|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWin|x64.Build.0 = DebugWin|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWin|x86.ActiveCfg = DebugWin|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWin|x86.Build.0 = DebugWin|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWin|x64.Build.0 = ReleaseWin|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWin|x86.Build.0 = ReleaseWin|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86 - {86E834DE-1139-4511-96CC-69636A56E7AC}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86 + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Debug|x64.ActiveCfg = Debug|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Debug|x64.Build.0 = Debug|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Debug|x86.ActiveCfg = Debug|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Debug|x86.Build.0 = Debug|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Release|Any CPU.Build.0 = Release|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Release|x64.ActiveCfg = Release|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Release|x64.Build.0 = Release|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Release|x86.ActiveCfg = Release|Any CPU + {4E8C8FE2-0FB8-4517-B2D9-5FB2D5FC849B}.Release|x86.Build.0 = Release|Any CPU + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Debug|Any CPU.ActiveCfg = Debug|x64 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Debug|Any CPU.Build.0 = Debug|x64 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Debug|x64.ActiveCfg = Debug|x64 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Debug|x64.Build.0 = Debug|x64 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Debug|x86.ActiveCfg = Debug|x86 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Debug|x86.Build.0 = Debug|x86 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Release|Any CPU.ActiveCfg = Release|x64 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Release|Any CPU.Build.0 = Release|x64 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Release|x64.ActiveCfg = Release|x64 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Release|x64.Build.0 = Release|x64 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Release|x86.ActiveCfg = Release|x86 + {E6B01706-00BA-4144-9029-186AC42FBE9A}.Release|x86.Build.0 = Release|x86 + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Debug|x64.ActiveCfg = Debug|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Debug|x64.Build.0 = Debug|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Debug|x86.ActiveCfg = Debug|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Debug|x86.Build.0 = Debug|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Release|Any CPU.Build.0 = Release|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Release|x64.ActiveCfg = Release|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Release|x64.Build.0 = Release|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Release|x86.ActiveCfg = Release|Any CPU + {F9F5FA13-BC52-4C0B-BC1C-FE3C0B8FCCDD}.Release|x86.Build.0 = Release|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Debug|Any CPU.Build.0 = Debug|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Debug|x64.ActiveCfg = Debug|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Debug|x64.Build.0 = Debug|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Debug|x86.ActiveCfg = Debug|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Debug|x86.Build.0 = Debug|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Release|Any CPU.ActiveCfg = Release|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Release|Any CPU.Build.0 = Release|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Release|x64.ActiveCfg = Release|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Release|x64.Build.0 = Release|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Release|x86.ActiveCfg = Release|Any CPU + {819E089B-4770-400E-93C6-4F7A35F0EA12}.Release|x86.Build.0 = Release|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Debug|x64.ActiveCfg = Debug|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Debug|x64.Build.0 = Debug|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Debug|x86.ActiveCfg = Debug|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Debug|x86.Build.0 = Debug|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Release|Any CPU.Build.0 = Release|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Release|x64.ActiveCfg = Release|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Release|x64.Build.0 = Release|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Release|x86.ActiveCfg = Release|Any CPU + {14EF9518-5BB7-4F83-8686-015BD2CC788E}.Release|x86.Build.0 = Release|Any CPU + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Debug|Any CPU.ActiveCfg = Debug|x64 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Debug|Any CPU.Build.0 = Debug|x64 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Debug|x64.ActiveCfg = Debug|x64 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Debug|x64.Build.0 = Debug|x64 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Debug|x86.ActiveCfg = Debug|x86 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Debug|x86.Build.0 = Debug|x86 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Release|Any CPU.ActiveCfg = Release|x64 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Release|Any CPU.Build.0 = Release|x64 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Release|x64.ActiveCfg = Release|x64 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Release|x64.Build.0 = Release|x64 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Release|x86.ActiveCfg = Release|x86 + {4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Release|x86.Build.0 = Release|x86 + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Debug|x64.ActiveCfg = Debug|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Debug|x64.Build.0 = Debug|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Debug|x86.ActiveCfg = Debug|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Debug|x86.Build.0 = Debug|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Release|Any CPU.Build.0 = Release|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Release|x64.ActiveCfg = Release|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Release|x64.Build.0 = Release|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Release|x86.ActiveCfg = Release|Any CPU + {F2FB6DA3-318E-4F30-9A1F-932C667E38C5}.Release|x86.Build.0 = Release|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|x64.ActiveCfg = Debug|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|x64.Build.0 = Debug|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|x86.ActiveCfg = Debug|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|x86.Build.0 = Debug|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|Any CPU.Build.0 = Release|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x64.ActiveCfg = Release|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x64.Build.0 = Release|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x86.ActiveCfg = Release|Any CPU + {35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = src\console\Console.csproj - Policies = $0 - $0.VersionControlPolicy = $1 - $1.inheritsSet = Mono - $0.ChangeLogPolicy = $2 - $2.UpdateMode = None - $2.MessageStyle = $3 - $3.LineAlign = 0 - $2.inheritsSet = Mono + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C8845072-C642-4858-8627-27E862AD21BB} EndGlobalSection EndGlobal diff --git a/pythonnet/.gitignore b/pythonnet/.gitignore new file mode 100644 index 000000000..7ebf7884b --- /dev/null +++ b/pythonnet/.gitignore @@ -0,0 +1,3 @@ +mono/ +netfx/ +runtime/ diff --git a/pythonnet/__init__.py b/pythonnet/__init__.py new file mode 100644 index 000000000..4f162dcd5 --- /dev/null +++ b/pythonnet/__init__.py @@ -0,0 +1,71 @@ +import os +import sys +import clr_loader + +_RUNTIME = None +_LOADER_ASSEMBLY = None +_FFI = None +_LOADED = False + + +def set_runtime(runtime): + global _RUNTIME + if _LOADED: + raise RuntimeError("The runtime {runtime} has already been loaded".format(_RUNTIME)) + + _RUNTIME = runtime + + +def set_default_runtime() -> None: + if sys.platform == 'win32': + set_runtime(clr_loader.get_netfx()) + else: + set_runtime(clr_loader.get_mono()) + + +def load(): + global _FFI, _LOADED, _LOADER_ASSEMBLY + + if _LOADED: + return + + from .find_libpython import linked_libpython + from os.path import join, dirname + + if _RUNTIME is None: + # TODO: Warn, in the future the runtime must be set explicitly, either + # as a config/env variable or via set_runtime + set_default_runtime() + + dll_path = join(dirname(__file__), "runtime", "Python.Runtime.dll") + libpython = linked_libpython() + + if libpython and _FFI is None and sys.platform != "win32": + # Load and leak libpython handle s.t. the .NET runtime doesn't dlcloses + # it + import posix + + import cffi + _FFI = cffi.FFI() + _FFI.dlopen(libpython, posix.RTLD_NODELETE | posix.RTLD_LOCAL) + + _LOADER_ASSEMBLY = _RUNTIME.get_assembly(dll_path) + + func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Initialize"] + if func(f"{libpython or ''}".encode("utf8")) != 0: + raise RuntimeError("Failed to initialize Python.Runtime.dll") + + import atexit + atexit.register(unload) + + +def unload(): + global _RUNTIME + if _LOADER_ASSEMBLY is not None: + func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Shutdown"] + if func(b"full_shutdown") != 0: + raise RuntimeError("Failed to call Python.NET shutdown") + + if _RUNTIME is not None: + # TODO: Add explicit `close` to clr_loader + _RUNTIME = None diff --git a/pythonnet/find_libpython/__init__.py b/pythonnet/find_libpython/__init__.py new file mode 100644 index 000000000..185540c8f --- /dev/null +++ b/pythonnet/find_libpython/__init__.py @@ -0,0 +1,399 @@ +#!/usr/bin/env python + +""" +Locate libpython associated with this Python executable. +""" + +# License +# +# Copyright 2018, Takafumi Arakaki +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from __future__ import print_function, absolute_import + +from logging import getLogger +import ctypes.util +import functools +import os +import sys +import sysconfig + +logger = getLogger("find_libpython") + +is_windows = os.name == "nt" +is_apple = sys.platform == "darwin" + +SHLIB_SUFFIX = sysconfig.get_config_var("SHLIB_SUFFIX") +if SHLIB_SUFFIX is None: + if is_windows: + SHLIB_SUFFIX = ".dll" + else: + SHLIB_SUFFIX = ".so" +if is_apple: + # sysconfig.get_config_var("SHLIB_SUFFIX") can be ".so" in macOS. + # Let's not use the value from sysconfig. + SHLIB_SUFFIX = ".dylib" + + +def linked_libpython(): + """ + Find the linked libpython using dladdr (in *nix). + + Returns + ------- + path : str or None + A path to linked libpython. Return `None` if statically linked. + """ + if is_windows: + return _linked_libpython_windows() + return _linked_libpython_unix() + + +class Dl_info(ctypes.Structure): + _fields_ = [ + ("dli_fname", ctypes.c_char_p), + ("dli_fbase", ctypes.c_void_p), + ("dli_sname", ctypes.c_char_p), + ("dli_saddr", ctypes.c_void_p), + ] + + +def _linked_libpython_unix(): + libdl = ctypes.CDLL(ctypes.util.find_library("dl")) + libdl.dladdr.argtypes = [ctypes.c_void_p, ctypes.POINTER(Dl_info)] + libdl.dladdr.restype = ctypes.c_int + + dlinfo = Dl_info() + retcode = libdl.dladdr( + ctypes.cast(ctypes.pythonapi.Py_GetVersion, ctypes.c_void_p), + ctypes.pointer(dlinfo)) + if retcode == 0: # means error + return None + path = os.path.realpath(dlinfo.dli_fname.decode()) + if path == os.path.realpath(sys.executable): + return None + return path + + +def _linked_libpython_windows(): + """ + Based on: https://stackoverflow.com/a/16659821 + """ + from ctypes.wintypes import HANDLE, LPWSTR, DWORD + + GetModuleFileName = ctypes.windll.kernel32.GetModuleFileNameW + GetModuleFileName.argtypes = [HANDLE, LPWSTR, DWORD] + GetModuleFileName.restype = DWORD + + MAX_PATH = 260 + try: + buf = ctypes.create_unicode_buffer(MAX_PATH) + GetModuleFileName(ctypes.pythonapi._handle, buf, MAX_PATH) + return buf.value + except (ValueError, OSError): + return None + + + +def library_name(name, suffix=SHLIB_SUFFIX, is_windows=is_windows): + """ + Convert a file basename `name` to a library name (no "lib" and ".so" etc.) + + >>> library_name("libpython3.7m.so") # doctest: +SKIP + 'python3.7m' + >>> library_name("libpython3.7m.so", suffix=".so", is_windows=False) + 'python3.7m' + >>> library_name("libpython3.7m.dylib", suffix=".dylib", is_windows=False) + 'python3.7m' + >>> library_name("python37.dll", suffix=".dll", is_windows=True) + 'python37' + """ + if not is_windows and name.startswith("lib"): + name = name[len("lib"):] + if suffix and name.endswith(suffix): + name = name[:-len(suffix)] + return name + + +def append_truthy(list, item): + if item: + list.append(item) + + +def uniquifying(items): + """ + Yield items while excluding the duplicates and preserving the order. + + >>> list(uniquifying([1, 2, 1, 2, 3])) + [1, 2, 3] + """ + seen = set() + for x in items: + if x not in seen: + yield x + seen.add(x) + + +def uniquified(func): + """ Wrap iterator returned from `func` by `uniquifying`. """ + @functools.wraps(func) + def wrapper(*args, **kwds): + return uniquifying(func(*args, **kwds)) + return wrapper + + +@uniquified +def candidate_names(suffix=SHLIB_SUFFIX): + """ + Iterate over candidate file names of libpython. + + Yields + ------ + name : str + Candidate name libpython. + """ + LDLIBRARY = sysconfig.get_config_var("LDLIBRARY") + if LDLIBRARY and not LDLIBRARY.endswith(".a"): + yield LDLIBRARY + + LIBRARY = sysconfig.get_config_var("LIBRARY") + if LIBRARY and not LIBRARY.endswith(".a"): + yield os.path.splitext(LIBRARY)[0] + suffix + + dlprefix = "" if is_windows else "lib" + sysdata = dict( + v=sys.version_info, + # VERSION is X.Y in Linux/macOS and XY in Windows: + VERSION=(sysconfig.get_python_version() or + "{v.major}.{v.minor}".format(v=sys.version_info) or + sysconfig.get_config_var("VERSION")), + ABIFLAGS=(sysconfig.get_config_var("ABIFLAGS") or + sysconfig.get_config_var("abiflags") or ""), + ) + + for stem in [ + "python{VERSION}{ABIFLAGS}".format(**sysdata), + "python{VERSION}".format(**sysdata), + "python{v.major}".format(**sysdata), + "python", + ]: + yield dlprefix + stem + suffix + + + +@uniquified +def candidate_paths(suffix=SHLIB_SUFFIX): + """ + Iterate over candidate paths of libpython. + + Yields + ------ + path : str or None + Candidate path to libpython. The path may not be a fullpath + and may not exist. + """ + + yield linked_libpython() + + # List candidates for directories in which libpython may exist + lib_dirs = [] + append_truthy(lib_dirs, sysconfig.get_config_var('LIBPL')) + append_truthy(lib_dirs, sysconfig.get_config_var('srcdir')) + append_truthy(lib_dirs, sysconfig.get_config_var("LIBDIR")) + + # LIBPL seems to be the right config_var to use. It is the one + # used in python-config when shared library is not enabled: + # https://github.com/python/cpython/blob/v3.7.0/Misc/python-config.in#L55-L57 + # + # But we try other places just in case. + + if is_windows: + lib_dirs.append(os.path.join(os.path.dirname(sys.executable))) + else: + lib_dirs.append(os.path.join( + os.path.dirname(os.path.dirname(sys.executable)), + "lib")) + + # For macOS: + append_truthy(lib_dirs, sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")) + + lib_dirs.append(sys.exec_prefix) + lib_dirs.append(os.path.join(sys.exec_prefix, "lib")) + + lib_basenames = list(candidate_names(suffix=suffix)) + + for directory in lib_dirs: + for basename in lib_basenames: + yield os.path.join(directory, basename) + + # In macOS and Windows, ctypes.util.find_library returns a full path: + for basename in lib_basenames: + yield ctypes.util.find_library(library_name(basename)) + +# Possibly useful links: +# * https://packages.ubuntu.com/bionic/amd64/libpython3.6/filelist +# * https://github.com/Valloric/ycmd/issues/518 +# * https://github.com/Valloric/ycmd/pull/519 + + +def normalize_path(path, suffix=SHLIB_SUFFIX, is_apple=is_apple): + """ + Normalize shared library `path` to a real path. + + If `path` is not a full path, `None` is returned. If `path` does + not exists, append `SHLIB_SUFFIX` and check if it exists. + Finally, the path is canonicalized by following the symlinks. + + Parameters + ---------- + path : str ot None + A candidate path to a shared library. + """ + if not path: + return None + if not os.path.isabs(path): + return None + if os.path.exists(path): + return os.path.realpath(path) + if os.path.exists(path + suffix): + return os.path.realpath(path + suffix) + if is_apple: + return normalize_path(_remove_suffix_apple(path), + suffix=".so", is_apple=False) + return None + + +def _remove_suffix_apple(path): + """ + Strip off .so or .dylib. + + >>> _remove_suffix_apple("libpython.so") + 'libpython' + >>> _remove_suffix_apple("libpython.dylib") + 'libpython' + >>> _remove_suffix_apple("libpython3.7") + 'libpython3.7' + """ + if path.endswith(".dylib"): + return path[:-len(".dylib")] + if path.endswith(".so"): + return path[:-len(".so")] + return path + + +@uniquified +def finding_libpython(): + """ + Iterate over existing libpython paths. + + The first item is likely to be the best one. + + Yields + ------ + path : str + Existing path to a libpython. + """ + logger.debug("is_windows = %s", is_windows) + logger.debug("is_apple = %s", is_apple) + for path in candidate_paths(): + logger.debug("Candidate: %s", path) + normalized = normalize_path(path) + if normalized: + logger.debug("Found: %s", normalized) + yield normalized + else: + logger.debug("Not found.") + + +def find_libpython(): + """ + Return a path (`str`) to libpython or `None` if not found. + + Parameters + ---------- + path : str or None + Existing path to the (supposedly) correct libpython. + """ + for path in finding_libpython(): + return os.path.realpath(path) + + +def print_all(items): + for x in items: + print(x) + + +def cli_find_libpython(cli_op, verbose, export): + import logging + # Importing `logging` module here so that using `logging.debug` + # instead of `logger.debug` outside of this function becomes an + # error. + + if verbose: + logging.basicConfig( + format="%(levelname)s %(message)s", + level=logging.DEBUG) + + if cli_op == "list-all": + print_all(finding_libpython()) + elif cli_op == "candidate-names": + print_all(candidate_names()) + elif cli_op == "candidate-paths": + print_all(p for p in candidate_paths() if p and os.path.isabs(p)) + else: + path = find_libpython() + if path is None: + return 1 + if export: + print(f"PYTHONNET_PYDLL={path}") + else: + print(path, end="") + + +def main(args=None): + import argparse + parser = argparse.ArgumentParser( + description=__doc__) + parser.add_argument( + "--verbose", "-v", action="store_true", + help="Print debugging information.") + + group = parser.add_mutually_exclusive_group() + group.add_argument( + "--list-all", + action="store_const", dest="cli_op", const="list-all", + help="Print list of all paths found.") + group.add_argument( + "--candidate-names", + action="store_const", dest="cli_op", const="candidate-names", + help="Print list of candidate names of libpython.") + group.add_argument( + "--candidate-paths", + action="store_const", dest="cli_op", const="candidate-paths", + help="Print list of candidate paths of libpython.") + group.add_argument( + "--export", + action="store_true", + help="Print as an environment export expression" + ) + + ns = parser.parse_args(args) + parser.exit(cli_find_libpython(**vars(ns))) diff --git a/pythonnet/find_libpython/__main__.py b/pythonnet/find_libpython/__main__.py new file mode 100644 index 000000000..031df43e1 --- /dev/null +++ b/pythonnet/find_libpython/__main__.py @@ -0,0 +1,2 @@ +from . import main +main() diff --git a/src/tests/fixtures/.gitkeep b/pythonnet/runtime/.gitkeep similarity index 100% rename from src/tests/fixtures/.gitkeep rename to pythonnet/runtime/.gitkeep diff --git a/pythonnet/util/__init__.py b/pythonnet/util/__init__.py new file mode 100644 index 000000000..75d4bad8c --- /dev/null +++ b/pythonnet/util/__init__.py @@ -0,0 +1 @@ +from .find_libpython import find_libpython diff --git a/requirements.txt b/requirements.txt index 29c2e4566..f5aedfc3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ # Requirements for both Travis and AppVeyor -pytest==3.2.5 -coverage +pytest psutil # Coverage upload +coverage codecov -# Platform specific requirements -# pip; sys_platform == 'win32' -wheel; sys_platform == 'win32' -pycparser; sys_platform != 'win32' +wheel +pycparser +setuptools +clr-loader diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 19c6f9fc9..000000000 --- a/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[tool:pytest] -xfail_strict = True -# -r fsxX: show extra summary info for: (f)ailed, (s)kip, (x)failed, (X)passed -addopts = -r fsxX --color=yes --durations=5 diff --git a/setup.py b/setup.py index 9b7219008..c74ca2c8c 100644 --- a/setup.py +++ b/setup.py @@ -1,636 +1,161 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Setup script for building clr.pyd and dependencies using mono and into -an egg or wheel. -""" - -import collections -import fnmatch -import glob -import os -import subprocess -import sys -import sysconfig -from distutils import spawn -from distutils.command import install, build, build_ext, install_data, install_lib - -from setuptools import Extension, setup - -try: - from wheel import bdist_wheel -except ImportError: - bdist_wheel = None - -# Allow config/verbosity to be set from cli -# http://stackoverflow.com/a/4792601/5208670 -CONFIG = "Release" # Release or Debug -VERBOSITY = "normal" # quiet, minimal, normal, detailed, diagnostic - -is_64bits = sys.maxsize > 2 ** 32 -DEVTOOLS = "MsDev" if sys.platform == "win32" else "Mono" -ARCH = "x64" if is_64bits else "x86" -PY_MAJOR = sys.version_info[0] -PY_MINOR = sys.version_info[1] - -############################################################################### -# Windows Keys Constants for MSBUILD tools -RegKey = collections.namedtuple("RegKey", "sdk_name key value_name suffix") -vs_python = "Programs\\Common\\Microsoft\\Visual C++ for Python\\9.0\\WinSDK" -vs_root = "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{0}" -sdks_root = "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v{0}Win32Tools" -kits_root = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots" -kits_suffix = os.path.join("bin", ARCH) - -WIN_SDK_KEYS = [ - RegKey( - sdk_name="Windows Kit 10.0", - key=kits_root, - value_name="KitsRoot10", - suffix=os.path.join("bin", "10.0.16299.0", ARCH), - ), - RegKey( - sdk_name="Windows Kit 10.0", - key=kits_root, - value_name="KitsRoot10", - suffix=os.path.join("bin", "10.0.15063.0", ARCH), - ), - RegKey( - sdk_name="Windows Kit 10.0", - key=kits_root, - value_name="KitsRoot10", - suffix=kits_suffix, - ), - RegKey( - sdk_name="Windows Kit 8.1", - key=kits_root, - value_name="KitsRoot81", - suffix=kits_suffix, - ), - RegKey( - sdk_name="Windows Kit 8.0", - key=kits_root, - value_name="KitsRoot", - suffix=kits_suffix, - ), - RegKey( - sdk_name="Windows SDK 7.1A", - key=sdks_root.format("7.1A\\WinSDK-"), - value_name="InstallationFolder", - suffix="", - ), - RegKey( - sdk_name="Windows SDK 7.1", - key=sdks_root.format("7.1\\WinSDK"), - value_name="InstallationFolder", - suffix="", - ), - RegKey( - sdk_name="Windows SDK 7.0A", - key=sdks_root.format("7.0A\\WinSDK-"), - value_name="InstallationFolder", - suffix="", - ), - RegKey( - sdk_name="Windows SDK 7.0", - key=sdks_root.format("7.0\\WinSDK"), - value_name="InstallationFolder", - suffix="", - ), - RegKey( - sdk_name="Windows SDK 6.0A", - key=sdks_root.format("6.0A\\WinSDK"), - value_name="InstallationFolder", - suffix="", - ), -] - -VS_KEYS = ( - RegKey( - sdk_name="MSBuild 15", - key=vs_root.format("15.0"), - value_name="MSBuildToolsPath", - suffix="", - ), - RegKey( - sdk_name="MSBuild 14", - key=vs_root.format("14.0"), - value_name="MSBuildToolsPath", - suffix="", - ), - RegKey( - sdk_name="MSBuild 12", - key=vs_root.format("12.0"), - value_name="MSBuildToolsPath", - suffix="", - ), - RegKey( - sdk_name="MSBuild 4", - key=vs_root.format("4.0"), - value_name="MSBuildToolsPath", - suffix="", - ), - RegKey( - sdk_name="MSBuild 3.5", - key=vs_root.format("3.5"), - value_name="MSBuildToolsPath", - suffix="", - ), - RegKey( - sdk_name="MSBuild 2.0", - key=vs_root.format("2.0"), - value_name="MSBuildToolsPath", - suffix="", - ), -) - - -############################################################################### -def _check_output(*args, **kwargs): - """Check output wrapper for py2/py3 compatibility""" - output = subprocess.check_output(*args, **kwargs) - return output.decode("ascii") - - -def _get_interop_filename(): - """interopXX.cs is auto-generated as part of the build. - For common windows platforms pre-generated files are included - as most windows users won't have Clang installed, which is - required to generate the file. - """ - interop_filename = "interop{0}{1}{2}.cs".format( - PY_MAJOR, PY_MINOR, getattr(sys, "abiflags", "") - ) - return os.path.join("src", "runtime", interop_filename) +from setuptools import setup, Command, Extension +from setuptools.command.build_ext import build_ext +import distutils +from distutils.command import build +from subprocess import check_output, check_call -def _get_source_files(): - """Walk project and collect the files needed for ext_module""" - for ext in (".sln",): - for path in glob.glob("*" + ext): - yield path +import sys, os - for root, dirnames, filenames in os.walk("src"): - for ext in (".cs", ".csproj", ".snk", ".config", ".py", ".c", ".h", ".ico"): - for filename in fnmatch.filter(filenames, "*" + ext): - yield os.path.join(root, filename) - - for root, dirnames, filenames in os.walk("tools"): - for ext in (".exe", ".py", ".c", ".h"): - for filename in fnmatch.filter(filenames, "*" + ext): - yield os.path.join(root, filename) - - -def _get_long_description(): - """Helper to populate long_description for pypi releases""" - return open("README.rst").read() - - -def _update_xlat_devtools(): - global DEVTOOLS - if DEVTOOLS == "MsDev": - DEVTOOLS = "MsDev15" - elif DEVTOOLS == "Mono": - DEVTOOLS = "dotnet" - - -def _collect_installed_windows_kits_v10(winreg): - """Adds the installed Windows 10 kits to WIN_SDK_KEYS """ - global WIN_SDK_KEYS - installed_kits = [] - - with winreg.OpenKey( - winreg.HKEY_LOCAL_MACHINE, kits_root, 0, winreg.KEY_READ - ) as key: - i = 0 - while True: - try: - installed_kits.append(winreg.EnumKey(key, i)) - i += 1 - except WindowsError: - break +PY_MAJOR = sys.version_info[0] +PY_MINOR = sys.version_info[1] - def make_reg_key(version): - return RegKey( - sdk_name="Windows Kit 10.0", - key=kits_root, - value_name="KitsRoot10", - suffix=os.path.join("bin", version, ARCH), - ) - WIN_SDK_KEYS += [make_reg_key(e) for e in installed_kits if e.startswith("10.")] +class DotnetLib: + def __init__(self, name, path, **kwargs): + self.name = name + self.path = path + self.args = kwargs - # Make sure this function won't be called again - _collect_installed_windows_kits_v10 = lambda: None +class build_dotnet(Command): + """Build command for dotnet-cli based builds""" -class BuildExtPythonnet(build_ext.build_ext): - user_options = build_ext.build_ext.user_options + [("xplat", None, None)] + description = "Build DLLs with dotnet-cli" + user_options = [ + ("dotnet-config", None, "dotnet build configuration"), + ( + "inplace", + "i", + "ignore build-lib and put compiled extensions into the source " + + "directory alongside your pure Python modules", + ), + ] def initialize_options(self): - build_ext.build_ext.initialize_options(self) - self.xplat = None + self.dotnet_config = None + self.build_lib = None + self.inplace = False 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) - - # install packages using nuget - self._install_packages() - - dest_file = self.get_ext_fullpath(ext.name) - dest_dir = os.path.dirname(dest_file) - if not os.path.exists(dest_dir): - os.makedirs(dest_dir) - - # Up to Python 3.2 sys.maxunicode is used to determine the size of - # Py_UNICODE, but from 3.3 onwards Py_UNICODE is a typedef of wchar_t. - import ctypes - unicode_width = ctypes.sizeof(ctypes.c_wchar) - - defines = [ - "PYTHON{0}{1}".format(PY_MAJOR, PY_MINOR), - "UCS{0}".format(unicode_width), - ] - - if CONFIG == "Debug": - defines.extend(["DEBUG", "TRACE"]) - - if sys.platform != "win32" and (DEVTOOLS == "Mono" or DEVTOOLS == "dotnet"): - on_darwin = sys.platform == "darwin" - - # Check if --enable-shared was set when Python was built - enable_shared = sysconfig.get_config_var("Py_ENABLE_SHARED") - if enable_shared: - # Double-check if libpython is linked dynamically with python - ldd_cmd = ["otool", "-L"] if on_darwin else ["ldd"] - lddout = _check_output(ldd_cmd + [sys.executable]) - if "libpython" not in lddout: - enable_shared = False - - if not enable_shared: - defines.append("PYTHON_WITHOUT_ENABLE_SHARED") - - if sys.platform == "win32": - defines.append("WINDOWS") - - if hasattr(sys, "abiflags"): - if "d" in sys.abiflags: - defines.append("PYTHON_WITH_PYDEBUG") - if "m" in sys.abiflags: - defines.append("PYTHON_WITH_PYMALLOC") - - # check the interop file exists, and create it if it doesn't - interop_file = _get_interop_filename() - if not os.path.exists(interop_file): - self.debug_print("Creating {0}".format(interop_file)) - geninterop = os.path.join("tools", "geninterop", "geninterop.py") - subprocess.check_call([sys.executable, geninterop, interop_file]) - - if DEVTOOLS == "MsDev": - _xbuild = '"{0}"'.format(self._find_msbuild_tool("msbuild.exe")) - _config = "{0}Win".format(CONFIG) - _solution_file = "pythonnet.sln" - _custom_define_constants = False - elif DEVTOOLS == "MsDev15": - _xbuild = '"{0}"'.format(self._find_msbuild_tool_15()) - _config = "{0}Win".format(CONFIG) - _solution_file = "pythonnet.15.sln" - _custom_define_constants = True - elif DEVTOOLS == "Mono": - _xbuild = "xbuild" - _config = "{0}Mono".format(CONFIG) - _solution_file = "pythonnet.sln" - _custom_define_constants = False - elif DEVTOOLS == "dotnet": - _xbuild = "dotnet msbuild" - _config = "{0}Mono".format(CONFIG) - _solution_file = "pythonnet.15.sln" - _custom_define_constants = True + if self.dotnet_config is None: + self.dotnet_config = "release" + + build = self.distribution.get_command_obj("build") + build.ensure_finalized() + if self.inplace: + self.build_lib = "." else: - raise NotImplementedError( - "DevTool {0} not supported (use MsDev/MsDev15/Mono/dotnet)".format( - DEVTOOLS - ) - ) + self.build_lib = build.build_lib - cmd = [ - _xbuild, - _solution_file, - "/p:Configuration={}".format(_config), - "/p:Platform={}".format(ARCH), - '/p:{}DefineConstants="{}"'.format( - "Custom" if _custom_define_constants else "", "%3B".join(defines) - ), - '/p:PythonBuildDir="{}"'.format(os.path.abspath(dest_dir)), - '/p:PythonInteropFile="{}"'.format(os.path.basename(interop_file)), - "/p:PackageId=pythonnet_py{0}{1}_{2}".format(PY_MAJOR, PY_MINOR, ARCH), - "/verbosity:{}".format(VERBOSITY), - ] - - manifest = self._get_manifest(dest_dir) - if manifest: - cmd.append('/p:PythonManifest="{0}"'.format(manifest)) - - self.debug_print("Building: {0}".format(" ".join(cmd))) - 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 == "MsDev15" or DEVTOOLS == "dotnet": - subprocess.check_call( - " ".join( - cmd - + [ - '"/t:Console_15:publish;Python_EmbeddingTest_15:publish"', - "/p:TargetFramework=netcoreapp2.0", - ] - ), - shell=use_shell, - ) - subprocess.check_call( - " ".join( - cmd - + [ - '"/t:Python_PerformanceTests:publish"', - "/p:TargetFramework=net461", - ] - ), - shell=use_shell, - ) - if DEVTOOLS == "Mono" or DEVTOOLS == "dotnet": - self._build_monoclr() - - def _get_manifest(self, build_dir): - if DEVTOOLS != "MsDev" and DEVTOOLS != "MsDev15": - return - mt = self._find_msbuild_tool("mt.exe", use_windows_sdk=True) - manifest = os.path.abspath(os.path.join(build_dir, "app.manifest")) - cmd = [ - mt, - '-inputresource:"{0}"'.format(sys.executable), - '-out:"{0}"'.format(manifest), - ] - self.debug_print("Extracting manifest from {}".format(sys.executable)) - subprocess.check_call(" ".join(cmd), shell=False) - return manifest - - def _build_monoclr(self): - 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) - cflags = mono_cflags.strip() - libs = mono_libs.strip() - - # build the clr python module - clr_ext = Extension( - "clr", - language="c++", - sources=["src/monoclr/pynetinit.c", "src/monoclr/clrmod.c"], - extra_compile_args=cflags.split(" "), - extra_link_args=libs.split(" "), - ) - - build_ext.build_ext.build_extension(self, clr_ext) - - def _install_packages(self): - """install packages using nuget""" - 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 + def run(self): + dotnet_modules = self.distribution.dotnet_libs + + for lib in dotnet_modules: + output = os.path.join( + os.path.abspath(self.build_lib), lib.args.pop("output") ) - self.debug_print("Updating packages with xplat: {0}".format(cmd)) - subprocess.check_call(cmd, shell=use_shell) - else: - nuget = os.path.join("tools", "nuget", "nuget.exe") - - if DEVTOOLS == "Mono": - nuget = "mono {0}".format(nuget) - - cmd = "{0} update -self".format(nuget) - self.debug_print("Updating NuGet: {0}".format(cmd)) - subprocess.check_call(cmd, shell=use_shell) - - 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: - basePaths = subprocess.check_output( - [ - "tools\\vswhere\\vswhere.exe", - "-latest", - "-version", - "[15.0,)", - "-requires", - "Microsoft.Component.MSBuild", - "-find", - "MSBuild\**\Bin\MSBuild.exe", - ] - ).splitlines() - if len(basePaths): - return basePaths[0].decode(sys.stdout.encoding or "utf-8") - except: - pass # keep trying to search by old method. - - # Search in PATH first - path = spawn.find_executable(tool) - if path: - return path - - # Search within registry to find build tools - import winreg - - _collect_installed_windows_kits_v10(winreg) - - keys_to_check = WIN_SDK_KEYS if use_windows_sdk else VS_KEYS - hklm = winreg.HKEY_LOCAL_MACHINE - for rkey in keys_to_check: - try: - with winreg.OpenKey(hklm, rkey.key) as hkey: - val, type_ = winreg.QueryValueEx(hkey, rkey.value_name) - if type_ != winreg.REG_SZ: - continue - path = os.path.join(val, rkey.suffix, tool) - if os.path.exists(path): - self.debug_print( - "Using {0} from {1}".format(tool, rkey.sdk_name) - ) - return path - except WindowsError: - # Key doesn't exist - pass - - # Add Visual C++ for Python as a fall-back in case one - # of the other Windows SDKs isn't installed. - # TODO: Extend checking by using setuptools/msvc.py? - if use_windows_sdk: - sdk_name = "Visual C++ for Python" - localappdata = os.environ["LOCALAPPDATA"] - suffix = "Bin\\x64" if ARCH == "x64" else "Bin" - path = os.path.join(localappdata, vs_python, suffix, tool) - if os.path.exists(path): - self.debug_print("Using {0} from {1}".format(tool, sdk_name)) - return path - - 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: - basePaths = subprocess.check_output( + rename = lib.args.pop("rename", {}) + + opts = sum( [ - "tools\\vswhere\\vswhere.exe", - "-latest", - "-version", - "[15.0,)", - "-requires", - "Microsoft.Component.MSBuild", - "-find", - "MSBuild\**\Bin\MSBuild.exe", - ] - ).splitlines() - if len(basePaths): - return basePaths[0].decode(sys.stdout.encoding or "utf-8") - 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) + ["--" + name.replace("_", "-"), value] + for name, value in lib.args.items() + ], + [], ) + opts.extend(["--configuration", self.dotnet_config]) + opts.extend(["--output", output]) -class InstallLibPythonnet(install_lib.install_lib): - def install(self): - if not os.path.isdir(self.build_dir): - self.warn( - "'{0}' does not exist -- no Python modules" - " to install".format(self.build_dir) - ) - return + self.announce("Running dotnet build...", level=distutils.log.INFO) + self.spawn(["dotnet", "build", lib.path] + opts) - if not os.path.exists(self.install_dir): - self.mkpath(self.install_dir) + for k, v in rename.items(): + source = os.path.join(output, k) + dest = os.path.join(output, v) - # only copy clr.pyd/.so - for srcfile in glob.glob(os.path.join(self.build_dir, "clr.*")): - destfile = os.path.join(self.install_dir, os.path.basename(srcfile)) - self.copy_file(srcfile, destfile) + if os.path.isfile(source): + try: + os.remove(dest) + except OSError: + pass + self.move_file(src=source, dst=dest, level=distutils.log.INFO) + else: + self.warn( + "Can't find file to rename: {}, current dir: {}".format( + source, os.getcwd() + ) + ) -class InstallDataPythonnet(install_data.install_data): - def run(self): - build_cmd = self.get_finalized_command("build_ext") - install_cmd = self.get_finalized_command("install") - build_lib = os.path.abspath(build_cmd.build_lib) - install_platlib = os.path.relpath(install_cmd.install_platlib, self.install_dir) - - for i, data_files in enumerate(self.data_files): - if isinstance(data_files, str): - self.data_files[i] = data_files[i].format(build_lib=build_lib) - else: - for j, filename in enumerate(data_files[1]): - data_files[1][j] = filename.format(build_lib=build_lib) - dest = data_files[0].format(install_platlib=install_platlib) - self.data_files[i] = dest, data_files[1] - return install_data.install_data.run(self) +# Add build_dotnet to the build tasks: +from distutils.command.build import build as _build +from setuptools.command.develop import develop as _develop +from wheel.bdist_wheel import bdist_wheel as _bdist_wheel +from setuptools import Distribution +import setuptools -class InstallPythonnet(install.install): - user_options = install.install.user_options + [("xplat", None, None)] +class build(_build): + sub_commands = _build.sub_commands + [("build_dotnet", None)] - def initialize_options(self): - install.install.initialize_options(self) - self.xplat = None - def finalize_options(self): - install.install.finalize_options(self) +class develop(_develop): + def install_for_development(self): + # Build extensions in-place + self.reinitialize_command("build_dotnet", inplace=1) + self.run_command("build_dotnet") - def run(self): - if self.xplat: - _update_xlat_devtools() - return install.install.run(self) + return super().install_for_development() -if bdist_wheel: - 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 +class bdist_wheel(_bdist_wheel): + def finalize_options(self): + # Monkey patch bdist_wheel to think the package is pure even though we + # include DLLs + super().finalize_options() + self.root_is_pure = True - 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) +# Monkey-patch Distribution s.t. it supports the dotnet_libs attribute +Distribution.dotnet_libs = None - ############################################################################### +cmdclass = { + "build": build, + "build_dotnet": build_dotnet, + "develop": develop, + "bdist_wheel": bdist_wheel, +} -setupdir = os.path.dirname(__file__) -if setupdir: - os.chdir(setupdir) +with open("README.rst", "r") as f: + long_description = f.read() -cmdclass={ - "install": InstallPythonnet, - "build_ext": BuildExtPythonnet, - "install_lib": InstallLibPythonnet, - "install_data": InstallDataPythonnet, -} -if bdist_wheel: - cmdclass["bdist_wheel"] = BDistWheelPythonnet +dotnet_libs = [ + DotnetLib( + "python-runtime", + "src/runtime/Python.Runtime.csproj", + output="pythonnet/runtime", + ) +] setup( + cmdclass=cmdclass, name="pythonnet", - version="3.0.0dev1", + version="3.0.0.dev1", description=".Net and Mono integration for Python", url="https://pythonnet.github.io/", license="MIT", author="The Contributors of the Python.NET Project", author_email="pythonnet@python.org", - install_requires=["pycparser"], - long_description=_get_long_description(), - ext_modules=[Extension("clr", sources=list(_get_source_files()))], - data_files=[("{install_platlib}", ["{build_lib}/Python.Runtime.dll"])], - cmdclass=cmdclass, + packages=["pythonnet", "pythonnet.find_libpython"], + install_requires=["clr_loader"], + long_description=long_description, + py_modules=["clr"], + dotnet_libs=dotnet_libs, classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", @@ -640,6 +165,7 @@ def run(self): "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Operating System :: MacOS :: MacOS X", diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs deleted file mode 100644 index ab79c56eb..000000000 --- a/src/SharedAssemblyInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Reflection; -using System.Resources; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("pythonnet")] -[assembly: AssemblyProduct("Python.NET")] -[assembly: AssemblyCopyright("Copyright (c) 2006-2020 the contributors of the Python.NET project")] -[assembly: AssemblyTrademark("")] - -[assembly: AssemblyCulture("")] -[assembly: NeutralResourcesLanguage("")] - -[assembly: CLSCompliant(true)] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// 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("3.0.0")] diff --git a/src/clrmodule/ClrModule.cs b/src/clrmodule/ClrModule.cs deleted file mode 100644 index e19e58594..000000000 --- a/src/clrmodule/ClrModule.cs +++ /dev/null @@ -1,113 +0,0 @@ -//============================================================================ -// This file replaces the hand-maintained stub that used to implement clr.dll. -// This is a line-by-line port from IL back to C#. -// We now use RGiesecke.DllExport on the required static init method so it can be -// loaded by a standard CPython interpreter as an extension module. When it -// is loaded, it bootstraps the managed runtime integration layer and defers -// to it to do initialization and put the clr module into sys.modules, etc. - -// The "USE_PYTHON_RUNTIME_*" defines control what extra evidence is used -// to help the CLR find the appropriate Python.Runtime assembly. - -// If defined, the "pythonRuntimeVersionString" variable must be set to -// Python.Runtime's current version. -#define USE_PYTHON_RUNTIME_VERSION - -// If defined, the "PythonRuntimePublicKeyTokenData" data array must be -// set to Python.Runtime's public key token. (sn -T Python.Runtin.dll) -#define USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN - -// If DEBUG is defined in the Build Properties, a few Console.WriteLine -// calls are made to indicate what's going on during the load... -//============================================================================ -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using RGiesecke.DllExport; - -public class clrModule -{ - [DllExport("PyInit_clr", CallingConvention.StdCall)] - public static IntPtr PyInit_clr() - { - DebugPrint("Attempting to load 'Python.Runtime' using standard binding rules."); -#if USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN - var pythonRuntimePublicKeyTokenData = new byte[] { 0x50, 0x00, 0xfe, 0xa6, 0xcb, 0xa7, 0x02, 0xdd }; -#endif - - // Attempt to find and load Python.Runtime using standard assembly binding rules. - // This roughly translates into looking in order: - // - GAC - // - ApplicationBase - // - A PrivateBinPath under ApplicationBase - // With an unsigned assembly, the GAC is skipped. - var pythonRuntimeName = new AssemblyName("Python.Runtime") - { -#if USE_PYTHON_RUNTIME_VERSION - // Has no effect until SNK works. Keep updated anyways. - Version = new Version("2.5.0"), -#endif - CultureInfo = CultureInfo.InvariantCulture - }; -#if USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN - pythonRuntimeName.SetPublicKeyToken(pythonRuntimePublicKeyTokenData); -#endif - // We've got the AssemblyName with optional features; try to load it. - Assembly pythonRuntime; - try - { - pythonRuntime = Assembly.Load(pythonRuntimeName); - DebugPrint("Success loading 'Python.Runtime' using standard binding rules."); - } - catch (IOException) - { - DebugPrint("'Python.Runtime' not found using standard binding rules."); - try - { - // If the above fails for any reason, we fallback to attempting to load "Python.Runtime.dll" - // from the directory this assembly is running in. "This assembly" is probably "clr.pyd", - // sitting somewhere in PYTHONPATH. This is using Assembly.LoadFrom, and inherits all the - // caveats of that call. See MSDN docs for details. - // Suzanne Cook's blog is also an excellent source of info on this: - // http://blogs.msdn.com/suzcook/ - // http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx - // http://blogs.msdn.com/suzcook/archive/2003/06/13/57180.aspx - - Assembly executingAssembly = Assembly.GetExecutingAssembly(); - string assemblyDirectory = Path.GetDirectoryName(executingAssembly.Location); - if (assemblyDirectory == null) - { - throw new InvalidOperationException(executingAssembly.Location); - } - string pythonRuntimeDllPath = Path.Combine(assemblyDirectory, "Python.Runtime.dll"); - DebugPrint($"Attempting to load Python.Runtime from: '{pythonRuntimeDllPath}'."); - pythonRuntime = Assembly.LoadFrom(pythonRuntimeDllPath); - DebugPrint($"Success loading 'Python.Runtime' from: '{pythonRuntimeDllPath}'."); - } - catch (InvalidOperationException) - { - DebugPrint("Could not load 'Python.Runtime'."); - return IntPtr.Zero; - } - } - - // Once here, we've successfully loaded SOME version of Python.Runtime - // So now we get the PythonEngine and execute the InitExt method on it. - Type pythonEngineType = pythonRuntime.GetType("Python.Runtime.PythonEngine"); - - return (IntPtr)pythonEngineType.InvokeMember("InitExt", BindingFlags.InvokeMethod, null, null, null); - } - - /// - /// Substitute for Debug.Writeline(...). Ideally we would use Debug.Writeline - /// but haven't been able to configure the TRACE from within Python. - /// - [Conditional("DEBUG")] - private static void DebugPrint(string str) - { - Console.WriteLine(str); - } -} diff --git a/src/clrmodule/Properties/AssemblyInfo.cs b/src/clrmodule/Properties/AssemblyInfo.cs deleted file mode 100644 index 939f4171f..000000000 --- a/src/clrmodule/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("clrmodule")] -[assembly: AssemblyDescription("")] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("ae10d6a4-55c2-482f-9716-9988e6c169e3")] diff --git a/src/clrmodule/clrmodule.15.csproj b/src/clrmodule/clrmodule.15.csproj deleted file mode 100644 index 7fc9c2dda..000000000 --- a/src/clrmodule/clrmodule.15.csproj +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - net40 - x64;x86 - DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 - clrmodule - clrmodule - clrmodule - 2.5.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/clrmodule/clrmodule.csproj b/src/clrmodule/clrmodule.csproj deleted file mode 100644 index 6e5ff4966..000000000 --- a/src/clrmodule/clrmodule.csproj +++ /dev/null @@ -1,95 +0,0 @@ - - - - Debug - AnyCPU - {86E834DE-1139-4511-96CC-69636A56E7AC} - Library - clrmodule - clrmodule - bin\clrmodule.xml - bin\ - v4.0 - - 1591 - ..\..\ - $(SolutionDir)\bin\ - Properties - 6 - true - prompt - - - x86 - - - x64 - - - true - PYTHON2;TRACE;DEBUG - full - - - PYTHON2 - true - pdbonly - - - true - PYTHON2;TRACE;DEBUG - full - - - PYTHON2 - true - pdbonly - - - true - PYTHON3;TRACE;DEBUG - full - - - PYTHON3 - true - pdbonly - - - true - PYTHON3;TRACE;DEBUG - full - - - PYTHON3 - true - pdbonly - - - - ..\..\packages\UnmanagedExports.1.2.7\lib\net\RGiesecke.DllExport.Metadata.dll - False - - - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - $(TargetPath) - $(TargetDir)$(TargetName).pdb - - - - - - - diff --git a/src/clrmodule/packages.config b/src/clrmodule/packages.config deleted file mode 100644 index 2a95dc54d..000000000 --- a/src/clrmodule/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/console/Console.15.csproj b/src/console/Console.15.csproj deleted file mode 100644 index 3c51caa31..000000000 --- a/src/console/Console.15.csproj +++ /dev/null @@ -1,94 +0,0 @@ - - - - net40;netcoreapp2.0 - x64;x86 - DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 - Exe - nPython - Python.Runtime - nPython - 2.5.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/Console.csproj b/src/console/Console.csproj index ea88b6356..08854cfc9 100644 --- a/src/console/Console.csproj +++ b/src/console/Console.csproj @@ -1,101 +1,27 @@ - - + - Debug - AnyCPU - {E29DCF0A-5114-4A98-B1DD-71264B6EA349} + net472;netcoreapp3.1 + x64;x86 Exe nPython Python.Runtime - bin\nPython.xml - bin\ - v4.0 - - 1591 - ..\..\ - $(SolutionDir)\bin\ - Properties - 6 + nPython python-clear.ico - prompt - - x86 - - - x64 - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - $(PythonManifest) - - - - - - - - Properties\SharedAssemblyInfo.cs - - - + - + Python.Runtime.dll - - {097b4ac0-74e9-4c58-bcf8-c69746ec8271} - Python.Runtime - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + - - - - diff --git a/src/console/Properties/AssemblyInfo.cs b/src/console/Properties/AssemblyInfo.cs deleted file mode 100644 index 081ae0c94..000000000 --- a/src/console/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Reflection; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Python Console")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyDefaultAlias("python.exe")] diff --git a/src/console/pythonconsole.cs b/src/console/pythonconsole.cs index 912e9bb0d..bf17848f7 100644 --- a/src/console/pythonconsole.cs +++ b/src/console/pythonconsole.cs @@ -26,7 +26,7 @@ private PythonConsole() [STAThread] public static int Main(string[] args) { - // Only net40 is capable to safely inject python.runtime.dll into resources. + // Only .NET Framework 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; diff --git a/src/domain_tests/App.config b/src/domain_tests/App.config new file mode 100644 index 000000000..56efbc7b5 --- /dev/null +++ b/src/domain_tests/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/domain_tests/Python.DomainReloadTests.csproj b/src/domain_tests/Python.DomainReloadTests.csproj new file mode 100644 index 000000000..54196f210 --- /dev/null +++ b/src/domain_tests/Python.DomainReloadTests.csproj @@ -0,0 +1,26 @@ + + + + net472 + bin\ + Exe + + + + + + + + + + + + + + + + + + + + diff --git a/src/domain_tests/TestRunner.cs b/src/domain_tests/TestRunner.cs new file mode 100644 index 000000000..a21297829 --- /dev/null +++ b/src/domain_tests/TestRunner.cs @@ -0,0 +1,1321 @@ +// We can't refer to or use Python.Runtime here. +// We want it to be loaded only inside the subdomains +using System; +using Microsoft.CSharp; +using System.CodeDom.Compiler; +using System.IO; +using System.Linq; + +namespace Python.DomainReloadTests +{ + /// + /// This class provides an executable that can run domain reload tests. + /// The setup is a bit complicated: + /// 1. pytest runs test_*.py in this directory. + /// 2. test_classname runs Python.DomainReloadTests.exe (this class) with an argument + /// 3. This class at runtime creates a directory that has both C# and + /// python code, and compiles the C#. + /// 4. This class then runs the C# code. + /// + /// But there's a bit more indirection. This class compiles a DLL that + /// contains code that will change. + /// Then, the test case: + /// * Compiles some code, loads it into a domain, runs python that refers to it. + /// * Unload the domain, re-runs the domain to make sure domain reload happens correctly. + /// * Compile a new piece of code, load it into a new domain, run a new piece of + /// Python code to test the objects after they've been deleted or modified in C#. + /// * Unload the domain. Reload the domain, run the same python again. + /// + /// This class gets built into an executable which takes one argument: + /// which test case to run. That's because pytest assumes we'll run + /// everything in one process, but we really want a clean process on each + /// test case to test the init/reload/teardown parts of the domain reload. + /// + /// + class TestRunner + { + const string TestAssemblyName = "DomainTests"; + + class TestCase + { + /// + /// The key to pass as an argument to choose this test. + /// + public string Name; + + /// + /// The C# code to run in the first domain. + /// + public string DotNetBefore; + + /// + /// The C# code to run in the second domain. + /// + public string DotNetAfter; + + /// + /// The Python code to run as a module that imports the C#. + /// It should have two functions: before_reload() and after_reload(). + /// Before will be called twice when DotNetBefore is loaded; + /// after will also be called twice when DotNetAfter is loaded. + /// To make the test fail, have those functions raise exceptions. + /// + /// Make sure there's no leading spaces since Python cares. + /// + public string PythonCode; + } + + static TestCase[] Cases = new TestCase[] + { + new TestCase + { + Name = "class_rename", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Before { } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class After { } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace + +def before_reload(): + sys.my_cls = TestNamespace.Before + + +def after_reload(): + assert sys.my_cls is not None + try: + foo = TestNamespace.Before + except AttributeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + ", + }, + + new TestCase + { + Name = "static_member_rename", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls { public static int Before() { return 5; } } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls { public static int After() { return 10; } } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace + +def before_reload(): + if not hasattr(sys, 'my_cls'): + sys.my_cls = TestNamespace.Cls + sys.my_fn = TestNamespace.Cls.Before + assert 5 == sys.my_fn() + assert 5 == TestNamespace.Cls.Before() + +def after_reload(): + + # We should have reloaded the class so we can access the new function. + assert 10 == sys.my_cls.After() + assert True is True + + try: + # We should have reloaded the class. The old function still exists, but is now invalid. + sys.my_cls.Before() + except AttributeError: + print('Caught expected TypeError') + else: + raise AssertionError('Failed to throw exception: expected TypeError calling class member that no longer exists') + + assert sys.my_fn is not None + + try: + # Unbound functions still exist. They will error out when called though. + sys.my_fn() + except TypeError: + print('Caught expected TypeError') + else: + raise AssertionError('Failed to throw exception: expected TypeError calling unbound .NET function that no longer exists') + ", + }, + + + new TestCase + { + Name = "member_rename", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls { public int Before() { return 5; } } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls { public int After() { return 10; } } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace + +def before_reload(): + sys.my_cls = TestNamespace.Cls() + sys.my_fn = TestNamespace.Cls().Before + sys.my_fn() + TestNamespace.Cls().Before() + +def after_reload(): + + # We should have reloaded the class so we can access the new function. + assert 10 == sys.my_cls.After() + assert True is True + + try: + # We should have reloaded the class. The old function still exists, but is now invalid. + sys.my_cls.Before() + except AttributeError: + print('Caught expected TypeError') + else: + raise AssertionError('Failed to throw exception: expected TypeError calling class member that no longer exists') + + assert sys.my_fn is not None + + try: + # Unbound functions still exist. They will error out when called though. + sys.my_fn() + except TypeError: + print('Caught expected TypeError') + else: + raise AssertionError('Failed to throw exception: expected TypeError calling unbound .NET function that no longer exists') + ", + }, + + new TestCase + { + Name = "field_rename", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + static public int Before = 2; + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + static public int After = 4; + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +from TestNamespace import Cls + +def before_reload(): + sys.my_int = Cls.Before + +def after_reload(): + print(sys.my_int) + try: + assert 2 == Cls.Before + except AttributeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') +", + }, + new TestCase + { + Name = "property_rename", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + static public int Before { get { return 2; } } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + static public int After { get { return 4; } } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +from TestNamespace import Cls + +def before_reload(): + sys.my_int = Cls.Before + +def after_reload(): + print(sys.my_int) + try: + assert 2 == Cls.Before + except AttributeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') +", + }, + + new TestCase + { + Name = "event_rename", + DotNetBefore = @" + using System; + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static event Action Before; + public static void Call() + { + Before(); + } + } + }", + DotNetAfter = @" + using System; + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static event Action After; + public static void Call() + { + After(); + } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +from TestNamespace import Cls + +called = False + +def callback_function(): + global called + called = True + +def before_reload(): + global called + called = False + Cls.Before += callback_function + Cls.Call() + assert called is True + +def after_reload(): + global called + assert called is True + called = False + Cls.Call() + assert called is False +", + }, + + new TestCase + { + Name = "namespace_rename", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public int Foo; + public Cls(int i) + { + Foo = i; + } + } + }", + DotNetAfter = @" + namespace NewTestNamespace + { + [System.Serializable] + public class Cls + { + public int Foo; + public Cls(int i) + { + Foo = i; + } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace + +def before_reload(): + sys.my_cls = TestNamespace.Cls + sys.my_inst = TestNamespace.Cls(1) + +def after_reload(): + try: + TestNamespace.Cls(2) + except AttributeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + ", + }, + + new TestCase + { + Name = "field_visibility_change", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static int Foo = 1; + public static int Field = 2; + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static int Foo = 1; + private static int Field = 2; + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +from TestNamespace import Cls + +def before_reload(): + assert 2 == Cls.Field + assert 1 == Cls.Foo + +def after_reload(): + assert 1 == Cls.Foo + try: + assert 1 == Cls.Field + except AttributeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + ", + }, + + new TestCase + { + Name = "method_visibility_change", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static int Foo() { return 1; } + public static int Function() { return 2; } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static int Foo() { return 1; } + private static int Function() { return 2; } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +from TestNamespace import Cls + +def before_reload(): + sys.my_func = Cls.Function + assert 1 == Cls.Foo() + assert 2 == Cls.Function() + +def after_reload(): + assert 1 == Cls.Foo() + try: + assert 2 == Cls.Function() + except AttributeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + + try: + assert 2 == sys.my_func() + except TypeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + ", + }, + + new TestCase + { + Name = "property_visibility_change", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static int Foo { get { return 1; } } + public static int Property { get { return 2; } } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static int Foo { get { return 1; } } + private static int Property { get { return 2; } } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +from TestNamespace import Cls + +def before_reload(): + assert 1 == Cls.Foo + assert 2 == Cls.Property + +def after_reload(): + assert 1 == Cls.Foo + try: + assert 2 == Cls.Property + except AttributeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + ", + }, + + new TestCase + { + Name = "class_visibility_change", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class PublicClass { } + + [System.Serializable] + public class Cls { } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + internal class Cls { } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace + +def before_reload(): + sys.my_cls = TestNamespace.Cls + +def after_reload(): + sys.my_cls() + + try: + TestNamespace.Cls() + except AttributeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + ", + }, + + new TestCase + { + Name = "method_parameters_change", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static void MyFunction(int a) + { + System.Console.WriteLine(string.Format(""MyFunction says: {0}"", a)); + } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static void MyFunction(string a) + { + System.Console.WriteLine(string.Format(""MyFunction says: {0}"", a)); + } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +from TestNamespace import Cls + +def before_reload(): + sys.my_cls = Cls + sys.my_func = Cls.MyFunction + sys.my_cls.MyFunction(1) + sys.my_func(2) + +def after_reload(): + try: + sys.my_cls.MyFunction(1) + except TypeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + + try: + sys.my_func(2) + except TypeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + + # Calling the function from the class passes + sys.my_cls.MyFunction('test') + + try: + # calling the callable directly fails + sys.my_func('test') + except TypeError: + print('Caught expected exception') + else: + raise AssertionError('Failed to throw exception') + + Cls.MyFunction('another test') + + ", + }, + + new TestCase + { + Name = "method_return_type_change", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static int MyFunction() + { + return 2; + } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + public static string MyFunction() + { + return ""22""; + } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +from TestNamespace import Cls + +def before_reload(): + sys.my_cls = Cls + sys.my_func = Cls.MyFunction + assert 2 == sys.my_cls.MyFunction() + assert 2 == sys.my_func() + +def after_reload(): + assert '22' == sys.my_cls.MyFunction() + assert '22' == sys.my_func() + assert '22' == Cls.MyFunction() + ", + }, + + new TestCase + { + Name = "field_type_change", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + static public int Field = 2; + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Cls + { + static public string Field = ""22""; + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +from TestNamespace import Cls + +def before_reload(): + sys.my_cls = Cls + assert 2 == sys.my_cls.Field + +def after_reload(): + assert '22' == Cls.Field + assert '22' == sys.my_cls.Field + ", + }, + + new TestCase + { + Name = "construct_removed_class", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Before { } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class After { } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace + +def before_reload(): + sys.my_cls = TestNamespace.Before + +def after_reload(): + bar = sys.my_cls() + + # Don't crash! + print(bar) + print(bar.__str__()) + print(bar.__repr__()) + ", + }, + + new TestCase + { + Name = "out_to_ref_param", + DotNetBefore = @" + namespace TestNamespace + { + + [System.Serializable] + public class Data + { + public int num = -1; + } + + [System.Serializable] + public class Cls + { + public static void MyFn (out Data a) + { + a = new Data(); + a.num = 9001; + } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + + [System.Serializable] + public class Data + { + public int num = -1; + } + + [System.Serializable] + public class Cls + { + public static void MyFn (ref Data a) + { + a.num = 7; + } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace +import System + +def before_reload(): + + foo = TestNamespace.Data() + bar = TestNamespace.Cls.MyFn(foo) + assert bar.num == 9001 + # foo shouldn't have changed. + assert foo.num == -1 + + +def after_reload(): + + try: + # Now that the function takes a ref type, we must pass a valid object. + bar = TestNamespace.Cls.MyFn(None) + except System.NullReferenceException as e: + print('caught expected exception') + else: + raise AssertionError('failed to raise') + + foo = TestNamespace.Data() + bar = TestNamespace.Cls.MyFn(foo) + # foo should have changed + assert foo.num == 7 + assert bar.num == 7 + # Pythonnet also returns a new object with `ref`-qualified parameters + assert foo is not bar + ", + }, + + new TestCase + { + Name = "ref_to_out_param", + DotNetBefore = @" + namespace TestNamespace + { + + [System.Serializable] + public class Data + { + public int num = -1; + } + + [System.Serializable] + public class Cls + { + public static void MyFn (ref Data a) + { + a.num = 7; + } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + + [System.Serializable] + public class Data + { + public int num = -1; + } + + [System.Serializable] + public class Cls + { + public static void MyFn (out Data a) + { + a = new Data(); + a.num = 9001; + } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace +import System + +def before_reload(): + + foo = TestNamespace.Data() + bar = TestNamespace.Cls.MyFn(foo) + # foo should have changed + assert foo.num == 7 + assert bar.num == 7 + + +def after_reload(): + + foo = TestNamespace.Data() + bar = TestNamespace.Cls.MyFn(foo) + assert bar.num == 9001 + # foo shouldn't have changed. + assert foo.num == -1 + # this should work too + baz = TestNamespace.Cls.MyFn(None) + assert baz.num == 9001 + ", + }, + new TestCase + { + Name = "ref_to_in_param", + DotNetBefore = @" + namespace TestNamespace + { + + [System.Serializable] + public class Data + { + public int num = -1; + } + + [System.Serializable] + public class Cls + { + public static void MyFn (ref Data a) + { + a.num = 7; + System.Console.Write(""Method with ref parameter: ""); + System.Console.WriteLine(a.num); + } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class Data + { + public int num = -1; + } + + [System.Serializable] + public class Cls + { + public static void MyFn (Data a) + { + System.Console.Write(""Method with in parameter: ""); + System.Console.WriteLine(a.num); + } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace +import System + +def before_reload(): + + foo = TestNamespace.Data() + bar = TestNamespace.Cls.MyFn(foo) + # foo should have changed + assert foo.num == 7 + assert bar.num == 7 + +def after_reload(): + + foo = TestNamespace.Data() + TestNamespace.Cls.MyFn(foo) + # foo should not have changed + assert foo.num == TestNamespace.Data().num + + ", + }, + new TestCase + { + Name = "in_to_ref_param", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class Data + { + public int num = -1; + } + + [System.Serializable] + public class Cls + { + public static void MyFn (Data a) + { + System.Console.Write(""Method with in parameter: ""); + System.Console.WriteLine(a.num); + } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + + [System.Serializable] + public class Data + { + public int num = -1; + } + + [System.Serializable] + public class Cls + { + public static void MyFn (ref Data a) + { + a.num = 7; + System.Console.Write(""Method with ref parameter: ""); + System.Console.WriteLine(a.num); + } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace +import System + +def before_reload(): + + foo = TestNamespace.Data() + TestNamespace.Cls.MyFn(foo) + # foo should not have changed + assert foo.num == TestNamespace.Data().num + +def after_reload(): + + foo = TestNamespace.Data() + bar = TestNamespace.Cls.MyFn(foo) + # foo should have changed + assert foo.num == 7 + assert bar.num == 7 + ", + }, + new TestCase + { + Name = "nested_type", + DotNetBefore = @" + namespace TestNamespace + { + [System.Serializable] + public class WithNestedType + { + [System.Serializable] + public class Inner + { + public static int Value = -1; + } + } + }", + DotNetAfter = @" + namespace TestNamespace + { + [System.Serializable] + public class WithNestedType + { + [System.Serializable] + public class Inner + { + public static int Value = -1; + } + } + }", + PythonCode = @" +import clr +import sys +clr.AddReference('DomainTests') +import TestNamespace + +def before_reload(): + + sys.my_obj = TestNamespace.WithNestedType + +def after_reload(): + + assert sys.my_obj is not None + foo = sys.my_obj.Inner() + print(foo) + + ", + }, + }; + + /// + /// The runner's code. Runs the python code + /// This is a template for string.Format + /// Arg 0 is the reload mode: ShutdownMode.Reload or other. + /// Arg 1 is the no-arg python function to run, before or after. + /// + const string CaseRunnerTemplate = @" +using System; +using System.IO; +using Python.Runtime; +namespace CaseRunner +{{ + class CaseRunner + {{ + public static int Main() + {{ + try + {{ + PythonEngine.Initialize(mode:{0}); + using (Py.GIL()) + {{ + var temp = AppDomain.CurrentDomain.BaseDirectory; + dynamic sys = Py.Import(""sys""); + sys.path.append(new PyString(temp)); + dynamic test_mod = Py.Import(""domain_test_module.mod""); + test_mod.{1}_reload(); + }} + PythonEngine.Shutdown(); + }} + catch (PythonException pe) + {{ + throw new ArgumentException(message:pe.Message+"" ""+pe.StackTrace); + }} + catch (Exception e) + {{ + Console.WriteLine(e.StackTrace); + throw; + }} + return 0; + }} + }} +}} +"; + readonly static string PythonDllLocation = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Python.Runtime.dll"); + + static string TestPath = null; + + public static int Main(string[] args) + { + TestCase testCase; + if (args.Length < 1) + { + testCase = Cases[0]; + } + else + { + string testName = args[0]; + Console.WriteLine($"-- Looking for domain reload test case {testName}"); + testCase = Cases.First(c => c.Name == testName); + } + + Console.WriteLine($"-- Running domain reload test case: {testCase.Name}"); + + SetupTestFolder(testCase.Name); + + CreatePythonModule(testCase); + { + var runnerAssembly = CreateCaseRunnerAssembly(verb:"before"); + CreateTestClassAssembly(testCase.DotNetBefore); + { + var runnerDomain = CreateDomain("case runner before"); + RunAndUnload(runnerDomain, runnerAssembly); + } + { + var runnerDomain = CreateDomain("case runner before (again)"); + RunAndUnload(runnerDomain, runnerAssembly); + } + } + + { + var runnerAssembly = CreateCaseRunnerAssembly(verb:"after"); + CreateTestClassAssembly(testCase.DotNetAfter); + + // Do it twice for good measure + { + var runnerDomain = CreateDomain("case runner after"); + RunAndUnload(runnerDomain, runnerAssembly); + } + { + var runnerDomain = CreateDomain("case runner after (again)"); + RunAndUnload(runnerDomain, runnerAssembly); + } + } + + // Don't delete unconditionally. It's sometimes useful to leave the + // folder behind to debug failing tests. + TeardownTestFolder(); + + return 0; + } + + static void SetupTestFolder(string testCaseName) + { + var pid = System.Diagnostics.Process.GetCurrentProcess().Id; + TestPath = Path.Combine(Path.GetTempPath(), $"Python.TestRunner.{testCaseName}-{pid}"); + if (Directory.Exists(TestPath)) + { + Directory.Delete(TestPath, recursive: true); + } + Directory.CreateDirectory(TestPath); + Console.WriteLine($"Using directory: {TestPath}"); + File.Copy(PythonDllLocation, Path.Combine(TestPath, "Python.Runtime.dll")); + } + + static void TeardownTestFolder() + { + if (Directory.Exists(TestPath)) + { + Directory.Delete(TestPath, recursive: true); + } + } + + static void RunAndUnload(AppDomain domain, string assemblyPath) + { + // Somehow the stack traces during execution sometimes have the wrong line numbers. + // Add some info for when debugging is required. + Console.WriteLine($"-- Running domain {domain.FriendlyName}"); + domain.ExecuteAssembly(assemblyPath); + AppDomain.Unload(domain); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } + + static string CreateTestClassAssembly(string code) + { + return CreateAssembly(TestAssemblyName + ".dll", code, exe: false); + } + + static string CreateCaseRunnerAssembly(string verb, string shutdownMode = "ShutdownMode.Reload") + { + var code = string.Format(CaseRunnerTemplate, shutdownMode, verb); + var name = "TestCaseRunner.exe"; + + return CreateAssembly(name, code, exe: true); + } + static string CreateAssembly(string name, string code, bool exe = false) + { + // Never return or hold the Assembly instance. This will cause + // the assembly to be loaded into the current domain and this + // interferes with the tests. The Domain can execute fine from a + // path, so let's return that. + CSharpCodeProvider provider = new CSharpCodeProvider(); + CompilerParameters parameters = new CompilerParameters(); + parameters.GenerateExecutable = exe; + var assemblyName = name; + var assemblyFullPath = Path.Combine(TestPath, assemblyName); + parameters.OutputAssembly = assemblyFullPath; + parameters.ReferencedAssemblies.Add("System.dll"); + parameters.ReferencedAssemblies.Add("System.Core.dll"); + parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll"); + var netstandard = "netstandard.dll"; + if (Type.GetType("Mono.Runtime") != null) + { + netstandard = "Facades/" + netstandard; + } + parameters.ReferencedAssemblies.Add(netstandard); + parameters.ReferencedAssemblies.Add(PythonDllLocation); + CompilerResults results = provider.CompileAssemblyFromSource(parameters, code); + if (results.NativeCompilerReturnValue != 0) + { + var stderr = System.Console.Error; + stderr.WriteLine($"Error in {name} compiling:\n{code}"); + foreach (var error in results.Errors) + { + stderr.WriteLine(error); + } + throw new ArgumentException("Error compiling code"); + } + + return assemblyFullPath; + } + + static AppDomain CreateDomain(string name) + { + // 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 = TestPath, + ConfigurationFile = currentDomain.SetupInformation.ConfigurationFile, + LoaderOptimization = LoaderOptimization.SingleDomain, + PrivateBinPath = "." + }; + var domain = AppDomain.CreateDomain( + $"My Domain {name}", + currentDomain.Evidence, + domainsetup); + + return domain; + } + + static string CreatePythonModule(TestCase testCase) + { + var modulePath = Path.Combine(TestPath, "domain_test_module"); + if (Directory.Exists(modulePath)) + { + Directory.Delete(modulePath, recursive: true); + } + Directory.CreateDirectory(modulePath); + + File.Create(Path.Combine(modulePath, "__init__.py")).Close(); //Create and don't forget to close! + using (var writer = File.CreateText(Path.Combine(modulePath, "mod.py"))) + { + writer.Write(testCase.PythonCode); + } + + return null; + } + } +} diff --git a/src/domain_tests/conftest.py b/src/domain_tests/conftest.py new file mode 100644 index 000000000..5f0d52e10 --- /dev/null +++ b/src/domain_tests/conftest.py @@ -0,0 +1,7 @@ +import os + +from subprocess import check_call +# test_proj_path = os.path.join(cwd, "..", "testing") +cfd = os.path.dirname(__file__) +bin_path = os.path.join(cfd, 'bin') +check_call(["dotnet", "build", cfd, '-o', bin_path]) \ No newline at end of file diff --git a/src/domain_tests/test_domain_reload.py b/src/domain_tests/test_domain_reload.py new file mode 100644 index 000000000..a7cd2fa4d --- /dev/null +++ b/src/domain_tests/test_domain_reload.py @@ -0,0 +1,90 @@ +import subprocess +import os +import platform + +import pytest + +from pythonnet.find_libpython import find_libpython +libpython = find_libpython() + +pytestmark = pytest.mark.xfail(libpython is None, reason="Can't find suitable libpython") + + +def _run_test(testname): + dirname = os.path.split(__file__)[0] + exename = os.path.join(dirname, 'bin', 'Python.DomainReloadTests.exe') + args = [exename, testname] + + if platform.system() != 'Windows': + args = ['mono'] + args + + env = os.environ.copy() + env["PYTHONNET_PYDLL"] = libpython + + proc = subprocess.Popen(args, env=env) + proc.wait() + + assert proc.returncode == 0 + +def test_rename_class(): + _run_test('class_rename') + +def test_rename_class_member_static_function(): + _run_test('static_member_rename') + +def test_rename_class_member_function(): + _run_test('member_rename') + +def test_rename_class_member_field(): + _run_test('field_rename') + +def test_rename_class_member_property(): + _run_test('property_rename') + +def test_rename_namespace(): + _run_test('namespace_rename') + +def test_field_visibility_change(): + _run_test("field_visibility_change") + +def test_method_visibility_change(): + _run_test("method_visibility_change") + +def test_property_visibility_change(): + _run_test("property_visibility_change") + +def test_class_visibility_change(): + _run_test("class_visibility_change") + +@pytest.mark.skip(reason='FIXME: Domain reload fails when Python points to a .NET object which points back to Python objects') +def test_method_parameters_change(): + _run_test("method_parameters_change") + +def test_method_return_type_change(): + _run_test("method_return_type_change") + +def test_field_type_change(): + _run_test("field_type_change") + +@pytest.mark.xfail(reason="Events not yet serializable") +def test_rename_event(): + _run_test('event_rename') + +@pytest.mark.xfail(reason="newly instanced object uses PyType_GenericAlloc") +def test_construct_removed_class(): + _run_test("construct_removed_class") + +def test_out_to_ref_param(): + _run_test("out_to_ref_param") + +def test_ref_to_out_param(): + _run_test("ref_to_out_param") + +def test_ref_to_in_param(): + _run_test("ref_to_in_param") + +def test_in_to_ref_param(): + _run_test("in_to_ref_param") + +def test_nested_type(): + _run_test("nested_type") diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index 18fcd32d1..266badb9e 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -1,33 +1,39 @@ namespace Python.EmbeddingTest { using System; using System.Collections.Generic; - using System.Text; + using System.Linq; using NUnit.Framework; using Python.Runtime; using Python.Runtime.Codecs; - public class Codecs { + public class Codecs + { [SetUp] - public void SetUp() { + public void SetUp() + { PythonEngine.Initialize(); } [TearDown] - public void Dispose() { + public void Dispose() + { PythonEngine.Shutdown(); } [Test] - public void ConversionsGeneric() { - ConversionsGeneric, ValueTuple>(); + public void TupleConversionsGeneric() + { + TupleConversionsGeneric, ValueTuple>(); } - static void ConversionsGeneric() { + static void TupleConversionsGeneric() + { TupleCodec.Register(); var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); T restored = default; using (Py.GIL()) - using (var scope = Py.CreateScope()) { + using (var scope = Py.CreateScope()) + { void Accept(T value) => restored = value; var accept = new Action(Accept).ToPython(); scope.Set(nameof(tuple), tuple); @@ -38,15 +44,18 @@ static void ConversionsGeneric() { } [Test] - public void ConversionsObject() { - ConversionsObject, ValueTuple>(); + public void TupleConversionsObject() + { + TupleConversionsObject, ValueTuple>(); } - static void ConversionsObject() { + static void TupleConversionsObject() + { TupleCodec.Register(); var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); T restored = default; using (Py.GIL()) - using (var scope = Py.CreateScope()) { + using (var scope = Py.CreateScope()) + { void Accept(object value) => restored = (T)value; var accept = new Action(Accept).ToPython(); scope.Set(nameof(tuple), tuple); @@ -57,12 +66,15 @@ static void ConversionsObject() { } [Test] - public void TupleRoundtripObject() { + public void TupleRoundtripObject() + { TupleRoundtripObject, ValueTuple>(); } - static void TupleRoundtripObject() { + static void TupleRoundtripObject() + { var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); - using (Py.GIL()) { + using (Py.GIL()) + { var pyTuple = TupleCodec.Instance.TryEncode(tuple); Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out object restored)); Assert.AreEqual(expected: tuple, actual: restored); @@ -70,18 +82,220 @@ static void TupleRoundtripObject() { } [Test] - public void TupleRoundtripGeneric() { + public void TupleRoundtripGeneric() + { TupleRoundtripGeneric, ValueTuple>(); } - static void TupleRoundtripGeneric() { + static void TupleRoundtripGeneric() + { var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); - using (Py.GIL()) { + using (Py.GIL()) + { var pyTuple = TupleCodec.Instance.TryEncode(tuple); Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out T restored)); Assert.AreEqual(expected: tuple, actual: restored); } } + + static PyObject GetPythonIterable() + { + using (Py.GIL()) + { + return PythonEngine.Eval("map(lambda x: x, [1,2,3])"); + } + } + + [Test] + public void ListDecoderTest() + { + var codec = ListDecoder.Instance; + var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; + + var pyList = new PyList(items.ToArray()); + + var pyListType = pyList.GetPythonType(); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IList))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IList))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(System.Collections.IEnumerable))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(IEnumerable))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(ICollection))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(bool))); + + //we'd have to copy into a list instance to do this, it would not be lossless. + //lossy converters can be implemented outside of the python.net core library + Assert.IsFalse(codec.CanDecode(pyListType, typeof(List))); + + //convert to list of int + IList intList = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intList); }); + CollectionAssert.AreEqual(intList, new List { 1, 2, 3 }); + + //convert to list of string. This will not work. + //The ListWrapper class will throw a python exception when it tries to access any element. + //TryDecode is a lossless conversion so there will be no exception at that point + //interestingly, since the size of the python list can be queried without any conversion, + //the IList will report a Count of 3. + IList stringList = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out stringList); }); + Assert.AreEqual(stringList.Count, 3); + Assert.Throws(typeof(InvalidCastException), () => { var x = stringList[0]; }); + + //can't convert python iterable to list (this will require a copy which isn't lossless) + var foo = GetPythonIterable(); + var fooType = foo.GetPythonType(); + Assert.IsFalse(codec.CanDecode(fooType, typeof(IList))); + } + + [Test] + public void SequenceDecoderTest() + { + var codec = SequenceDecoder.Instance; + var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; + + //SequenceConverter can only convert to any ICollection + var pyList = new PyList(items.ToArray()); + //it can convert a PyList, since PyList satisfies the python sequence protocol + + Assert.IsFalse(codec.CanDecode(pyList, typeof(bool))); + Assert.IsFalse(codec.CanDecode(pyList, typeof(IList))); + Assert.IsFalse(codec.CanDecode(pyList, typeof(System.Collections.IEnumerable))); + Assert.IsFalse(codec.CanDecode(pyList, typeof(IEnumerable))); + + Assert.IsTrue(codec.CanDecode(pyList, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(pyList, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(pyList, typeof(ICollection))); + + //convert to collection of int + ICollection intCollection = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intCollection); }); + CollectionAssert.AreEqual(intCollection, new List { 1, 2, 3 }); + + //no python exception should have occurred during the above conversion and check + Runtime.CheckExceptionOccurred(); + + //convert to collection of string. This will not work. + //The SequenceWrapper class will throw a python exception when it tries to access any element. + //TryDecode is a lossless conversion so there will be no exception at that point + //interestingly, since the size of the python sequence can be queried without any conversion, + //the IList will report a Count of 3. + ICollection stringCollection = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out stringCollection); }); + Assert.AreEqual(3, stringCollection.Count()); + Assert.Throws(typeof(InvalidCastException), () => { + string[] array = new string[3]; + stringCollection.CopyTo(array, 0); + }); + + Runtime.CheckExceptionOccurred(); + + //can't convert python iterable to collection (this will require a copy which isn't lossless) + //python iterables do not satisfy the python sequence protocol + var foo = GetPythonIterable(); + var fooType = foo.GetPythonType(); + Assert.IsFalse(codec.CanDecode(fooType, typeof(ICollection))); + + //python tuples do satisfy the python sequence protocol + var pyTuple = new PyTuple(items.ToArray()); + var pyTupleType = pyTuple.GetPythonType(); + + Assert.IsTrue(codec.CanDecode(pyTupleType, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(pyTupleType, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(pyTupleType, typeof(ICollection))); + + //convert to collection of int + ICollection intCollection2 = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyTuple, out intCollection2); }); + CollectionAssert.AreEqual(intCollection2, new List { 1, 2, 3 }); + + //no python exception should have occurred during the above conversion and check + Runtime.CheckExceptionOccurred(); + + //convert to collection of string. This will not work. + //The SequenceWrapper class will throw a python exception when it tries to access any element. + //TryDecode is a lossless conversion so there will be no exception at that point + //interestingly, since the size of the python sequence can be queried without any conversion, + //the IList will report a Count of 3. + ICollection stringCollection2 = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyTuple, out stringCollection2); }); + Assert.AreEqual(3, stringCollection2.Count()); + Assert.Throws(typeof(InvalidCastException), () => { + string[] array = new string[3]; + stringCollection2.CopyTo(array, 0); + }); + + Runtime.CheckExceptionOccurred(); + + } + + [Test] + public void IterableDecoderTest() + { + var codec = IterableDecoder.Instance; + var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; + + var pyList = new PyList(items.ToArray()); + var pyListType = pyList.GetPythonType(); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(IList))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(System.Collections.IEnumerable))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(ICollection))); + Assert.IsFalse(codec.CanDecode(pyListType, typeof(bool))); + + //ensure a PyList can be converted to a plain IEnumerable + System.Collections.IEnumerable plainEnumerable1 = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out plainEnumerable1); }); + CollectionAssert.AreEqual(plainEnumerable1, new List { 1, 2, 3 }); + + //can convert to any generic ienumerable. If the type is not assignable from the python element + //it will lead to an empty iterable when decoding. TODO - should it throw? + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); + Assert.IsTrue(codec.CanDecode(pyListType, typeof(IEnumerable))); + + IEnumerable intEnumerable = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intEnumerable); }); + CollectionAssert.AreEqual(intEnumerable, new List { 1, 2, 3 }); + + Runtime.CheckExceptionOccurred(); + + IEnumerable doubleEnumerable = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out doubleEnumerable); }); + CollectionAssert.AreEqual(doubleEnumerable, new List { 1, 2, 3 }); + + Runtime.CheckExceptionOccurred(); + + IEnumerable stringEnumerable = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out stringEnumerable); }); + + Assert.Throws(typeof(InvalidCastException), () => { + foreach (string item in stringEnumerable) + { + var x = item; + } + }); + Assert.Throws(typeof(InvalidCastException), () => { + stringEnumerable.Count(); + }); + + Runtime.CheckExceptionOccurred(); + + //ensure a python class which implements the iterator protocol can be converter to a plain IEnumerable + var foo = GetPythonIterable(); + var fooType = foo.GetPythonType(); + System.Collections.IEnumerable plainEnumerable2 = null; + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out plainEnumerable2); }); + CollectionAssert.AreEqual(plainEnumerable2, new List { 1, 2, 3 }); + + //can convert to any generic ienumerable. If the type is not assignable from the python element + //it will be an exception during TryDecode + Assert.IsTrue(codec.CanDecode(fooType, typeof(IEnumerable))); + Assert.IsTrue(codec.CanDecode(fooType, typeof(IEnumerable))); + Assert.IsTrue(codec.CanDecode(fooType, typeof(IEnumerable))); + + Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intEnumerable); }); + CollectionAssert.AreEqual(intEnumerable, new List { 1, 2, 3 }); + } } /// diff --git a/src/embed_tests/Program.cs b/src/embed_tests/Program.cs deleted file mode 100644 index b4439e3e4..000000000 --- a/src/embed_tests/Program.cs +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index eb6957656..000000000 --- a/src/embed_tests/Python.EmbeddingTest.15.csproj +++ /dev/null @@ -1,121 +0,0 @@ - - - - - net40;netcoreapp2.0 - x64;x86 - DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 - Exe - false - Python.EmbeddingTest - Python.EmbeddingTest - Python.EmbeddingTest - 2.5.0 - false - false - false - false - bin\ - false - $(OutputPath)\$(AssemblyName).xml - $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml - 1591 - ..\..\ - $(SolutionDir)\bin\ - $(OutputPath)\$(TargetFramework)_publish - 7.3 - 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) - - - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - $(TargetPath) - $(TargetDir)$(TargetName).pdb - - - - - - - - diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj index eff226dd5..099d026fa 100644 --- a/src/embed_tests/Python.EmbeddingTest.csproj +++ b/src/embed_tests/Python.EmbeddingTest.csproj @@ -1,143 +1,36 @@ - - + + - Debug - AnyCPU - {4165C59D-2822-499F-A6DB-EACA4C331EB5} - Library - Python.EmbeddingTest - Python.EmbeddingTest - bin\Python.EmbeddingTest.xml - bin\ - v4.0 - - 1591 - ..\..\ - $(SolutionDir)\bin\ - 7.3 - true - prompt + net472;netcoreapp3.1 + 9.0 + ..\pythonnet.snk + true - - x86 - - - x64 - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - - - ..\..\packages\NUnit.3.12.0\lib\net40\nunit.framework.dll - - - ..\..\packages\System.ValueTuple.4.5.0\lib\portable-net40+sl4+win8+wp8\System.ValueTuple.dll - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - {097B4AC0-74E9-4C58-BCF8-C69746EC8271} - Python.Runtime - + - - - - + - $(TargetPath) - $(TargetDir)$(TargetName).pdb + $(DefineConstants);$(ConfiguredConstants) - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + 1.0.0 + all + runtime; build; native; contentfiles; analyzers + + + diff --git a/src/embed_tests/References.cs b/src/embed_tests/References.cs index 1d29e85c7..417e743c0 100644 --- a/src/embed_tests/References.cs +++ b/src/embed_tests/References.cs @@ -23,7 +23,7 @@ public void Dispose() public void MoveToPyObject_SetsNull() { var dict = new PyDict(); - NewReference reference = Runtime.PyDict_Items(dict.Handle); + NewReference reference = Runtime.PyDict_Items(dict.Reference); try { Assert.IsFalse(reference.IsNull()); @@ -41,7 +41,7 @@ public void MoveToPyObject_SetsNull() public void CanBorrowFromNewReference() { var dict = new PyDict(); - NewReference reference = Runtime.PyDict_Items(dict.Handle); + NewReference reference = Runtime.PyDict_Items(dict.Reference); try { PythonException.ThrowIfIsNotZero(Runtime.PyList_Reverse(reference)); diff --git a/src/embed_tests/TestClass.cs b/src/embed_tests/TestClass.cs new file mode 100644 index 000000000..cbcb92483 --- /dev/null +++ b/src/embed_tests/TestClass.cs @@ -0,0 +1,78 @@ +using System; +using System.Runtime.InteropServices; + +using NUnit.Framework; + +using Python.Runtime; + +using PyRuntime = Python.Runtime.Runtime; + +namespace Python.EmbeddingTest +{ + public class TestClass + { + public class MyClass + { + } + + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + [Test] + public void WeakRefForClrObject() + { + var obj = new MyClass(); + using var scope = Py.CreateScope(); + scope.Set("clr_obj", obj); + scope.Exec(@" +import weakref +ref = weakref.ref(clr_obj) +"); + using PyObject pyobj = scope.Get("clr_obj"); + ValidateAttachedGCHandle(obj, pyobj.Handle); + } + + [Test] + public void WeakRefForSubClass() + { + using (var scope = Py.CreateScope()) + { + scope.Exec(@" +from Python.EmbeddingTest import TestClass +import weakref + +class Sub(TestClass.MyClass): + pass + +obj = Sub() +ref = weakref.ref(obj) +"); + using (PyObject pyobj = scope.Get("obj")) + { + IntPtr op = pyobj.Handle; + IntPtr type = PyRuntime.PyObject_TYPE(op); + IntPtr clrHandle = Marshal.ReadIntPtr(op, ObjectOffset.magic(type)); + var clobj = (CLRObject)GCHandle.FromIntPtr(clrHandle).Target; + Assert.IsTrue(clobj.inst is MyClass); + } + } + } + + private static void ValidateAttachedGCHandle(object obj, IntPtr op) + { + IntPtr type = PyRuntime.PyObject_TYPE(op); + IntPtr clrHandle = Marshal.ReadIntPtr(op, ObjectOffset.magic(type)); + var clobj = (CLRObject)GCHandle.FromIntPtr(clrHandle).Target; + Assert.True(ReferenceEquals(clobj.inst, obj)); + } + } +} diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs index 3556df0f6..e4479da18 100644 --- a/src/embed_tests/TestDomainReload.cs +++ b/src/embed_tests/TestDomainReload.cs @@ -14,7 +14,7 @@ // // Unfortunately this means no continuous integration testing for this case. // -#if !NETSTANDARD && !NETCOREAPP +#if NETFRAMEWORK namespace Python.EmbeddingTest { class TestDomainReload @@ -179,97 +179,6 @@ public static void CrossDomainObject() #endregion - #region TestClassReference - - class ReloadClassRefStep1 : CrossCaller - { - public override ValueType Execute(ValueType arg) - { - const string code = @" -from Python.EmbeddingTest.Domain import MyClass - -def test_obj_call(): - obj = MyClass() - obj.Method() - obj.StaticMethod() - obj.Property = 1 - obj.Field = 10 - -test_obj_call() -"; - const string name = "test_domain_reload_mod"; - using (Py.GIL()) - { - // Create a new module - IntPtr module = PyRuntime.PyModule_New(name); - Assert.That(module != IntPtr.Zero); - IntPtr globals = PyRuntime.PyObject_GetAttrString(module, "__dict__"); - Assert.That(globals != IntPtr.Zero); - try - { - // import builtins - // module.__dict__[__builtins__] = builtins - int res = PyRuntime.PyDict_SetItemString(globals, "__builtins__", - PyRuntime.PyEval_GetBuiltins()); - PythonException.ThrowIfIsNotZero(res); - - // Execute the code in the module's scope - PythonEngine.Exec(code, globals); - // import sys - // modules = sys.modules - IntPtr modules = PyRuntime.PyImport_GetModuleDict(); - // modules[name] = module - res = PyRuntime.PyDict_SetItemString(modules, name, module); - PythonException.ThrowIfIsNotZero(res); - } - catch - { - PyRuntime.XDecref(module); - throw; - } - finally - { - PyRuntime.XDecref(globals); - } - return module; - } - } - } - - class ReloadClassRefStep2 : CrossCaller - { - public override ValueType Execute(ValueType arg) - { - var module = (IntPtr)arg; - using (Py.GIL()) - { - var test_obj_call = PyRuntime.PyObject_GetAttrString(module, "test_obj_call"); - PythonException.ThrowIfIsNull(test_obj_call); - var args = PyRuntime.PyTuple_New(0); - var res = PyRuntime.PyObject_CallObject(test_obj_call, args); - PythonException.ThrowIfIsNull(res); - - PyRuntime.XDecref(args); - PyRuntime.XDecref(res); - } - return 0; - } - } - - - [Test] - /// - /// Create a new Python module, define a function in it. - /// Unload the domain, load a new one. - /// Make sure the function (and module) still exists. - /// - public void TestClassReference() - { - RunDomainReloadSteps(); - } - - #endregion - #region Tempary tests // https://github.com/pythonnet/pythonnet/pull/1074#issuecomment-596139665 @@ -288,7 +197,7 @@ void ExecTest() GC.Collect(); GC.WaitForPendingFinalizers(); // <- this will put former `num` into Finalizer queue - Finalizer.Instance.Collect(forceDispose: true); + Finalizer.Instance.Collect(); // ^- this will call PyObject.Dispose, which will call XDecref on `num.Handle`, // but Python interpreter from "run" 1 is long gone, so it will corrupt memory instead. Assert.False(numRef.IsAlive); @@ -333,7 +242,7 @@ void ExecTest() PythonEngine.Initialize(); // <- "run" 2 starts GC.Collect(); GC.WaitForPendingFinalizers(); - Finalizer.Instance.Collect(forceDispose: true); + Finalizer.Instance.Collect(); Assert.False(objRef.IsAlive); } finally @@ -400,7 +309,7 @@ public object Call(string methodName, params object[] args) return method.Invoke(null, args); } } - + static T CreateInstanceInstanceAndUnwrap(AppDomain domain) { Type type = typeof(T); @@ -423,7 +332,7 @@ static void RunAssemblyAndUnload(string domainName) // assembly (and Python .NET) to reside var theProxy = CreateInstanceInstanceAndUnwrap(domain); - theProxy.Call("InitPython", ShutdownMode.Soft); + theProxy.Call(nameof(PythonRunner.InitPython), ShutdownMode.Soft, PyRuntime.PythonDLL); // From now on use the Proxy to call into the new assembly theProxy.RunPython(); @@ -491,7 +400,7 @@ static void RunDomainReloadSteps() where T1 : CrossCaller where T2 : Cro try { var theProxy = CreateInstanceInstanceAndUnwrap(domain); - theProxy.Call("InitPython", ShutdownMode.Reload); + theProxy.Call(nameof(PythonRunner.InitPython), ShutdownMode.Reload, PyRuntime.PythonDLL); var caller = CreateInstanceInstanceAndUnwrap(domain); arg = caller.Execute(arg); @@ -509,7 +418,7 @@ static void RunDomainReloadSteps() where T1 : CrossCaller where T2 : Cro try { var theProxy = CreateInstanceInstanceAndUnwrap(domain); - theProxy.Call("InitPython", ShutdownMode.Reload); + theProxy.Call(nameof(PythonRunner.InitPython), ShutdownMode.Reload, PyRuntime.PythonDLL); var caller = CreateInstanceInstanceAndUnwrap(domain); caller.Execute(arg); @@ -569,8 +478,9 @@ public static void RunPython() private static IntPtr _state; - public static void InitPython(ShutdownMode mode) + public static void InitPython(ShutdownMode mode, string dllName) { + PyRuntime.PythonDLL = dllName; PythonEngine.Initialize(mode: mode); _state = PythonEngine.BeginAllowThreads(); } diff --git a/src/embed_tests/TestFinalizer.cs b/src/embed_tests/TestFinalizer.cs index a54bc7a96..46e2fcdf1 100644 --- a/src/embed_tests/TestFinalizer.cs +++ b/src/embed_tests/TestFinalizer.cs @@ -2,9 +2,9 @@ using Python.Runtime; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; namespace Python.EmbeddingTest @@ -28,25 +28,14 @@ public void TearDown() PythonEngine.Shutdown(); } - private static bool FullGCCollect() + private static void FullGCCollect() { - GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); - try - { - return GC.WaitForFullGCComplete() == GCNotificationStatus.Succeeded; - } - catch (NotImplementedException) - { - // Some clr runtime didn't implement GC.WaitForFullGCComplete yet. - return false; - } - finally - { - GC.WaitForPendingFinalizers(); - } + GC.Collect(); + GC.WaitForPendingFinalizers(); } [Test] + [Obsolete("GC tests are not guaranteed")] public void CollectBasicObject() { Assert.IsTrue(Finalizer.Instance.Enable); @@ -63,11 +52,7 @@ public void CollectBasicObject() Assert.IsFalse(called, "The event handler was called before it was installed"); Finalizer.Instance.CollectOnce += handler; - WeakReference shortWeak; - WeakReference longWeak; - { - MakeAGarbage(out shortWeak, out longWeak); - } + IntPtr pyObj = MakeAGarbage(out var shortWeak, out var longWeak); FullGCCollect(); // The object has been resurrected Warn.If( @@ -85,7 +70,7 @@ public void CollectBasicObject() var garbage = Finalizer.Instance.GetCollectedObjects(); Assert.NotZero(garbage.Count, "There should still be garbage around"); Warn.Unless( - garbage.Any(T => ReferenceEquals(T.Target, longWeak.Target)), + garbage.Contains(pyObj), $"The {nameof(longWeak)} reference doesn't show up in the garbage list", garbage ); @@ -103,20 +88,15 @@ public void CollectBasicObject() } [Test] - [Ignore("Ignore temporarily")] + [Obsolete("GC tests are not guaranteed")] public void CollectOnShutdown() { IntPtr op = MakeAGarbage(out var shortWeak, out var longWeak); - int hash = shortWeak.Target.GetHashCode(); - List garbage; - if (!FullGCCollect()) - { - Assert.IsTrue(WaitForCollected(op, hash, 10000)); - } + FullGCCollect(); Assert.IsFalse(shortWeak.IsAlive); - garbage = Finalizer.Instance.GetCollectedObjects(); + List garbage = Finalizer.Instance.GetCollectedObjects(); Assert.IsNotEmpty(garbage, "The garbage object should be collected"); - Assert.IsTrue(garbage.Any(r => ReferenceEquals(r.Target, longWeak.Target)), + Assert.IsTrue(garbage.Contains(op), "Garbage should contains the collected object"); PythonEngine.Shutdown(); @@ -124,12 +104,29 @@ public void CollectOnShutdown() Assert.IsEmpty(garbage); } + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to obj + [Obsolete("GC tests are not guaranteed")] private static IntPtr MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak) { - PyLong obj = new PyLong(1024); - shortWeak = new WeakReference(obj); - longWeak = new WeakReference(obj, true); - return obj.Handle; + IntPtr handle = IntPtr.Zero; + WeakReference @short = null, @long = null; + // must create Python object in the thread where we have GIL + IntPtr val = PyLong.FromLong(1024); + // must create temp object in a different thread to ensure it is not present + // when conservatively scanning stack for GC roots. + // see https://xamarin.github.io/bugzilla-archives/17/17593/bug.html + var garbageGen = new Thread(() => + { + var obj = new PyObject(val, skipCollect: true); + @short = new WeakReference(obj); + @long = new WeakReference(obj, true); + handle = obj.Handle; + }); + garbageGen.Start(); + Assert.IsTrue(garbageGen.Join(TimeSpan.FromSeconds(5)), "Garbage creation timed out"); + shortWeak = @short; + longWeak = @long; + return handle; } private static long CompareWithFinalizerOn(PyObject pyCollect, bool enbale) @@ -190,62 +187,6 @@ public void SimpleTestMemory() } } - class MyPyObject : PyObject - { - public MyPyObject(IntPtr op) : base(op) - { - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - GC.SuppressFinalize(this); - throw new Exception("MyPyObject"); - } - internal static void CreateMyPyObject(IntPtr op) - { - Runtime.Runtime.XIncref(op); - new MyPyObject(op); - } - } - - [Test] - public void ErrorHandling() - { - bool called = false; - var errorMessage = ""; - EventHandler handleFunc = (sender, args) => - { - called = true; - errorMessage = args.Error.Message; - }; - Finalizer.Instance.Threshold = 1; - Finalizer.Instance.ErrorHandler += handleFunc; - try - { - WeakReference shortWeak; - WeakReference longWeak; - { - MakeAGarbage(out shortWeak, out longWeak); - var obj = (PyLong)longWeak.Target; - IntPtr handle = obj.Handle; - shortWeak = null; - longWeak = null; - MyPyObject.CreateMyPyObject(handle); - obj.Dispose(); - obj = null; - } - FullGCCollect(); - Finalizer.Instance.Collect(); - Assert.IsTrue(called); - } - finally - { - Finalizer.Instance.ErrorHandler -= handleFunc; - } - Assert.AreEqual(errorMessage, "MyPyObject"); - } - [Test] public void ValidateRefCount() { @@ -278,6 +219,7 @@ public void ValidateRefCount() } } + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to s1 and s2 private static IntPtr CreateStringGarbage() { PyString s1 = new PyString("test_string"); @@ -285,29 +227,5 @@ private static IntPtr CreateStringGarbage() PyString s2 = new PyString(s1.Handle); return s1.Handle; } - - private static bool WaitForCollected(IntPtr op, int hash, int milliseconds) - { - var stopwatch = Stopwatch.StartNew(); - do - { - var garbage = Finalizer.Instance.GetCollectedObjects(); - foreach (var item in garbage) - { - // The validation is not 100% precise, - // but it's rare that two conditions satisfied but they're still not the same object. - if (item.Target.GetHashCode() != hash) - { - continue; - } - var obj = (IPyDisposable)item.Target; - if (obj.GetTrackedHandles().Contains(op)) - { - return true; - } - } - } while (stopwatch.ElapsedMilliseconds < milliseconds); - return false; - } } } diff --git a/src/embed_tests/TestInterrupt.cs b/src/embed_tests/TestInterrupt.cs new file mode 100644 index 000000000..a40407782 --- /dev/null +++ b/src/embed_tests/TestInterrupt.cs @@ -0,0 +1,64 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +using NUnit.Framework; + +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class TestInterrupt + { + private IntPtr _threadState; + + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + _threadState = PythonEngine.BeginAllowThreads(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.EndAllowThreads(_threadState); + PythonEngine.Shutdown(); + } + + [Test] + public void InterruptTest() + { + long pythonThreadID = 0; + var asyncCall = Task.Factory.StartNew(() => + { + using (Py.GIL()) + { + Interlocked.Exchange(ref pythonThreadID, (long)PythonEngine.GetPythonThreadID()); + return PythonEngine.RunSimpleString(@" +import time + +while True: + time.sleep(0.2)"); + } + }); + + var timeout = Stopwatch.StartNew(); + while (Interlocked.Read(ref pythonThreadID) == 0) + { + Assert.Less(timeout.Elapsed, TimeSpan.FromSeconds(5), "thread ID was not assigned in time"); + } + + using (Py.GIL()) + { + int interruptReturnValue = PythonEngine.Interrupt((ulong)Interlocked.Read(ref pythonThreadID)); + Assert.AreEqual(1, interruptReturnValue); + } + + Assert.IsTrue(asyncCall.Wait(TimeSpan.FromSeconds(5)), "Async thread was not interrupted in time"); + + Assert.AreEqual(-1, asyncCall.Result); + } + } +} diff --git a/src/embed_tests/TestNativeTypeOffset.cs b/src/embed_tests/TestNativeTypeOffset.cs new file mode 100644 index 000000000..7dd5a765e --- /dev/null +++ b/src/embed_tests/TestNativeTypeOffset.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +using NUnit.Framework; + +using Python.Runtime; + +namespace Python.EmbeddingPythonTest +{ + public class TestNativeTypeOffset + { + private Py.GILState _gs; + + [SetUp] + public void SetUp() + { + _gs = Py.GIL(); + } + + [TearDown] + public void Dispose() + { + _gs.Dispose(); + } + + /// + /// Tests that installation has generated code for NativeTypeOffset and that it can be loaded. + /// + [Test] + public void LoadNativeTypeOffsetClass() + { + PyObject sys = Py.Import("sys"); + // We can safely ignore the "m" abi flag + var abiflags = sys.GetAttr("abiflags", "".ToPython()).ToString().Replace("m", ""); + if (!string.IsNullOrEmpty(abiflags)) + { + string typeName = "Python.Runtime.NativeTypeOffset, Python.Runtime"; + Assert.NotNull(Type.GetType(typeName), $"{typeName} does not exist and sys.abiflags={abiflags}"); + } + } + } +} diff --git a/src/embed_tests/TestOperator.cs b/src/embed_tests/TestOperator.cs new file mode 100644 index 000000000..8e9feb241 --- /dev/null +++ b/src/embed_tests/TestOperator.cs @@ -0,0 +1,521 @@ +using NUnit.Framework; + +using Python.Runtime; + +using System.Linq; +using System.Reflection; + +namespace Python.EmbeddingTest +{ + public class TestOperator + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + public class OperableObject + { + public int Num { get; set; } + + public override int GetHashCode() + { + return unchecked(159832395 + Num.GetHashCode()); + } + + public override bool Equals(object obj) + { + return obj is OperableObject @object && + Num == @object.Num; + } + + public OperableObject(int num) + { + Num = num; + } + + public static OperableObject operator ~(OperableObject a) + { + return new OperableObject(~a.Num); + } + + public static OperableObject operator +(OperableObject a) + { + return new OperableObject(+a.Num); + } + + public static OperableObject operator -(OperableObject a) + { + return new OperableObject(-a.Num); + } + + public static OperableObject operator +(int a, OperableObject b) + { + return new OperableObject(a + b.Num); + } + public static OperableObject operator +(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num + b.Num); + } + public static OperableObject operator +(OperableObject a, int b) + { + return new OperableObject(a.Num + b); + } + + public static OperableObject operator -(int a, OperableObject b) + { + return new OperableObject(a - b.Num); + } + public static OperableObject operator -(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num - b.Num); + } + public static OperableObject operator -(OperableObject a, int b) + { + return new OperableObject(a.Num - b); + } + + public static OperableObject operator *(int a, OperableObject b) + { + return new OperableObject(a * b.Num); + } + public static OperableObject operator *(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num * b.Num); + } + public static OperableObject operator *(OperableObject a, int b) + { + return new OperableObject(a.Num * b); + } + + public static OperableObject operator /(int a, OperableObject b) + { + return new OperableObject(a / b.Num); + } + public static OperableObject operator /(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num / b.Num); + } + public static OperableObject operator /(OperableObject a, int b) + { + return new OperableObject(a.Num / b); + } + + public static OperableObject operator %(int a, OperableObject b) + { + return new OperableObject(a % b.Num); + } + public static OperableObject operator %(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num % b.Num); + } + public static OperableObject operator %(OperableObject a, int b) + { + return new OperableObject(a.Num % b); + } + + public static OperableObject operator &(int a, OperableObject b) + { + return new OperableObject(a & b.Num); + } + public static OperableObject operator &(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num & b.Num); + } + public static OperableObject operator &(OperableObject a, int b) + { + return new OperableObject(a.Num & b); + } + + public static OperableObject operator |(int a, OperableObject b) + { + return new OperableObject(a | b.Num); + } + public static OperableObject operator |(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num | b.Num); + } + public static OperableObject operator |(OperableObject a, int b) + { + return new OperableObject(a.Num | b); + } + + public static OperableObject operator ^(int a, OperableObject b) + { + return new OperableObject(a ^ b.Num); + } + public static OperableObject operator ^(OperableObject a, OperableObject b) + { + return new OperableObject(a.Num ^ b.Num); + } + public static OperableObject operator ^(OperableObject a, int b) + { + return new OperableObject(a.Num ^ b); + } + + public static bool operator ==(int a, OperableObject b) + { + return (a == b.Num); + } + public static bool operator ==(OperableObject a, OperableObject b) + { + return (a.Num == b.Num); + } + public static bool operator ==(OperableObject a, int b) + { + return (a.Num == b); + } + + public static bool operator !=(int a, OperableObject b) + { + return (a != b.Num); + } + public static bool operator !=(OperableObject a, OperableObject b) + { + return (a.Num != b.Num); + } + public static bool operator !=(OperableObject a, int b) + { + return (a.Num != b); + } + + public static bool operator <=(int a, OperableObject b) + { + return (a <= b.Num); + } + public static bool operator <=(OperableObject a, OperableObject b) + { + return (a.Num <= b.Num); + } + public static bool operator <=(OperableObject a, int b) + { + return (a.Num <= b); + } + + public static bool operator >=(int a, OperableObject b) + { + return (a >= b.Num); + } + public static bool operator >=(OperableObject a, OperableObject b) + { + return (a.Num >= b.Num); + } + public static bool operator >=(OperableObject a, int b) + { + return (a.Num >= b); + } + + public static bool operator >=(OperableObject a, PyObject b) + { + using (Py.GIL()) + { + // Assuming b is a tuple, take the first element. + int bNum = b[0].As(); + return a.Num >= bNum; + } + } + public static bool operator <=(OperableObject a, PyObject b) + { + using (Py.GIL()) + { + // Assuming b is a tuple, take the first element. + int bNum = b[0].As(); + return a.Num <= bNum; + } + } + + public static bool operator <(int a, OperableObject b) + { + return (a < b.Num); + } + public static bool operator <(OperableObject a, OperableObject b) + { + return (a.Num < b.Num); + } + public static bool operator <(OperableObject a, int b) + { + return (a.Num < b); + } + + public static bool operator >(int a, OperableObject b) + { + return (a > b.Num); + } + public static bool operator >(OperableObject a, OperableObject b) + { + return (a.Num > b.Num); + } + public static bool operator >(OperableObject a, int b) + { + return (a.Num > b); + } + + public static OperableObject operator <<(OperableObject a, int offset) + { + return new OperableObject(a.Num << offset); + } + + public static OperableObject operator >>(OperableObject a, int offset) + { + return new OperableObject(a.Num >> offset); + } + } + + [Test] + public void SymmetricalOperatorOverloads() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + + PythonEngine.Exec($@" +from {module} import * +cls = {name} +a = cls(-2) +b = cls(10) +c = ~a +assert c.Num == ~a.Num + +c = +a +assert c.Num == +a.Num + +a = cls(2) +c = -a +assert c.Num == -a.Num + +c = a + b +assert c.Num == a.Num + b.Num + +c = a - b +assert c.Num == a.Num - b.Num + +c = a * b +assert c.Num == a.Num * b.Num + +c = a / b +assert c.Num == a.Num // b.Num + +c = a % b +assert c.Num == a.Num % b.Num + +c = a & b +assert c.Num == a.Num & b.Num + +c = a | b +assert c.Num == a.Num | b.Num + +c = a ^ b +assert c.Num == a.Num ^ b.Num + +c = a == b +assert c == (a.Num == b.Num) + +c = a != b +assert c == (a.Num != b.Num) + +c = a <= b +assert c == (a.Num <= b.Num) + +c = a >= b +assert c == (a.Num >= b.Num) + +c = a < b +assert c == (a.Num < b.Num) + +c = a > b +assert c == (a.Num > b.Num) +"); + } + + [Test] + public void OperatorOverloadMissingArgument() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + + Assert.Throws(() => + PythonEngine.Exec($@" +from {module} import * +cls = {name} +a = cls(2) +b = cls(10) +a.op_Addition() +")); + } + + [Test] + public void ForwardOperatorOverloads() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + + PythonEngine.Exec($@" +from {module} import * +cls = {name} +a = cls(2) +b = 10 +c = a + b +assert c.Num == a.Num + b + +c = a - b +assert c.Num == a.Num - b + +c = a * b +assert c.Num == a.Num * b + +c = a / b +assert c.Num == a.Num // b + +c = a % b +assert c.Num == a.Num % b + +c = a & b +assert c.Num == a.Num & b + +c = a | b +assert c.Num == a.Num | b + +c = a ^ b +assert c.Num == a.Num ^ b + +c = a == b +assert c == (a.Num == b) + +c = a != b +assert c == (a.Num != b) + +c = a <= b +assert c == (a.Num <= b) + +c = a >= b +assert c == (a.Num >= b) + +c = a < b +assert c == (a.Num < b) + +c = a > b +assert c == (a.Num > b) +"); + } + + [Test] + public void TupleComparisonOperatorOverloads() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + PythonEngine.Exec($@" +from {module} import * +cls = {name} +a = cls(2) +b = (1, 2) + +c = a >= b +assert c == (a.Num >= b[0]) + +c = a <= b +assert c == (a.Num <= b[0]) + +c = b >= a +assert c == (b[0] >= a.Num) + +c = b <= a +assert c == (b[0] <= a.Num) +"); + } + + + [Test] + public void ReverseOperatorOverloads() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + + PythonEngine.Exec($@" +from {module} import * +cls = {name} +a = 2 +b = cls(10) + +c = a + b +assert c.Num == a + b.Num + +c = a - b +assert c.Num == a - b.Num + +c = a * b +assert c.Num == a * b.Num + +c = a / b +assert c.Num == a // b.Num + +c = a % b +assert c.Num == a % b.Num + +c = a & b +assert c.Num == a & b.Num + +c = a | b +assert c.Num == a | b.Num + +c = a ^ b +assert c.Num == a ^ b.Num + +c = a == b +assert c == (a == b.Num) + +c = a != b +assert c == (a != b.Num) + +c = a <= b +assert c == (a <= b.Num) + +c = a >= b +assert c == (a >= b.Num) + +c = a < b +assert c == (a < b.Num) + +c = a > b +assert c == (a > b.Num) +"); + + } + [Test] + public void ShiftOperatorOverloads() + { + string name = string.Format("{0}.{1}", + typeof(OperableObject).DeclaringType.Name, + typeof(OperableObject).Name); + string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace; + + PythonEngine.Exec($@" +from {module} import * +cls = {name} +a = cls(2) +b = cls(10) + +c = a << b.Num +assert c.Num == a.Num << b.Num + +c = a >> b.Num +assert c.Num == a.Num >> b.Num +"); + } + } +} diff --git a/src/embed_tests/TestPyIter.cs b/src/embed_tests/TestPyIter.cs new file mode 100644 index 000000000..7428da979 --- /dev/null +++ b/src/embed_tests/TestPyIter.cs @@ -0,0 +1,37 @@ +using System.Linq; +using System.Text; + +using NUnit.Framework; + +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + class TestPyIter + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + [Test] + public void KeepOldObjects() + { + using (Py.GIL()) + using (var testString = new PyString("hello world! !$%&/()=?")) + { + PyObject[] chars = testString.ToArray(); + Assert.IsTrue(chars.Length > 1); + string reconstructed = string.Concat(chars.Select(c => c.As())); + Assert.AreEqual(testString.As(), reconstructed); + } + } + } +} diff --git a/src/embed_tests/TestPyObject.cs b/src/embed_tests/TestPyObject.cs index d4952d4a3..d0d8eab45 100644 --- a/src/embed_tests/TestPyObject.cs +++ b/src/embed_tests/TestPyObject.cs @@ -59,9 +59,17 @@ def add(self, x, y): } [Test] - public void InvokeNull() { + public void InvokeNull() + { var list = PythonEngine.Eval("list"); Assert.Throws(() => list.Invoke(new PyObject[] {null})); } + + [Test] + public void AsManagedObjectInvalidCast() + { + var list = PythonEngine.Eval("list"); + Assert.Throws(() => list.AsManagedObject(typeof(int))); + } } } diff --git a/src/embed_tests/TestPyWith.cs b/src/embed_tests/TestPyWith.cs index 0c4e9023f..dcd539504 100644 --- a/src/embed_tests/TestPyWith.cs +++ b/src/embed_tests/TestPyWith.cs @@ -25,15 +25,14 @@ public void Dispose() 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') + pass def fail(self): return 5 / 0 @@ -51,6 +50,7 @@ def fail(self): } catch (PythonException e) { + TestContext.Out.WriteLine(e.Message); Assert.IsTrue(e.Message.Contains("ZeroDivisionError")); } } diff --git a/src/embed_tests/TestPythonEngineProperties.cs b/src/embed_tests/TestPythonEngineProperties.cs index 6d2d5d6cc..626e3c77f 100644 --- a/src/embed_tests/TestPythonEngineProperties.cs +++ b/src/embed_tests/TestPythonEngineProperties.cs @@ -181,13 +181,37 @@ public void SetProgramName() public void SetPythonPath() { PythonEngine.Initialize(); - string path = PythonEngine.PythonPath; + + const string moduleName = "pytest"; + bool importShouldSucceed; + try + { + Py.Import(moduleName); + importShouldSucceed = true; + } + catch + { + importShouldSucceed = false; + } + + string[] paths = Py.Import("sys").GetAttr("path").As(); + string path = string.Join(System.IO.Path.PathSeparator.ToString(), paths); + + // path should not be set to PythonEngine.PythonPath here. + // PythonEngine.PythonPath gets the default module search path, not the full search path. + // The list sys.path is initialized with this value on interpreter startup; + // it can be (and usually is) modified later to change the search path for loading modules. + // See https://docs.python.org/3/c-api/init.html#c.Py_GetPath + // After PythonPath is set, then PythonEngine.PythonPath will correctly return the full search path. + PythonEngine.Shutdown(); PythonEngine.PythonPath = path; PythonEngine.Initialize(); Assert.AreEqual(path, PythonEngine.PythonPath); + if (importShouldSucceed) Py.Import(moduleName); + PythonEngine.Shutdown(); } } diff --git a/src/embed_tests/TestPythonException.cs b/src/embed_tests/TestPythonException.cs index 000c32ca3..31addfba1 100644 --- a/src/embed_tests/TestPythonException.cs +++ b/src/embed_tests/TestPythonException.cs @@ -65,7 +65,21 @@ public void TestPythonExceptionFormat() } catch (PythonException ex) { - Assert.That(ex.Format(), Does.Contain("Traceback").And.Contains("(most recent call last):").And.Contains("ValueError: Error!")); + // Console.WriteLine($"Format: {ex.Format()}"); + // Console.WriteLine($"Stacktrace: {ex.StackTrace}"); + Assert.That( + ex.Format(), + Does.Contain("Traceback") + .And.Contains("(most recent call last):") + .And.Contains("ValueError: Error!") + ); + + // Check that the stacktrace is properly formatted + Assert.That( + ex.StackTrace, + Does.Not.StartWith("[") + .And.Not.Contain("\\n") + ); } } @@ -86,9 +100,64 @@ public void TestPythonExceptionFormatNoTraceback() } catch (PythonException ex) { - // ImportError/ModuleNotFoundError do not have a traceback when not running in a script + // ImportError/ModuleNotFoundError do not have a traceback when not running in a script Assert.AreEqual(ex.StackTrace, ex.Format()); } } + + [Test] + public void TestPythonExceptionFormatNormalized() + { + try + { + PythonEngine.Exec("a=b\n"); + Assert.Fail("Exception should have been raised"); + } + catch (PythonException ex) + { + Assert.AreEqual("Traceback (most recent call last):\n File \"\", line 1, in \nNameError: name 'b' is not defined\n", ex.Format()); + } + } + + [Test] + public void TestPythonException_PyErr_NormalizeException() + { + using (var scope = Py.CreateScope()) + { + scope.Exec(@" +class TestException(NameError): + def __init__(self, val): + super().__init__(val) + x = int(val)"); + Assert.IsTrue(scope.TryGet("TestException", out PyObject type)); + + PyObject str = "dummy string".ToPython(); + IntPtr typePtr = type.Handle; + IntPtr strPtr = str.Handle; + IntPtr tbPtr = Runtime.Runtime.None.Handle; + Runtime.Runtime.XIncref(typePtr); + Runtime.Runtime.XIncref(strPtr); + Runtime.Runtime.XIncref(tbPtr); + Runtime.Runtime.PyErr_NormalizeException(ref typePtr, ref strPtr, ref tbPtr); + + using (PyObject typeObj = new PyObject(typePtr), strObj = new PyObject(strPtr), tbObj = new PyObject(tbPtr)) + { + // the type returned from PyErr_NormalizeException should not be the same type since a new + // exception was raised by initializing the exception + Assert.AreNotEqual(type.Handle, typePtr); + // the message should now be the string from the throw exception during normalization + Assert.AreEqual("invalid literal for int() with base 10: 'dummy string'", strObj.ToString()); + } + } + } + + [Test] + public void TestPythonException_Normalize_ThrowsWhenErrorSet() + { + Exceptions.SetError(Exceptions.TypeError, "Error!"); + var pythonException = new PythonException(); + Exceptions.SetError(Exceptions.TypeError, "Another error"); + Assert.Throws(() => pythonException.Normalize()); + } } } diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs index 38878205c..59c66cc5e 100644 --- a/src/embed_tests/TestRuntime.cs +++ b/src/embed_tests/TestRuntime.cs @@ -18,25 +18,6 @@ public void SetUp() } } - /// - /// 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(NativeCodePageHelper.Machine, Is.Not.EqualTo(MachineType.Other)); - Assert.That(!string.IsNullOrEmpty(NativeCodePageHelper.MachineName)); - - Assert.That(NativeCodePageHelper.OperatingSystem, Is.Not.EqualTo(OperatingSystemType.Other)); - Assert.That(!string.IsNullOrEmpty(NativeCodePageHelper.OperatingSystemName)); - - Runtime.Runtime.Shutdown(); - } - [Test] public static void Py_IsInitializedValue() { @@ -115,13 +96,15 @@ public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test() // 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"); Exceptions.ErrorCheck(threading); - var threadingDict = Runtime.Runtime.PyModule_GetDict(threading); + var threadingDict = Runtime.Runtime.PyModule_GetDict(new BorrowedReference(threading)); Exceptions.ErrorCheck(threadingDict); var lockType = Runtime.Runtime.PyDict_GetItemString(threadingDict, "Lock"); - if (lockType == IntPtr.Zero) + if (lockType.IsNull) throw new KeyNotFoundException("class 'Lock' was not found in 'threading'"); - var lockInstance = Runtime.Runtime.PyObject_CallObject(lockType, Runtime.Runtime.PyTuple_New(0)); + var args = Runtime.Runtime.PyTuple_New(0); + var lockInstance = Runtime.Runtime.PyObject_CallObject(lockType.DangerousGetAddress(), args); + Runtime.Runtime.XDecref(args); Exceptions.ErrorCheck(lockInstance); Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(lockInstance)); diff --git a/src/embed_tests/TestTypeManager.cs b/src/embed_tests/TestTypeManager.cs deleted file mode 100644 index 43155e1bf..000000000 --- a/src/embed_tests/TestTypeManager.cs +++ /dev/null @@ -1,65 +0,0 @@ -using NUnit.Framework; -using Python.Runtime; -using Python.Runtime.Platform; -using System.Runtime.InteropServices; - -namespace Python.EmbeddingTest -{ - class TestTypeManager - { - [SetUp] - public static void Init() - { - Runtime.Runtime.Initialize(); - } - - [TearDown] - public static void Fini() - { - Runtime.Runtime.Shutdown(); - } - - [Test] - public static void TestNativeCode() - { - Assert.That(() => { var _ = NativeCodePageHelper.NativeCode.Active; }, Throws.Nothing); - Assert.That(NativeCodePageHelper.NativeCode.Active.Code.Length, Is.GreaterThan(0)); - } - - [Test] - public static void TestMemoryMapping() - { - Assert.That(() => { var _ = NativeCodePageHelper.CreateMemoryMapper(); }, Throws.Nothing); - var mapper = NativeCodePageHelper.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/pyimport.cs b/src/embed_tests/pyimport.cs index ebb4fabd0..24f31acff 100644 --- a/src/embed_tests/pyimport.cs +++ b/src/embed_tests/pyimport.cs @@ -30,12 +30,8 @@ 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); + string testPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "fixtures"); + TestContext.Out.WriteLine(testPath); IntPtr str = Runtime.Runtime.PyString_FromString(testPath); BorrowedReference path = Runtime.Runtime.PySys_GetObject("path"); diff --git a/src/monoclr/clrmod.c b/src/monoclr/clrmod.c deleted file mode 100644 index 4e8027e3a..000000000 --- a/src/monoclr/clrmod.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "pynetclr.h" - -/* List of functions defined in the module */ -static PyMethodDef clr_methods[] = { - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -PyDoc_STRVAR(clr_module_doc, - "clr facade module to initialize the CLR. It's later " - "replaced by the real clr module. This module has a facade " - "attribute to make it distinguishable from the real clr module." -); - -static PyNet_Args *pn_args; -char **environ = NULL; - -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef clrdef = { - PyModuleDef_HEAD_INIT, - "clr", /* m_name */ - clr_module_doc, /* m_doc */ - -1, /* m_size */ - clr_methods, /* m_methods */ - NULL, /* m_reload */ - NULL, /* m_traverse */ - NULL, /* m_clear */ - NULL, /* m_free */ -}; -#endif - -static PyObject *_initclr() -{ - PyObject *m; - - /* Create the module and add the functions */ -#if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&clrdef); -#else - m = Py_InitModule3("clr", clr_methods, clr_module_doc); -#endif - if (m == NULL) - return NULL; - PyModule_AddObject(m, "facade", Py_True); - Py_INCREF(Py_True); - - pn_args = PyNet_Init(1); - if (pn_args->error) - { - return NULL; - } - - if (NULL != pn_args->module) - return pn_args->module; - - return m; -} - -#if PY_MAJOR_VERSION >= 3 -PyMODINIT_FUNC -PyInit_clr(void) -{ - return _initclr(); -} -#else -PyMODINIT_FUNC -initclr(void) -{ - _initclr(); -} -#endif diff --git a/src/monoclr/pynetclr.h b/src/monoclr/pynetclr.h deleted file mode 100644 index 1863b1d43..000000000 --- a/src/monoclr/pynetclr.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef PYNET_CLR_H -#define PYNET_CLR_H - -#include -#include -#include -#include -#include -#include - -#define MONO_VERSION "v4.0.30319.1" -#define MONO_DOMAIN "Python.Runtime" -#define PR_ASSEMBLY "Python.Runtime.dll" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - MonoDomain *domain; - MonoAssembly *pr_assm; - MonoMethod *shutdown; - char *pr_file; - char *error; - char *init_name; - char *shutdown_name; - PyObject *module; -} PyNet_Args; - -PyNet_Args *PyNet_Init(int); -void PyNet_Finalize(PyNet_Args *); -void main_thread_handler(PyNet_Args *user_data); -char *PyNet_ExceptionToString(MonoObject *); - -#ifdef __cplusplus -} -#endif - -#endif // PYNET_CLR_H diff --git a/src/monoclr/pynetinit.c b/src/monoclr/pynetinit.c deleted file mode 100644 index 149d52296..000000000 --- a/src/monoclr/pynetinit.c +++ /dev/null @@ -1,261 +0,0 @@ -#include "pynetclr.h" -#include "stdlib.h" - -#ifndef _WIN32 -#include "dirent.h" -#include "dlfcn.h" -#include "libgen.h" -#include "alloca.h" -#endif - - -// initialize Mono and PythonNet -PyNet_Args *PyNet_Init(int ext) -{ - PyNet_Args *pn_args; - pn_args = (PyNet_Args *)malloc(sizeof(PyNet_Args)); - pn_args->pr_file = PR_ASSEMBLY; - pn_args->error = NULL; - pn_args->shutdown = NULL; - pn_args->module = NULL; - -#ifdef __linux__ - // Force preload libmono-2.0 as global. Without this, on some systems - // symbols from libmono are not found by libmononative (which implements - // some of the System.* namespaces). Since the only happened on Linux so - // far, we hardcode the library name, load the symbols into the global - // namespace and leak the handle. - dlopen("libmono-2.0.so", RTLD_LAZY | RTLD_GLOBAL); -#endif - - if (ext == 0) - { - pn_args->init_name = "Python.Runtime:Initialize()"; - } - else - { - pn_args->init_name = "Python.Runtime:InitExt()"; - } - pn_args->shutdown_name = "Python.Runtime:Shutdown()"; - - pn_args->domain = mono_jit_init_version(MONO_DOMAIN, MONO_VERSION); - mono_domain_set_config(pn_args->domain, ".", "Python.Runtime.dll.config"); - - /* - * Load the default Mono configuration file, this is needed - * if you are planning on using the dllmaps defined on the - * system configuration - */ - mono_config_parse(NULL); - - /* I can't use this call to run the main_thread_handler. The function - * runs it in another thread but *this* thread holds the Python - * import lock -> DEAD LOCK. - * - * mono_runtime_exec_managed_code(pn_args->domain, main_thread_handler, - * pn_args); - */ - - main_thread_handler(pn_args); - - if (pn_args->error != NULL) - { - PyErr_SetString(PyExc_ImportError, pn_args->error); - } - return pn_args; -} - -// Shuts down PythonNet and cleans up Mono -void PyNet_Finalize(PyNet_Args *pn_args) -{ - MonoObject *exception = NULL; - - if (pn_args->shutdown) - { - mono_runtime_invoke(pn_args->shutdown, NULL, NULL, &exception); - if (exception) - { - pn_args->error = PyNet_ExceptionToString(exception); - } - pn_args->shutdown = NULL; - } - - if (pn_args->domain) - { - mono_jit_cleanup(pn_args->domain); - pn_args->domain = NULL; - } - free(pn_args); -} - -MonoMethod *getMethodFromClass(MonoClass *cls, char *name) -{ - MonoMethodDesc *mdesc; - MonoMethod *method; - - mdesc = mono_method_desc_new(name, 1); - method = mono_method_desc_search_in_class(mdesc, cls); - mono_method_desc_free(mdesc); - - return method; -} - -void main_thread_handler(PyNet_Args *user_data) -{ - PyNet_Args *pn_args = user_data; - MonoMethod *init; - MonoImage *pr_image; - MonoClass *pythonengine; - MonoObject *exception = NULL; - MonoObject *init_result; - -#ifndef _WIN32 - // Get the filename of the python shared object and set - // LD_LIBRARY_PATH so Mono can find it. - Dl_info dlinfo = {0}; - if (0 != dladdr(&Py_Initialize, &dlinfo)) - { - char *fname = alloca(strlen(dlinfo.dli_fname) + 1); - strcpy(fname, dlinfo.dli_fname); - char *py_libdir = dirname(fname); - char *ld_library_path = getenv("LD_LIBRARY_PATH"); - if (NULL == ld_library_path) - { - setenv("LD_LIBRARY_PATH", py_libdir, 1); - } - else - { - char *new_ld_library_path = alloca(strlen(py_libdir) - + strlen(ld_library_path) - + 2); - strcpy(new_ld_library_path, py_libdir); - strcat(new_ld_library_path, ":"); - strcat(new_ld_library_path, ld_library_path); - setenv("LD_LIBRARY_PATH", new_ld_library_path, 1); - } - } - - //get python path system variable - PyObject *syspath = PySys_GetObject("path"); - char *runtime_full_path = (char*) malloc(1024); - const char *slash = "/"; - int found = 0; - - int ii = 0; - for (ii = 0; ii < PyList_Size(syspath); ++ii) - { -#if PY_MAJOR_VERSION >= 3 - Py_ssize_t wlen; - wchar_t *wstr = PyUnicode_AsWideCharString(PyList_GetItem(syspath, ii), &wlen); - char *pydir = (char*)malloc(wlen + 1); - size_t mblen = wcstombs(pydir, wstr, wlen + 1); - if (mblen > wlen) - pydir[wlen] = '\0'; - PyMem_Free(wstr); -#else - const char *pydir = PyString_AsString(PyList_GetItem(syspath, ii)); -#endif - char *curdir = (char*) malloc(1024); - strncpy(curdir, strlen(pydir) > 0 ? pydir : ".", 1024); - strncat(curdir, slash, 1024); - -#if PY_MAJOR_VERSION >= 3 - free(pydir); -#endif - - //look in this directory for the pn_args->pr_file - DIR *dirp = opendir(curdir); - if (dirp != NULL) - { - struct dirent *dp; - while ((dp = readdir(dirp)) != NULL) - { - if (strcmp(dp->d_name, pn_args->pr_file) == 0) - { - strcpy(runtime_full_path, curdir); - strcat(runtime_full_path, pn_args->pr_file); - found = 1; - break; - } - } - closedir(dirp); - } - free(curdir); - - if (found) - { - pn_args->pr_file = runtime_full_path; - break; - } - } - - if (!found) - { - fprintf(stderr, "Could not find assembly %s. \n", pn_args->pr_file); - return; - } -#endif - - pn_args->pr_assm = mono_domain_assembly_open(pn_args->domain, pn_args->pr_file); - if (!pn_args->pr_assm) - { - pn_args->error = "Unable to load assembly"; - return; - } -#ifndef _WIN32 - free(runtime_full_path); -#endif - - pr_image = mono_assembly_get_image(pn_args->pr_assm); - if (!pr_image) - { - pn_args->error = "Unable to get image"; - return; - } - - pythonengine = mono_class_from_name(pr_image, "Python.Runtime", "PythonEngine"); - if (!pythonengine) - { - pn_args->error = "Unable to load class PythonEngine from Python.Runtime"; - return; - } - - init = getMethodFromClass(pythonengine, pn_args->init_name); - if (!init) - { - pn_args->error = "Unable to fetch Init method from PythonEngine"; - return; - } - - pn_args->shutdown = getMethodFromClass(pythonengine, pn_args->shutdown_name); - if (!pn_args->shutdown) - { - pn_args->error = "Unable to fetch shutdown method from PythonEngine"; - return; - } - - init_result = mono_runtime_invoke(init, NULL, NULL, &exception); - if (exception) - { - pn_args->error = PyNet_ExceptionToString(exception); - return; - } - -#if PY_MAJOR_VERSION >= 3 - if (NULL != init_result) - pn_args->module = *(PyObject**)mono_object_unbox(init_result); -#endif -} - -// Get string from a Mono exception -char *PyNet_ExceptionToString(MonoObject *e) -{ - MonoMethodDesc *mdesc = mono_method_desc_new(":ToString()", 0 /*FALSE*/); - MonoMethod *mmethod = mono_method_desc_search_in_class(mdesc, mono_get_object_class()); - mono_method_desc_free(mdesc); - - mmethod = mono_object_get_virtual_method(e, mmethod); - MonoString *monoString = (MonoString*) mono_runtime_invoke(mmethod, e, NULL, NULL); - mono_runtime_invoke(mmethod, e, NULL, NULL); - return mono_string_to_utf8(monoString); -} diff --git a/src/perf_tests/Python.PerformanceTests.csproj b/src/perf_tests/Python.PerformanceTests.csproj index f84e556aa..22783e595 100644 --- a/src/perf_tests/Python.PerformanceTests.csproj +++ b/src/perf_tests/Python.PerformanceTests.csproj @@ -1,12 +1,8 @@ - net461 - DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 - bin\ - + net472 false - x64;x86 @@ -28,7 +24,7 @@ - + diff --git a/src/python_tests_runner/Python.PythonTestsRunner.csproj b/src/python_tests_runner/Python.PythonTestsRunner.csproj new file mode 100644 index 000000000..1006b2148 --- /dev/null +++ b/src/python_tests_runner/Python.PythonTestsRunner.csproj @@ -0,0 +1,26 @@ + + + + net472;netcoreapp3.1 + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + 1.0.0 + all + runtime; build; native; contentfiles; analyzers + + + + diff --git a/src/python_tests_runner/PythonTestRunner.cs b/src/python_tests_runner/PythonTestRunner.cs new file mode 100644 index 000000000..36e8049d4 --- /dev/null +++ b/src/python_tests_runner/PythonTestRunner.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +using NUnit.Framework; + +using Python.Runtime; +using Python.Test; + +namespace Python.PythonTestsRunner +{ + public class PythonTestRunner + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + /// + /// Selects the Python tests to be run as embedded tests. + /// + /// + static IEnumerable PythonTestCases() + { + // Add the test that you want to debug here. + yield return new[] { "test_enum", "test_enum_standard_attrs" }; + yield return new[] { "test_generic", "test_missing_generic_type" }; + } + + /// + /// Runs a test in src/tests/*.py as an embedded test. This facilitates debugging. + /// + /// The file name without extension + /// The name of the test method + [TestCaseSource(nameof(PythonTestCases))] + public void RunPythonTest(string testFile, string testName) + { + // Find the tests directory + string folder = typeof(PythonTestRunner).Assembly.Location; + while (Path.GetFileName(folder) != "src") + { + folder = Path.GetDirectoryName(folder); + } + folder = Path.Combine(folder, "..", "tests"); + string path = Path.Combine(folder, testFile + ".py"); + if (!File.Exists(path)) throw new FileNotFoundException("Cannot find test file", path); + + // We could use 'import' below, but importlib gives more helpful error messages than 'import' + // https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly + // Because the Python tests sometimes have relative imports, the module name must be inside the tests package + PythonEngine.Exec($@" +import sys +import os +sys.path.append(os.path.dirname(r'{folder}')) +sys.path.append(os.path.join(r'{folder}', 'fixtures')) +import clr +clr.AddReference('Python.Test') +import tests +module_name = 'tests.{testFile}' +file_path = r'{path}' +import importlib.util +spec = importlib.util.spec_from_file_location(module_name, file_path) +module = importlib.util.module_from_spec(spec) +sys.modules[module_name] = module +try: + spec.loader.exec_module(module) +except ImportError as error: + raise ImportError(str(error) + ' when sys.path=' + os.pathsep.join(sys.path)) +module.{testName}() +"); + } + } +} diff --git a/src/runtime/BorrowedReference.cs b/src/runtime/BorrowedReference.cs index 8ae382e77..2f5c347c7 100644 --- a/src/runtime/BorrowedReference.cs +++ b/src/runtime/BorrowedReference.cs @@ -10,10 +10,11 @@ readonly ref struct BorrowedReference readonly IntPtr pointer; public bool IsNull => this.pointer == IntPtr.Zero; - /// Gets a raw pointer to the Python object public IntPtr DangerousGetAddress() => this.IsNull ? throw new NullReferenceException() : this.pointer; + /// Gets a raw pointer to the Python object + public IntPtr DangerousGetAddressOrNull() => this.pointer; /// /// Creates new instance of from raw pointer. Unsafe. @@ -22,5 +23,19 @@ public BorrowedReference(IntPtr pointer) { this.pointer = pointer; } + + public static bool operator ==(BorrowedReference a, BorrowedReference b) + => a.pointer == b.pointer; + public static bool operator !=(BorrowedReference a, BorrowedReference b) + => a.pointer != b.pointer; + + public override bool Equals(object obj) { + if (obj is IntPtr ptr) + return ptr == pointer; + + return false; + } + + public override int GetHashCode() => pointer.GetHashCode(); } } diff --git a/src/runtime/Codecs/DecoderGroup.cs b/src/runtime/Codecs/DecoderGroup.cs index 8a290d5d4..cc511ed50 100644 --- a/src/runtime/Codecs/DecoderGroup.cs +++ b/src/runtime/Codecs/DecoderGroup.cs @@ -8,7 +8,6 @@ namespace Python.Runtime.Codecs /// /// Represents a group of s. Useful to group them by priority. /// - [Obsolete(Util.UnstableApiMessage)] public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable { readonly List decoders = new List(); @@ -49,7 +48,6 @@ public bool TryDecode(PyObject pyObj, out T value) IEnumerator IEnumerable.GetEnumerator() => this.decoders.GetEnumerator(); } - [Obsolete(Util.UnstableApiMessage)] public static class DecoderGroupExtensions { /// @@ -58,7 +56,6 @@ public static class DecoderGroupExtensions /// that can decode from to , /// or null if a matching decoder can not be found. /// - [Obsolete(Util.UnstableApiMessage)] public static IPyObjectDecoder GetDecoder( this IPyObjectDecoder decoder, PyObject objectType, Type targetType) diff --git a/src/runtime/Codecs/EncoderGroup.cs b/src/runtime/Codecs/EncoderGroup.cs index a5708c0bb..4f776a669 100644 --- a/src/runtime/Codecs/EncoderGroup.cs +++ b/src/runtime/Codecs/EncoderGroup.cs @@ -8,7 +8,6 @@ namespace Python.Runtime.Codecs /// /// Represents a group of s. Useful to group them by priority. /// - [Obsolete(Util.UnstableApiMessage)] public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable { readonly List encoders = new List(); @@ -50,7 +49,6 @@ public PyObject TryEncode(object value) IEnumerator IEnumerable.GetEnumerator() => this.encoders.GetEnumerator(); } - [Obsolete(Util.UnstableApiMessage)] public static class EncoderGroupExtensions { /// @@ -58,7 +56,6 @@ public static class EncoderGroupExtensions /// (potentially selecting one from a collection), /// that can encode the specified . /// - [Obsolete(Util.UnstableApiMessage)] public static IEnumerable GetEncoders(this IPyObjectEncoder decoder, Type type) { if (decoder is null) throw new ArgumentNullException(nameof(decoder)); diff --git a/src/runtime/Codecs/IterableDecoder.cs b/src/runtime/Codecs/IterableDecoder.cs new file mode 100644 index 000000000..346057238 --- /dev/null +++ b/src/runtime/Codecs/IterableDecoder.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; + +namespace Python.Runtime.Codecs +{ + public class IterableDecoder : IPyObjectDecoder + { + internal static bool IsIterable(Type targetType) + { + //if it is a plain IEnumerable, we can decode it using sequence protocol. + if (targetType == typeof(System.Collections.IEnumerable)) + return true; + + if (!targetType.IsGenericType) + return false; + + return targetType.GetGenericTypeDefinition() == typeof(IEnumerable<>); + } + + internal static bool IsIterable(PyObject objectType) + { + return objectType.HasAttr("__iter__"); + } + + public bool CanDecode(PyObject objectType, Type targetType) + { + return IsIterable(objectType) && IsIterable(targetType); + } + + public bool TryDecode(PyObject pyObj, out T value) + { + //first see if T is a plan IEnumerable + if (typeof(T) == typeof(System.Collections.IEnumerable)) + { + object enumerable = new CollectionWrappers.IterableWrapper(pyObj); + value = (T)enumerable; + return true; + } + + var elementType = typeof(T).GetGenericArguments()[0]; + var collectionType = typeof(CollectionWrappers.IterableWrapper<>).MakeGenericType(elementType); + + var instance = Activator.CreateInstance(collectionType, new[] { pyObj }); + value = (T)instance; + return true; + } + + public static IterableDecoder Instance { get; } = new IterableDecoder(); + + public static void Register() + { + PyObjectConversions.RegisterDecoder(Instance); + } + } +} diff --git a/src/runtime/Codecs/ListDecoder.cs b/src/runtime/Codecs/ListDecoder.cs new file mode 100644 index 000000000..013f3f3f9 --- /dev/null +++ b/src/runtime/Codecs/ListDecoder.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; + +namespace Python.Runtime.Codecs +{ + public class ListDecoder : IPyObjectDecoder + { + private static bool IsList(Type targetType) + { + if (!targetType.IsGenericType) + return false; + + return targetType.GetGenericTypeDefinition() == typeof(IList<>); + } + + private static bool IsList(PyObject objectType) + { + //TODO accept any python object that implements the sequence and list protocols + //must implement sequence protocol to fully implement list protocol + //if (!SequenceDecoder.IsSequence(objectType)) return false; + + //returns wheter the type is a list. + return objectType.Handle == Runtime.PyListType; + } + + public bool CanDecode(PyObject objectType, Type targetType) + { + return IsList(objectType) && IsList(targetType); + } + + public bool TryDecode(PyObject pyObj, out T value) + { + if (pyObj == null) throw new ArgumentNullException(nameof(pyObj)); + + var elementType = typeof(T).GetGenericArguments()[0]; + Type collectionType = typeof(CollectionWrappers.ListWrapper<>).MakeGenericType(elementType); + + var instance = Activator.CreateInstance(collectionType, new[] { pyObj }); + value = (T)instance; + return true; + } + + public static ListDecoder Instance { get; } = new ListDecoder(); + + public static void Register() + { + PyObjectConversions.RegisterDecoder(Instance); + } + } +} diff --git a/src/runtime/Codecs/RawProxyEncoder.cs b/src/runtime/Codecs/RawProxyEncoder.cs index a1b6c52b3..37ad0487b 100644 --- a/src/runtime/Codecs/RawProxyEncoder.cs +++ b/src/runtime/Codecs/RawProxyEncoder.cs @@ -6,7 +6,6 @@ namespace Python.Runtime.Codecs /// A .NET object encoder, that returns raw proxies (e.g. no conversion to Python types). /// You must inherit from this class and override . /// - [Obsolete(Util.UnstableApiMessage)] public class RawProxyEncoder: IPyObjectEncoder { public PyObject TryEncode(object value) diff --git a/src/runtime/Codecs/SequenceDecoder.cs b/src/runtime/Codecs/SequenceDecoder.cs new file mode 100644 index 000000000..dce08fd99 --- /dev/null +++ b/src/runtime/Codecs/SequenceDecoder.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; + +namespace Python.Runtime.Codecs +{ + public class SequenceDecoder : IPyObjectDecoder + { + internal static bool IsSequence(Type targetType) + { + if (!targetType.IsGenericType) + return false; + + return targetType.GetGenericTypeDefinition() == typeof(ICollection<>); + } + + internal static bool IsSequence(PyObject objectType) + { + //must implement iterable protocol to fully implement sequence protocol + if (!IterableDecoder.IsIterable(objectType)) return false; + + //returns wheter it implements the sequence protocol + //according to python doc this needs to exclude dict subclasses + //but I don't know how to look for that given the objectType + //rather than the instance. + return objectType.HasAttr("__getitem__"); + } + + public bool CanDecode(PyObject objectType, Type targetType) + { + return IsSequence(objectType) && IsSequence(targetType); + } + + public bool TryDecode(PyObject pyObj, out T value) + { + if (pyObj == null) throw new ArgumentNullException(nameof(pyObj)); + + var elementType = typeof(T).GetGenericArguments()[0]; + Type collectionType = typeof(CollectionWrappers.SequenceWrapper<>).MakeGenericType(elementType); + + var instance = Activator.CreateInstance(collectionType, new[] { pyObj }); + value = (T)instance; + return true; + } + + public static SequenceDecoder Instance { get; } = new SequenceDecoder(); + + public static void Register() + { + PyObjectConversions.RegisterDecoder(Instance); + } + } +} diff --git a/src/runtime/Codecs/TupleCodecs.cs b/src/runtime/Codecs/TupleCodecs.cs index a9ae33fe0..5ac55846f 100644 --- a/src/runtime/Codecs/TupleCodecs.cs +++ b/src/runtime/Codecs/TupleCodecs.cs @@ -5,7 +5,6 @@ namespace Python.Runtime.Codecs using System.Linq; using System.Reflection; - [Obsolete(Util.UnstableApiMessage)] public sealed class TupleCodec : IPyObjectEncoder, IPyObjectDecoder { TupleCodec() { } diff --git a/src/runtime/CollectionWrappers/IterableWrapper.cs b/src/runtime/CollectionWrappers/IterableWrapper.cs new file mode 100644 index 000000000..97979b59b --- /dev/null +++ b/src/runtime/CollectionWrappers/IterableWrapper.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Collections; + +namespace Python.Runtime.CollectionWrappers +{ + internal class IterableWrapper : IEnumerable + { + protected readonly PyObject pyObject; + + public IterableWrapper(PyObject pyObj) + { + if (pyObj == null) + throw new ArgumentNullException(); + pyObject = new PyObject(pyObj.Reference); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public IEnumerator GetEnumerator() + { + PyObject iterObject = null; + using (Py.GIL()) + iterObject = new PyObject(Runtime.PyObject_GetIter(pyObject.Handle)); + + while (true) + { + using (Py.GIL()) + { + var item = Runtime.PyIter_Next(iterObject.Handle); + if (item == IntPtr.Zero) + { + Runtime.CheckExceptionOccurred(); + iterObject.Dispose(); + break; + } + + yield return (T)new PyObject(item).AsManagedObject(typeof(T)); + } + } + } + } +} diff --git a/src/runtime/CollectionWrappers/ListWrapper.cs b/src/runtime/CollectionWrappers/ListWrapper.cs new file mode 100644 index 000000000..ec2476370 --- /dev/null +++ b/src/runtime/CollectionWrappers/ListWrapper.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; + +namespace Python.Runtime.CollectionWrappers +{ + internal class ListWrapper : SequenceWrapper, IList + { + public ListWrapper(PyObject pyObj) : base(pyObj) + { + + } + + public T this[int index] + { + get + { + var item = Runtime.PyList_GetItem(pyObject.Reference, index); + var pyItem = new PyObject(item); + return pyItem.As(); + } + set + { + var pyItem = value.ToPython(); + var result = Runtime.PyList_SetItem(pyObject.Handle, index, pyItem.Handle); + if (result == -1) + Runtime.CheckExceptionOccurred(); + } + } + + public int IndexOf(T item) + { + return indexOf(item); + } + + public void Insert(int index, T item) + { + if (IsReadOnly) + throw new InvalidOperationException("Collection is read-only"); + + var pyItem = item.ToPython(); + + var result = Runtime.PyList_Insert(pyObject.Reference, index, pyItem.Handle); + if (result == -1) + Runtime.CheckExceptionOccurred(); + } + + public void RemoveAt(int index) + { + var result = removeAt(index); + + //PySequence_DelItem will set an error if it fails. throw it here + //since RemoveAt does not have a bool return value. + if (result == false) + Runtime.CheckExceptionOccurred(); + } + } +} diff --git a/src/runtime/CollectionWrappers/SequenceWrapper.cs b/src/runtime/CollectionWrappers/SequenceWrapper.cs new file mode 100644 index 000000000..945019850 --- /dev/null +++ b/src/runtime/CollectionWrappers/SequenceWrapper.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; + +namespace Python.Runtime.CollectionWrappers +{ + internal class SequenceWrapper : IterableWrapper, ICollection + { + public SequenceWrapper(PyObject pyObj) : base(pyObj) + { + + } + + public int Count + { + get + { + var size = Runtime.PySequence_Size(pyObject.Reference); + if (size == -1) + { + Runtime.CheckExceptionOccurred(); + } + + return (int)size; + } + } + + public virtual bool IsReadOnly => false; + + public virtual void Add(T item) + { + //not implemented for Python sequence. + //ICollection is the closest analogue but it doesn't map perfectly. + //SequenceWrapper is not final and can be subclassed if necessary + throw new NotImplementedException(); + } + + public void Clear() + { + if (IsReadOnly) + throw new NotImplementedException(); + var result = Runtime.PySequence_DelSlice(pyObject.Handle, 0, Count); + if (result == -1) + { + Runtime.CheckExceptionOccurred(); + } + } + + public bool Contains(T item) + { + //not sure if IEquatable is implemented and this will work! + foreach (var element in this) + if (element.Equals(item)) return true; + + return false; + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (array == null) + throw new NullReferenceException(); + + if ((array.Length - arrayIndex) < this.Count) + throw new InvalidOperationException("Attempting to copy to an array that is too small"); + + var index = 0; + foreach (var item in this) + { + array[index + arrayIndex] = item; + index++; + } + } + + protected bool removeAt(int index) + { + if (IsReadOnly) + throw new NotImplementedException(); + if (index >= Count || index < 0) + return false; + + var result = Runtime.PySequence_DelItem(pyObject.Handle, index); + + if (result == 0) + return true; + + Runtime.CheckExceptionOccurred(); + return false; + } + + protected int indexOf(T item) + { + var index = 0; + foreach (var element in this) + { + if (element.Equals(item)) return index; + index++; + } + + return -1; + } + + public bool Remove(T item) + { + var result = removeAt(indexOf(item)); + + //clear the python exception from PySequence_DelItem + //it is idiomatic in C# to return a bool rather than + //throw for a failed Remove in ICollection + if (result == false) + Runtime.PyErr_Clear(); + return result; + } + } +} diff --git a/src/runtime/CustomMarshaler.cs b/src/runtime/CustomMarshaler.cs index 0cbbbaba2..4814e6c0b 100644 --- a/src/runtime/CustomMarshaler.cs +++ b/src/runtime/CustomMarshaler.cs @@ -41,8 +41,9 @@ public int GetNativeDataSize() /// internal class UcsMarshaler : MarshalerBase { + internal static readonly int _UCS = Runtime.PyUnicode_GetMax() <= 0xFFFF ? 2 : 4; + internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32; private static readonly MarshalerBase Instance = new UcsMarshaler(); - private static readonly Encoding PyEncoding = Runtime.PyEncoding; public override IntPtr MarshalManagedToNative(object managedObj) { @@ -91,13 +92,13 @@ public static int GetUnicodeByteLength(IntPtr p) var len = 0; while (true) { - int c = Runtime._UCS == 2 + int c = _UCS == 2 ? Marshal.ReadInt16(p, len * 2) : Marshal.ReadInt32(p, len * 4); if (c == 0) { - return len * Runtime._UCS; + return len * _UCS; } checked { @@ -147,7 +148,7 @@ public static string PtrToPy3UnicodePy2String(IntPtr p) internal class StrArrayMarshaler : MarshalerBase { private static readonly MarshalerBase Instance = new StrArrayMarshaler(); - private static readonly Encoding PyEncoding = Runtime.PyEncoding; + private static readonly Encoding PyEncoding = UcsMarshaler.PyEncoding; public override IntPtr MarshalManagedToNative(object managedObj) { @@ -159,7 +160,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 * UcsMarshaler._UCS; IntPtr mem = Marshal.AllocHGlobal(memSize); try diff --git a/src/runtime/NewReference.cs b/src/runtime/NewReference.cs index 89d53bb36..f19dfd04c 100644 --- a/src/runtime/NewReference.cs +++ b/src/runtime/NewReference.cs @@ -11,6 +11,16 @@ ref struct NewReference { IntPtr pointer; + /// Creates a pointing to the same object + public NewReference(BorrowedReference reference, bool canBeNull = false) + { + var address = canBeNull + ? reference.DangerousGetAddressOrNull() + : reference.DangerousGetAddress(); + Runtime.XIncref(address); + this.pointer = address; + } + [Pure] public static implicit operator BorrowedReference(in NewReference reference) => new BorrowedReference(reference.pointer); @@ -28,6 +38,24 @@ public PyObject MoveToPyObject() return result; } + /// Moves ownership of this instance to unmanged pointer + public IntPtr DangerousMoveToPointer() + { + if (this.IsNull()) throw new NullReferenceException(); + + var result = this.pointer; + this.pointer = IntPtr.Zero; + return result; + } + + /// Moves ownership of this instance to unmanged pointer + public IntPtr DangerousMoveToPointerOrNull() + { + var result = this.pointer; + this.pointer = IntPtr.Zero; + return result; + } + /// /// Removes this reference to a Python object, and sets it to null. /// diff --git a/src/runtime/Properties/AssemblyInfo.cs b/src/runtime/Properties/AssemblyInfo.cs index cab9df30b..3417bccc8 100644 --- a/src/runtime/Properties/AssemblyInfo.cs +++ b/src/runtime/Properties/AssemblyInfo.cs @@ -1,11 +1,3 @@ -using System.Reflection; using System.Runtime.CompilerServices; -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Python.NET")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyDefaultAlias("Python.Runtime.dll")] - -[assembly: InternalsVisibleTo("Python.EmbeddingTest")] +[assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")] diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj deleted file mode 100644 index d753a5dff..000000000 --- a/src/runtime/Python.Runtime.15.csproj +++ /dev/null @@ -1,154 +0,0 @@ - - - - net40;netstandard2.0 - AnyCPU - DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 - net45 - Python.Runtime - Python.Runtime - pythonnet - 2.5.0 - true - false - Python.NET - Copyright (c) 2006-2020 the contributors of the Python.NET project - Python and CLR (.NET and Mono) cross-platform language interop - pythonnet - https://github.com/pythonnet/pythonnet/blob/master/LICENSE - https://github.com/pythonnet/pythonnet - git - - python interop dynamic dlr Mono pinvoke - https://raw.githubusercontent.com/pythonnet/pythonnet/master/src/console/python-clear.ico - https://pythonnet.github.io/ - bin\ - false - $(OutputPath)\$(AssemblyName).xml - $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml - 1591;NU1701 - ..\..\ - $(SolutionDir)\bin\ - $(PythonBuildDir)\$(TargetFramework)\ - 7.3 - 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) - PYTHON38 - $(PYTHONNET_WIN_DEFINE_CONSTANTS) - UCS2 - $(PYTHONNET_MONO_DEFINE_CONSTANTS) - UCS4;MONO_LINUX;PYTHON_WITH_PYMALLOC - $(PYTHONNET_INTEROP_FILE) - - - false - full - - - true - pdbonly - - - true - false - full - - - true - true - portable - - - - $(DefineConstants);PYTHON2;$(Python2Version);$(PythonMonoDefineConstants) - - - $(DefineConstants);PYTHON3;$(Python3Version);$(PythonMonoDefineConstants) - - - $(DefineConstants);PYTHON2;$(Python2Version);$(PythonMonoDefineConstants);FINALIZER_CHECK;TRACE;DEBUG - - - $(DefineConstants);PYTHON3;$(Python3Version);$(PythonMonoDefineConstants);FINALIZER_CHECK;TRACE;DEBUG - - - $(DefineConstants);PYTHON2;$(Python2Version);$(PythonWinDefineConstants) - - - $(DefineConstants);PYTHON3;$(Python3Version);$(PythonWinDefineConstants) - - - $(DefineConstants);PYTHON2;$(Python2Version);$(PythonWinDefineConstants);FINALIZER_CHECK;TRACE;DEBUG - - - $(DefineConstants);PYTHON3;$(Python3Version);$(PythonWinDefineConstants);FINALIZER_CHECK;TRACE;DEBUG - - - - - - - - - - - - - - - - - - - - - - clr.py - - - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - $(TargetPath) - $(TargetDir)$(TargetName).pdb - - - - - - - - - - - diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 08dc1d860..0311dbf9a 100644 --- a/src/runtime/Python.Runtime.csproj +++ b/src/runtime/Python.Runtime.csproj @@ -1,188 +1,53 @@ - - - - Debug - AnyCPU - {097B4AC0-74E9-4C58-BCF8-C69746EC8271} - Library - Python.Runtime - Python.Runtime - bin\Python.Runtime.xml - bin\ - v4.0 - - 1591 - ..\..\ - $(SolutionDir)\bin\ - Properties - 7.3 - true - false - ..\pythonnet.snk - - - - - - PYTHON2;PYTHON27;UCS4 - true - pdbonly - - - PYTHON3;PYTHON38;UCS4 - true - pdbonly - - - true - PYTHON2;PYTHON27;UCS4;TRACE;DEBUG - false - full - - - true - PYTHON3;PYTHON38;UCS4;TRACE;DEBUG - false - full - - - PYTHON2;PYTHON27;UCS2 - true - pdbonly - - - PYTHON3;PYTHON38;UCS2 - true - pdbonly - - - true - PYTHON2;PYTHON27;UCS2;TRACE;DEBUG - false - full - - - true - PYTHON3;PYTHON38;UCS2;TRACE;DEBUG - false - full - - - - - - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - clr.py - - - - - $(TargetPath) - $(TargetDir)$(TargetName).pdb - - - - - + + + netstandard2.0 + AnyCPU + 9.0 + Python.Runtime + Python.Runtime + + pythonnet + LICENSE + https://github.com/pythonnet/pythonnet + git + python interop dynamic dlr Mono pinvoke + https://raw.githubusercontent.com/pythonnet/pythonnet/master/src/console/python-clear.ico + https://pythonnet.github.io/ + true + Python and CLR (.NET and Mono) cross-platform language interop + + true + true + snupkg + + ..\pythonnet.snk + true + + 1591;NU1701 + True + + true + + + + ..\..\pythonnet\runtime + false + + + + + + + + + + clr.py + + + + + + + + diff --git a/src/runtime/StateSerialization/MaybeMemberInfo.cs b/src/runtime/StateSerialization/MaybeMemberInfo.cs new file mode 100644 index 000000000..e14e74bbc --- /dev/null +++ b/src/runtime/StateSerialization/MaybeMemberInfo.cs @@ -0,0 +1,118 @@ +using System; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.IO; + +namespace Python.Runtime +{ + [Serializable] + internal struct MaybeMemberInfo : ISerializable where T : MemberInfo + { + public static implicit operator MaybeMemberInfo(T ob) => new MaybeMemberInfo(ob); + + // .ToString() of the serialized object + const string SerializationName = "s"; + // The ReflectedType of the object + const string SerializationType = "t"; + const string SerializationFieldName = "f"; + string name; + MemberInfo info; + + [NonSerialized] + Exception deserializationException; + + public string DeletedMessage + { + get + { + return $"The .NET {typeof(T)} {name} no longer exists. Cause: " + deserializationException?.Message ; + } + } + + public T Value + { + get + { + if (info == null) + { + throw new SerializationException(DeletedMessage, innerException: deserializationException); + } + return (T)info; + } + } + + public string Name => name; + public bool Valid => info != null; + + public override string ToString() + { + return (info != null ? info.ToString() : $"missing type: {name}"); + } + + public MaybeMemberInfo(T fi) + { + info = fi; + name = info?.ToString(); + deserializationException = null; + } + + internal MaybeMemberInfo(SerializationInfo serializationInfo, StreamingContext context) + { + // Assumption: name is always stored in "s" + name = serializationInfo.GetString(SerializationName); + info = null; + deserializationException = null; + try + { + var tp = Type.GetType(serializationInfo.GetString(SerializationType)); + if (tp != null) + { + var field_name = serializationInfo.GetString(SerializationFieldName); + MemberInfo mi = tp.GetField(field_name, ClassManager.BindingFlags); + if (mi != null && ShouldBindMember(mi)) + { + info = mi; + } + } + } + catch (Exception e) + { + deserializationException = e; + } + } + + // This is complicated because we bind fields + // based on the visibility of the field, properties + // based on it's setter/getter (which is a method + // info) visibility and events based on their + // AddMethod visibility. + static bool ShouldBindMember(MemberInfo mi) + { + if (mi is PropertyInfo pi) + { + return ClassManager.ShouldBindProperty(pi); + } + else if (mi is FieldInfo fi) + { + return ClassManager.ShouldBindField(fi); + } + else if (mi is EventInfo ei) + { + return ClassManager.ShouldBindEvent(ei); + } + + return false; + } + + public void GetObjectData(SerializationInfo serializationInfo, StreamingContext context) + { + serializationInfo.AddValue(SerializationName, name); + if (Valid) + { + serializationInfo.AddValue(SerializationFieldName, info.Name); + serializationInfo.AddValue(SerializationType, info.ReflectedType.AssemblyQualifiedName); + } + } + } +} diff --git a/src/runtime/StateSerialization/MaybeMethodBase.cs b/src/runtime/StateSerialization/MaybeMethodBase.cs new file mode 100644 index 000000000..d18c94059 --- /dev/null +++ b/src/runtime/StateSerialization/MaybeMethodBase.cs @@ -0,0 +1,199 @@ +using System; +using System.Reflection; +using System.Runtime.Serialization; +using System.Linq; + +namespace Python.Runtime +{ + [Serializable] + internal struct MaybeMethodBase : ISerializable where T: MethodBase + { + // .ToString() of the serialized object + const string SerializationName = "s"; + // The ReflectedType of the object + const string SerializationType = "t"; + // Fhe parameters of the MethodBase + const string SerializationParameters = "p"; + const string SerializationIsCtor = "c"; + const string SerializationMethodName = "n"; + + [Serializable] + struct ParameterHelper : IEquatable + { + public enum TypeModifier + { + None, + In, + Out, + Ref + } + public readonly string Name; + public readonly TypeModifier Modifier; + + public ParameterHelper(ParameterInfo tp) + { + Name = tp.ParameterType.AssemblyQualifiedName; + Modifier = TypeModifier.None; + + if (tp.IsIn && tp.ParameterType.IsByRef) + { + Modifier = TypeModifier.In; + } + else if (tp.IsOut && tp.ParameterType.IsByRef) + { + Modifier = TypeModifier.Out; + } + else if (tp.ParameterType.IsByRef) + { + Modifier = TypeModifier.Ref; + } + } + + public bool Equals(ParameterInfo other) + { + return this.Equals(new ParameterHelper(other)); + } + } + public static implicit operator MaybeMethodBase (T ob) => new MaybeMethodBase(ob); + + string name; + MethodBase info; + + [NonSerialized] + Exception deserializationException; + + public string DeletedMessage + { + get + { + return $"The .NET {typeof(T)} {name} no longer exists. Cause: " + deserializationException?.Message ; + } + } + + public T Value + { + get + { + if (info == null) + { + throw new SerializationException(DeletedMessage, innerException: deserializationException); + } + return (T)info; + } + } + + public T UnsafeValue { get { return (T)info; } } + public string Name {get{return name;}} + public bool Valid => info != null; + + public override string ToString() + { + return (info != null ? info.ToString() : $"missing method info: {name}"); + } + + public MaybeMethodBase(T mi) + { + info = mi; + name = mi?.ToString(); + deserializationException = null; + } + + internal MaybeMethodBase(SerializationInfo serializationInfo, StreamingContext context) + { + name = serializationInfo.GetString(SerializationName); + info = null; + deserializationException = null; + try + { + // Retrieve the reflected type of the method; + var typeName = serializationInfo.GetString(SerializationType); + var tp = Type.GetType(typeName); + if (tp == null) + { + throw new SerializationException($"The underlying type {typeName} can't be found"); + } + // Get the method's parameters types + var field_name = serializationInfo.GetString(SerializationMethodName); + var param = (ParameterHelper[])serializationInfo.GetValue(SerializationParameters, typeof(ParameterHelper[])); + Type[] types = new Type[param.Length]; + bool hasRefType = false; + for (int i = 0; i < param.Length; i++) + { + var paramTypeName = param[i].Name; + types[i] = Type.GetType(paramTypeName); + if (types[i] == null) + { + throw new SerializationException($"The parameter of type {paramTypeName} can't be found"); + } + else if (types[i].IsByRef) + { + hasRefType = true; + } + } + + MethodBase mb = null; + if (serializationInfo.GetBoolean(SerializationIsCtor)) + { + // We never want the static constructor. + mb = tp.GetConstructor(ClassManager.BindingFlags&(~BindingFlags.Static), binder:null, types:types, modifiers:null); + } + else + { + mb = tp.GetMethod(field_name, ClassManager.BindingFlags, binder:null, types:types, modifiers:null); + } + + if (mb != null && hasRefType) + { + mb = CheckRefTypes(mb, param); + } + + // Do like in ClassManager.GetClassInfo + if(mb != null && ClassManager.ShouldBindMethod(mb)) + { + info = mb; + } + } + catch (Exception e) + { + deserializationException = e; + } + } + + MethodBase CheckRefTypes(MethodBase mb, ParameterHelper[] ph) + { + // One more step: Changing: + // void MyFn (ref int a) + // to: + // void MyFn (out int a) + // will still find the function correctly as, `in`, `out` and `ref` + // are all represented as a reference type. Query the method we got + // and validate the parameters + if (ph.Length != 0) + { + foreach (var item in Enumerable.Zip(ph, mb.GetParameters(), (orig, current) => new {orig, current})) + { + if (!item.current.Equals(item.orig)) + { + // False positive + return null; + } + } + } + + return mb; + } + + public void GetObjectData(SerializationInfo serializationInfo, StreamingContext context) + { + serializationInfo.AddValue(SerializationName, name); + if (Valid) + { + serializationInfo.AddValue(SerializationMethodName, info.Name); + serializationInfo.AddValue(SerializationType, info.ReflectedType.AssemblyQualifiedName); + ParameterHelper[] parameters = (from p in info.GetParameters() select new ParameterHelper(p)).ToArray(); + serializationInfo.AddValue(SerializationParameters, parameters, typeof(ParameterHelper[])); + serializationInfo.AddValue(SerializationIsCtor, info.IsConstructor); + } + } + } +} \ No newline at end of file diff --git a/src/runtime/StateSerialization/MaybeType.cs b/src/runtime/StateSerialization/MaybeType.cs new file mode 100644 index 000000000..abb3a8fb6 --- /dev/null +++ b/src/runtime/StateSerialization/MaybeType.cs @@ -0,0 +1,64 @@ +using System; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.IO; + +namespace Python.Runtime +{ + [Serializable] + internal struct MaybeType : ISerializable + { + public static implicit operator MaybeType (Type ob) => new MaybeType(ob); + + // The AssemblyQualifiedName of the serialized Type + const string SerializationName = "n"; + string name; + Type type; + + public string DeletedMessage + { + get + { + return $"The .NET Type {name} no longer exists"; + } + } + + public Type Value + { + get + { + if (type == null) + { + throw new SerializationException(DeletedMessage); + } + return type; + } + } + + public string Name => name; + public bool Valid => type != null; + + public override string ToString() + { + return (type != null ? type.ToString() : $"missing type: {name}"); + } + + public MaybeType(Type tp) + { + type = tp; + name = tp.AssemblyQualifiedName; + } + + private MaybeType(SerializationInfo serializationInfo, StreamingContext context) + { + name = (string)serializationInfo.GetValue(SerializationName, typeof(string)); + type = Type.GetType(name, throwOnError:false); + } + + public void GetObjectData(SerializationInfo serializationInfo, StreamingContext context) + { + serializationInfo.AddValue(SerializationName, name); + } + } +} \ No newline at end of file diff --git a/src/runtime/arrayobject.cs b/src/runtime/arrayobject.cs index 0db84dd90..5c97c6dbf 100644 --- a/src/runtime/arrayobject.cs +++ b/src/runtime/arrayobject.cs @@ -20,21 +20,114 @@ internal override bool CanSubclass() return false; } - public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) + public static IntPtr tp_new(IntPtr tpRaw, IntPtr args, IntPtr kw) { + if (kw != IntPtr.Zero) + { + return Exceptions.RaiseTypeError("array constructor takes no keyword arguments"); + } + + var tp = new BorrowedReference(tpRaw); + var self = GetManagedObject(tp) as ArrayObject; - if (Runtime.PyTuple_Size(args) != 1) + if (!self.type.Valid) { - return Exceptions.RaiseTypeError("array expects 1 argument"); + return Exceptions.RaiseTypeError(self.type.DeletedMessage); } + Type arrType = self.type.Value; + + long[] dimensions = new long[Runtime.PyTuple_Size(args)]; + if (dimensions.Length == 0) + { + return Exceptions.RaiseTypeError("array constructor requires at least one integer argument or an object convertible to array"); + } + if (dimensions.Length != 1) + { + return CreateMultidimensional(arrType.GetElementType(), dimensions, + shapeTuple: new BorrowedReference(args), + pyType: tp) + .DangerousMoveToPointerOrNull(); + } + IntPtr op = Runtime.PyTuple_GetItem(args, 0); + + // create single dimensional array + if (Runtime.PyInt_Check(op)) + { + dimensions[0] = Runtime.PyLong_AsSignedSize_t(op); + if (dimensions[0] == -1 && Exceptions.ErrorOccurred()) + { + Exceptions.Clear(); + } + else + { + return NewInstance(arrType.GetElementType(), tp, dimensions) + .DangerousMoveToPointerOrNull(); + } + } object result; - if (!Converter.ToManaged(op, self.type, out result, true)) + // this implements casting to Array[T] + if (!Converter.ToManaged(op, arrType, out result, true)) { return IntPtr.Zero; } - return CLRObject.GetInstHandle(result, tp); + return CLRObject.GetInstHandle(result, tp) + .DangerousGetAddress(); + } + + static NewReference CreateMultidimensional(Type elementType, long[] dimensions, BorrowedReference shapeTuple, BorrowedReference pyType) + { + for (int dimIndex = 0; dimIndex < dimensions.Length; dimIndex++) + { + BorrowedReference dimObj = Runtime.PyTuple_GetItem(shapeTuple, dimIndex); + PythonException.ThrowIfIsNull(dimObj); + + if (!Runtime.PyInt_Check(dimObj)) + { + Exceptions.RaiseTypeError("array constructor expects integer dimensions"); + return default; + } + + dimensions[dimIndex] = Runtime.PyLong_AsSignedSize_t(dimObj); + if (dimensions[dimIndex] == -1 && Exceptions.ErrorOccurred()) + { + Exceptions.RaiseTypeError("array constructor expects integer dimensions"); + return default; + } + } + + return NewInstance(elementType, pyType, dimensions); + } + + static NewReference NewInstance(Type elementType, BorrowedReference arrayPyType, long[] dimensions) + { + object result; + try + { + result = Array.CreateInstance(elementType, dimensions); + } + catch (ArgumentException badArgument) + { + Exceptions.SetError(Exceptions.ValueError, badArgument.Message); + return default; + } + catch (OverflowException overflow) + { + Exceptions.SetError(overflow); + return default; + } + catch (NotSupportedException notSupported) + { + Exceptions.SetError(notSupported); + return default; + } + catch (OutOfMemoryException oom) + { + Exceptions.SetError(Exceptions.MemoryError, oom.Message); + return default; + } + return CLRObject.GetInstHandle(result, arrayPyType); } @@ -45,8 +138,12 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { var obj = (CLRObject)GetManagedObject(ob); var arrObj = (ArrayObject)GetManagedObjectType(ob); + if (!arrObj.type.Valid) + { + return Exceptions.RaiseTypeError(arrObj.type.DeletedMessage); + } var items = obj.inst as Array; - Type itemType = arrObj.type.GetElementType(); + Type itemType = arrObj.type.Value.GetElementType(); int rank = items.Rank; int index; object value; @@ -62,6 +159,10 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) if (rank == 1) { + if (!Runtime.PyInt_Check(idx)) + { + return RaiseIndexMustBeIntegerError(idx); + } index = Runtime.PyInt_AsLong(idx); if (Exceptions.ErrorOccurred()) @@ -102,6 +203,10 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) for (var i = 0; i < count; i++) { IntPtr op = Runtime.PyTuple_GetItem(idx, i); + if (!Runtime.PyInt_Check(op)) + { + return RaiseIndexMustBeIntegerError(op); + } index = Runtime.PyInt_AsLong(op); if (Exceptions.ErrorOccurred()) @@ -156,6 +261,11 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) if (rank == 1) { + if (!Runtime.PyInt_Check(idx)) + { + RaiseIndexMustBeIntegerError(idx); + return -1; + } index = Runtime.PyInt_AsLong(idx); if (Exceptions.ErrorOccurred()) @@ -194,6 +304,11 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) for (var i = 0; i < count; i++) { IntPtr op = Runtime.PyTuple_GetItem(idx, i); + if (!Runtime.PyInt_Check(op)) + { + RaiseIndexMustBeIntegerError(op); + return -1; + } index = Runtime.PyInt_AsLong(op); if (Exceptions.ErrorOccurred()) @@ -223,6 +338,11 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) return 0; } + private static IntPtr RaiseIndexMustBeIntegerError(IntPtr idx) + { + string tpName = Runtime.PyObject_GetTypeName(idx); + return Exceptions.RaiseTypeError($"array index has type {tpName}, expected an integer"); + } /// /// Implements __contains__ for array types. diff --git a/src/runtime/assemblymanager.cs b/src/runtime/assemblymanager.cs index ba6faa076..0387d2dfc 100644 --- a/src/runtime/assemblymanager.cs +++ b/src/runtime/assemblymanager.cs @@ -20,9 +20,9 @@ internal class AssemblyManager // therefore this should be a ConcurrentDictionary // // 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 - + // 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 = @@ -252,75 +252,6 @@ public static Assembly FindLoadedAssembly(string name) return null; } - /// - /// Given a qualified name of the form A.B.C.D, attempt to load - /// an assembly named after each of A.B.C.D, A.B.C, A.B, A. This - /// will only actually probe for the assembly once for each unique - /// namespace. Returns true if any assemblies were loaded. - /// - /// - /// TODO item 3 "* Deprecate implicit loading of assemblies": - /// Set the fromFile flag if the name of the loaded assembly matches - /// the fully qualified name that was requested if the framework - /// actually loads an assembly. - /// Call ONLY for namespaces that HAVE NOT been cached yet. - /// - public static bool LoadImplicit(string name, Action assemblyLoadErrorHandler, bool warn = true) - { - string[] names = name.Split('.'); - var loaded = false; - var s = ""; - Assembly lastAssembly = null; - HashSet assembliesSet = null; - for (var i = 0; i < names.Length; i++) - { - s = i == 0 ? names[0] : s + "." + names[i]; - if (!probed.ContainsKey(s)) - { - if (assembliesSet == null) - { - assembliesSet = new HashSet(AppDomain.CurrentDomain.GetAssemblies()); - } - Assembly a = FindLoadedAssembly(s); - try - { - if (a == null) - { - a = LoadAssemblyPath(s); - } - - if (a == null) - { - a = LoadAssembly(s); - } - } - catch (FileLoadException e) { assemblyLoadErrorHandler(e); } - catch (BadImageFormatException e) { assemblyLoadErrorHandler(e); } - catch (System.Security.SecurityException e) { assemblyLoadErrorHandler(e); } - catch (PathTooLongException e) { assemblyLoadErrorHandler(e); } - - if (a != null && !assembliesSet.Contains(a)) - { - loaded = true; - lastAssembly = a; - } - probed[s] = 1; - } - } - - // Deprecation warning - if (warn && loaded) - { - string location = Path.GetFileNameWithoutExtension(lastAssembly.Location); - string deprWarning = "The module was found, but not in a referenced namespace.\n" + - $"Implicit loading is deprecated. Please use clr.AddReference('{location}')."; - Exceptions.deprecation(deprWarning); - } - - return loaded; - } - - /// /// Scans an assembly for exported namespaces, adding them to the /// mapping of valid namespaces. Note that for a given namespace @@ -434,25 +365,6 @@ public static List GetNames(string nsname) return names; } - /// - /// Returns the System.Type object for a given qualified name, - /// looking in the currently loaded assemblies for the named - /// type. Returns null if the named type cannot be found. - /// - [Obsolete("Use LookupTypes and handle name conflicts")] - public static Type LookupType(string qname) - { - foreach (Assembly assembly in assemblies) - { - Type type = assembly.GetType(qname); - if (type != null && IsExported(type)) - { - return type; - } - } - return null; - } - /// /// Returns the objects for the given qualified name, /// looking in the currently loaded assemblies for the named diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs index 66153fbe1..a29e3a9b2 100644 --- a/src/runtime/classbase.cs +++ b/src/runtime/classbase.cs @@ -1,8 +1,7 @@ using System; using System.Collections; -using System.Diagnostics; +using System.Collections.Generic; using System.Runtime.InteropServices; -using System.Runtime.Serialization; namespace Python.Runtime { @@ -17,20 +16,33 @@ namespace Python.Runtime [Serializable] internal class ClassBase : ManagedType { + [NonSerialized] + internal List dotNetMembers; internal Indexer indexer; - internal Type type; + internal Dictionary richcompare; + internal MaybeType type; internal ClassBase(Type tp) { + dotNetMembers = new List(); indexer = null; type = tp; } internal virtual bool CanSubclass() { - return !type.IsEnum; + return !type.Value.IsEnum; } + public readonly static Dictionary CilToPyOpMap = new Dictionary + { + ["op_Equality"] = Runtime.Py_EQ, + ["op_Inequality"] = Runtime.Py_NE, + ["op_LessThanOrEqual"] = Runtime.Py_LE, + ["op_GreaterThanOrEqual"] = Runtime.Py_GE, + ["op_LessThan"] = Runtime.Py_LT, + ["op_GreaterThan"] = Runtime.Py_GT, + }; /// /// Default implementation of [] semantics for reflected types. @@ -43,17 +55,31 @@ public virtual IntPtr type_subscript(IntPtr idx) return Exceptions.RaiseTypeError("type(s) expected"); } - Type target = GenericUtil.GenericForType(type, types.Length); + if (!type.Valid) + { + return Exceptions.RaiseTypeError(type.DeletedMessage); + } + + Type target = GenericUtil.GenericForType(type.Value, types.Length); if (target != null) { - Type t = target.MakeGenericType(types); + Type t; + try + { + // MakeGenericType can throw ArgumentException + t = target.MakeGenericType(types); + } + catch (ArgumentException e) + { + return Exceptions.RaiseTypeError(e.Message); + } ManagedType c = ClassManager.GetClass(t); Runtime.XIncref(c.pyHandle); return c.pyHandle; } - return Exceptions.RaiseTypeError("no type matches params"); + return Exceptions.RaiseTypeError($"{type.Value.Namespace}.{type.Name} does not accept {types.Length} generic parameters"); } /// @@ -63,6 +89,30 @@ public static IntPtr tp_richcompare(IntPtr ob, IntPtr other, int op) { CLRObject co1; CLRObject co2; + IntPtr tp = Runtime.PyObject_TYPE(ob); + var cls = (ClassBase)GetManagedObject(tp); + // C# operator methods take precedence over IComparable. + // We first check if there's a comparison operator by looking up the richcompare table, + // otherwise fallback to checking if an IComparable interface is handled. + if (cls.richcompare.TryGetValue(op, out var methodObject)) + { + // Wrap the `other` argument of a binary comparison operator in a PyTuple. + IntPtr args = Runtime.PyTuple_New(1); + Runtime.XIncref(other); + Runtime.PyTuple_SetItem(args, 0, other); + + IntPtr value; + try + { + value = methodObject.Invoke(ob, args, IntPtr.Zero); + } + finally + { + Runtime.XDecref(args); // Free args pytuple + } + return value; + } + switch (op) { case Runtime.Py_EQ: @@ -184,7 +234,6 @@ public static IntPtr tp_iter(IntPtr ob) var e = co.inst as IEnumerable; IEnumerator o; - if (e != null) { o = e.GetEnumerator(); @@ -199,21 +248,36 @@ public static IntPtr tp_iter(IntPtr ob) } } - return new Iterator(o).pyHandle; + var elemType = typeof(object); + var iterType = co.inst.GetType(); + foreach(var ifc in iterType.GetInterfaces()) + { + if (ifc.IsGenericType) + { + var genTypeDef = ifc.GetGenericTypeDefinition(); + if (genTypeDef == typeof(IEnumerable<>) || genTypeDef == typeof(IEnumerator<>)) + { + elemType = ifc.GetGenericArguments()[0]; + break; + } + } + } + + return new Iterator(o, elemType).pyHandle; } /// /// Standard __hash__ implementation for instances of reflected types. /// - public static IntPtr tp_hash(IntPtr ob) + public static nint tp_hash(IntPtr ob) { var co = GetManagedObject(ob) as CLRObject; if (co == null) { return Exceptions.RaiseTypeError("unhashable type"); } - return new IntPtr(co.inst.GetHashCode()); + return co.inst.GetHashCode(); } @@ -264,7 +328,7 @@ public static IntPtr tp_repr(IntPtr ob) IntPtr args = Runtime.PyTuple_New(1); Runtime.XIncref(ob); Runtime.PyTuple_SetItem(args, 0, ob); - IntPtr reprFunc = Runtime.PyObject_GetAttrString(Runtime.PyBaseObjectType, "__repr__"); + IntPtr reprFunc = Runtime.PyObject_GetAttr(Runtime.PyBaseObjectType, PyIdentifier.__repr__); var output = Runtime.PyObject_Call(reprFunc, args, IntPtr.Zero); Runtime.XDecref(args); Runtime.XDecref(reprFunc); @@ -288,44 +352,39 @@ public static IntPtr tp_repr(IntPtr ob) public static void tp_dealloc(IntPtr ob) { ManagedType self = GetManagedObject(ob); - tp_clear(ob); - Runtime.PyObject_GC_UnTrack(self.pyHandle); - Runtime.PyObject_GC_Del(self.pyHandle); + if (Runtime.PyType_SUPPORTS_WEAKREFS(Runtime.PyObject_TYPE(ob))) + { + Runtime.PyObject_ClearWeakRefs(ob); + } + RemoveObjectDict(ob); + Runtime.Py_CLEAR(ref self.tpHandle); + Runtime.PyObject_GC_UnTrack(ob); + Runtime.PyObject_GC_Del(ob); self.FreeGCHandle(); } public static int tp_clear(IntPtr ob) { - ManagedType self = GetManagedObject(ob); - if (!self.IsTypeObject()) - { - ClearObjectDict(ob); - } - self.tpHandle = IntPtr.Zero; + ClearObjectDict(ob); return 0; } protected override void OnSave(InterDomainContext context) { base.OnSave(context); - if (pyHandle != tpHandle) - { - IntPtr dict = GetObjectDict(pyHandle); - Runtime.XIncref(dict); - context.Storage.AddValue("dict", dict); - } + IntPtr dict = GetObjectDict(pyHandle); + Runtime.XIncref(dict); + Runtime.XIncref(tpHandle); + context.Storage.AddValue("dict", dict); } protected override void OnLoad(InterDomainContext context) { base.OnLoad(context); - if (pyHandle != tpHandle) - { - IntPtr dict = context.Storage.GetValue("dict"); - SetObjectDict(pyHandle, dict); - } + IntPtr dict = context.Storage.GetValue("dict"); + SetObjectDict(pyHandle, dict); gcHandle = AllocGCHandle(); - Marshal.WriteIntPtr(pyHandle, TypeOffset.magic(), (IntPtr)gcHandle); + Marshal.WriteIntPtr(pyHandle, ObjectOffset.magic(tpHandle), (IntPtr)gcHandle); } diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs index e55e89240..4e8e88bf3 100644 --- a/src/runtime/classderived.cs +++ b/src/runtime/classderived.cs @@ -75,7 +75,8 @@ internal ClassDerivedObject(Type tp) : base(tp) // So we don't call PyObject_GC_Del here and instead we set the python // reference to a weak reference so that the C# object can be collected. GCHandle gc = GCHandle.Alloc(self, GCHandleType.Weak); - Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc); + int gcOffset = ObjectOffset.magic(Runtime.PyObject_TYPE(self.pyHandle)); + Marshal.WriteIntPtr(self.pyHandle, gcOffset, (IntPtr)gc); self.gcHandle.Free(); self.gcHandle = gc; } @@ -857,7 +858,7 @@ public static void Finalize(IPythonDerivedType obj) { if (0 == Runtime.Py_IsInitialized() || Runtime.IsFinalizing) { - self.gcHandle.Free(); + if (self.gcHandle.IsAllocated) self.gcHandle.Free(); return; } } @@ -872,7 +873,7 @@ public static void Finalize(IPythonDerivedType obj) // If python's been terminated then just free the gchandle. if (0 == Runtime.Py_IsInitialized() || Runtime.IsFinalizing) { - self.gcHandle.Free(); + if (self.gcHandle.IsAllocated) self.gcHandle.Free(); return; } diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs index 15f3d821d..5cad4bd69 100644 --- a/src/runtime/classmanager.cs +++ b/src/runtime/classmanager.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using System.Security; using System.Linq; +using System.Diagnostics; namespace Python.Runtime { @@ -18,7 +19,20 @@ namespace Python.Runtime /// internal class ClassManager { - private static Dictionary cache; + + // Binding flags to determine which members to expose in Python. + // This is complicated because inheritance in Python is name + // based. We can't just find DeclaredOnly members, because we + // could have a base class A that defines two overloads of a + // method and a class B that defines two more. The name-based + // descriptor Python will find needs to know about inherited + // overloads as well as those declared on the sub class. + internal static readonly BindingFlags BindingFlags = BindingFlags.Static | + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic; + + private static Dictionary cache; private static readonly Type dtype; private ClassManager() @@ -36,7 +50,7 @@ static ClassManager() public static void Reset() { - cache = new Dictionary(128); + cache = new Dictionary(128); } internal static void DisposePythonWrappersForClrTypes() @@ -85,27 +99,75 @@ internal static void SaveRuntimeData(RuntimeDataStorage storage) var contexts = storage.AddValue("contexts", new Dictionary()); storage.AddValue("cache", cache); - foreach (var cls in cache.Values) + foreach (var cls in cache) { + if (!cls.Key.Valid) + { + // Don't serialize an invalid class + continue; + } // This incref is for cache to hold the cls, // thus no need for decreasing it at RestoreRuntimeData. - Runtime.XIncref(cls.pyHandle); - var context = contexts[cls.pyHandle] = new InterDomainContext(); - cls.Save(context); + Runtime.XIncref(cls.Value.pyHandle); + var context = contexts[cls.Value.pyHandle] = new InterDomainContext(); + cls.Value.Save(context); + + // Remove all members added in InitBaseClass. + // this is done so that if domain reloads and a member of a + // reflected dotnet class is removed, it is removed from the + // Python object's dictionary tool; thus raising an AttributeError + // instead of a TypeError. + // Classes are re-initialized on in RestoreRuntimeData. + var dict = new BorrowedReference(Marshal.ReadIntPtr(cls.Value.tpHandle, TypeOffset.tp_dict)); + foreach (var member in cls.Value.dotNetMembers) + { + // No need to decref the member, the ClassBase instance does + // not own the reference. + if ((Runtime.PyDict_DelItemString(dict, member) == -1) && + (Exceptions.ExceptionMatches(Exceptions.KeyError))) + { + // Trying to remove a key that's not in the dictionary + // raises an error. We don't care about it. + Runtime.PyErr_Clear(); + } + else if (Exceptions.ErrorOccurred()) + { + throw new PythonException(); + } + } + // We modified the Type object, notify it we did. + Runtime.PyType_Modified(cls.Value.tpHandle); } } internal static Dictionary RestoreRuntimeData(RuntimeDataStorage storage) { - cache = storage.GetValue>("cache"); + cache = storage.GetValue>("cache"); + var invalidClasses = new List>(); var contexts = storage.GetValue >("contexts"); var loadedObjs = new Dictionary(); - foreach (var cls in cache.Values) + foreach (var pair in cache) + { + if (!pair.Key.Valid) + { + invalidClasses.Add(pair); + continue; + } + // re-init the class + InitClassBase(pair.Key.Value, pair.Value); + // We modified the Type object, notify it we did. + Runtime.PyType_Modified(pair.Value.tpHandle); + var context = contexts[pair.Value.pyHandle]; + pair.Value.Load(context); + loadedObjs.Add(pair.Value, context); + } + + foreach (var pair in invalidClasses) { - var context = contexts[cls.pyHandle]; - cls.Load(context); - loadedObjs.Add(cls, context); + cache.Remove(pair.Key); + Runtime.XDecref(pair.Value.pyHandle); } + return loadedObjs; } @@ -113,6 +175,7 @@ internal static Dictionary RestoreRuntimeData(R /// Return the ClassBase-derived instance that implements a particular /// reflected managed type, creating it if it doesn't yet exist. /// + /// A Borrowed reference to the ClassBase object internal static ClassBase GetClass(Type type) { ClassBase cb = null; @@ -197,6 +260,7 @@ private static void InitClassBase(Type type, ClassBase impl) ClassInfo info = GetClassInfo(type); impl.indexer = info.indexer; + impl.richcompare = new Dictionary(); // Now we allocate the Python type object to reflect the given // managed type, filling the Python type slots with thunks that @@ -206,34 +270,37 @@ private static void InitClassBase(Type type, ClassBase impl) IntPtr tp = TypeManager.GetTypeHandle(impl, type); // Finally, initialize the class __dict__ and return the object. - IntPtr dict = Marshal.ReadIntPtr(tp, TypeOffset.tp_dict); - + var dict = new BorrowedReference(Marshal.ReadIntPtr(tp, TypeOffset.tp_dict)); + Debug.Assert(!dict.IsNull); + if (impl.dotNetMembers == null) + { + impl.dotNetMembers = new List(); + } IDictionaryEnumerator iter = info.members.GetEnumerator(); while (iter.MoveNext()) { var item = (ManagedType)iter.Value; var name = (string)iter.Key; - Runtime.PyDict_SetItemString(dict, name, item.pyHandle); + impl.dotNetMembers.Add(name); + Runtime.PyDict_SetItemString(dict, name, item.ObjectReference); // Decref the item now that it's been used. item.DecrRefCount(); + if (ClassBase.CilToPyOpMap.TryGetValue(name, out var pyOp)) { + impl.richcompare.Add(pyOp, (MethodObject)item); + } } // If class has constructors, generate an __doc__ attribute. - IntPtr doc = IntPtr.Zero; + NewReference doc = default; Type marker = typeof(DocStringAttribute); var attrs = (Attribute[])type.GetCustomAttributes(marker, false); - if (attrs.Length == 0) - { - doc = IntPtr.Zero; - } - else + if (attrs.Length != 0) { var attr = (DocStringAttribute)attrs[0]; string docStr = attr.DocString; - doc = Runtime.PyString_FromString(docStr); - Runtime.PyDict_SetItemString(dict, "__doc__", doc); - Runtime.XDecref(doc); + doc = NewReference.DangerousFromPointer(Runtime.PyString_FromString(docStr)); + Runtime.PyDict_SetItem(dict, PyIdentifier.__doc__, doc); } var co = impl as ClassObject; @@ -241,7 +308,7 @@ private static void InitClassBase(Type type, ClassBase impl) // required that the ClassObject.ctors be changed to internal if (co != null) { - if (co.ctors.Length > 0) + if (co.NumCtors > 0) { // Implement Overloads on the class object if (!CLRModule._SuppressOverloads) @@ -249,20 +316,65 @@ private static void InitClassBase(Type type, ClassBase impl) var ctors = new ConstructorBinding(type, tp, co.binder); // ExtensionType types are untracked, so don't Incref() them. // TODO: deprecate __overloads__ soon... - Runtime.PyDict_SetItemString(dict, "__overloads__", ctors.pyHandle); - Runtime.PyDict_SetItemString(dict, "Overloads", ctors.pyHandle); + Runtime.PyDict_SetItem(dict, PyIdentifier.__overloads__, ctors.ObjectReference); + Runtime.PyDict_SetItem(dict, PyIdentifier.Overloads, ctors.ObjectReference); ctors.DecrRefCount(); } // don't generate the docstring if one was already set from a DocStringAttribute. - if (!CLRModule._SuppressDocs && doc == IntPtr.Zero) + if (!CLRModule._SuppressDocs && doc.IsNull()) { doc = co.GetDocString(); - Runtime.PyDict_SetItemString(dict, "__doc__", doc); - Runtime.XDecref(doc); + Runtime.PyDict_SetItem(dict, PyIdentifier.__doc__, doc); } } } + doc.Dispose(); + + // The type has been modified after PyType_Ready has been called + // Refresh the type + Runtime.PyType_Modified(tp); + } + + internal static bool ShouldBindMethod(MethodBase mb) + { + return (mb.IsPublic || mb.IsFamily || mb.IsFamilyOrAssembly); + } + + internal static bool ShouldBindField(FieldInfo fi) + { + return (fi.IsPublic || fi.IsFamily || fi.IsFamilyOrAssembly); + } + + internal static bool ShouldBindProperty(PropertyInfo pi) + { + MethodInfo mm = null; + try + { + mm = pi.GetGetMethod(true); + if (mm == null) + { + mm = pi.GetSetMethod(true); + } + } + catch (SecurityException) + { + // GetGetMethod may try to get a method protected by + // StrongNameIdentityPermission - effectively private. + return false; + } + + if (mm == null) + { + return false; + } + + return ShouldBindMethod(mm); + } + + internal static bool ShouldBindEvent(EventInfo ei) + { + return ShouldBindMethod(ei.GetAddMethod(true)); } private static ClassInfo GetClassInfo(Type type) @@ -277,18 +389,7 @@ private static ClassInfo GetClassInfo(Type type) Type tp; int i, n; - // This is complicated because inheritance in Python is name - // based. We can't just find DeclaredOnly members, because we - // could have a base class A that defines two overloads of a - // method and a class B that defines two more. The name-based - // descriptor Python will find needs to know about inherited - // overloads as well as those declared on the sub class. - BindingFlags flags = BindingFlags.Static | - BindingFlags.Instance | - BindingFlags.Public | - BindingFlags.NonPublic; - - MemberInfo[] info = type.GetMembers(flags); + MemberInfo[] info = type.GetMembers(BindingFlags); var local = new Hashtable(); var items = new ArrayList(); MemberInfo m; @@ -331,7 +432,7 @@ private static ClassInfo GetClassInfo(Type type) for (i = 0; i < inheritedInterfaces.Length; ++i) { Type inheritedType = inheritedInterfaces[i]; - MemberInfo[] imembers = inheritedType.GetMembers(flags); + MemberInfo[] imembers = inheritedType.GetMembers(BindingFlags); for (n = 0; n < imembers.Length; n++) { m = imembers[n]; @@ -341,6 +442,17 @@ private static ClassInfo GetClassInfo(Type type) } } } + + // All interface implementations inherit from Object, + // but GetMembers don't return them either. + var objFlags = BindingFlags.Public | BindingFlags.Instance; + foreach (var mi in typeof(object).GetMembers(objFlags)) + { + if (local[mi.Name] == null) + { + items.Add(mi); + } + } } for (i = 0; i < items.Count; i++) @@ -351,8 +463,7 @@ private static ClassInfo GetClassInfo(Type type) { case MemberTypes.Method: meth = (MethodInfo)mi; - if (!(meth.IsPublic || meth.IsFamily || - meth.IsFamilyOrAssembly)) + if (!ShouldBindMethod(meth)) { continue; } @@ -369,28 +480,7 @@ private static ClassInfo GetClassInfo(Type type) case MemberTypes.Property: var pi = (PropertyInfo)mi; - MethodInfo mm = null; - try - { - mm = pi.GetGetMethod(true); - if (mm == null) - { - mm = pi.GetSetMethod(true); - } - } - catch (SecurityException) - { - // GetGetMethod may try to get a method protected by - // StrongNameIdentityPermission - effectively private. - continue; - } - - if (mm == null) - { - continue; - } - - if (!(mm.IsPublic || mm.IsFamily || mm.IsFamilyOrAssembly)) + if(!ShouldBindProperty(pi)) { continue; } @@ -415,7 +505,7 @@ private static ClassInfo GetClassInfo(Type type) case MemberTypes.Field: var fi = (FieldInfo)mi; - if (!(fi.IsPublic || fi.IsFamily || fi.IsFamilyOrAssembly)) + if (!ShouldBindField(fi)) { continue; } @@ -425,8 +515,7 @@ private static ClassInfo GetClassInfo(Type type) case MemberTypes.Event: var ei = (EventInfo)mi; - MethodInfo me = ei.GetAddMethod(true); - if (!(me.IsPublic || me.IsFamily || me.IsFamilyOrAssembly)) + if (!ShouldBindEvent(ei)) { continue; } @@ -443,6 +532,8 @@ private static ClassInfo GetClassInfo(Type type) } // Note the given instance might be uninitialized ob = GetClass(tp); + // GetClass returns a Borrowed ref. ci.members owns the reference. + ob.IncrRefCount(); ci.members[mi.Name] = ob; continue; } @@ -459,6 +550,18 @@ private static ClassInfo GetClassInfo(Type type) ob = new MethodObject(type, name, mlist); ci.members[name] = ob; + if (mlist.Any(OperatorMethod.IsOperatorMethod)) + { + string pyName = OperatorMethod.GetPyMethodName(name); + string pyNameReverse = OperatorMethod.ReversePyMethodName(pyName); + OperatorMethod.FilterMethods(mlist, out var forwardMethods, out var reverseMethods); + // Only methods where the left operand is the declaring type. + if (forwardMethods.Length > 0) + ci.members[pyName] = new MethodObject(type, name, forwardMethods); + // Only methods where only the right operand is the declaring type. + if (reverseMethods.Length > 0) + ci.members[pyNameReverse] = new MethodObject(type, name, reverseMethods); + } } if (ci.indexer == null && type.IsClass) diff --git a/src/runtime/classobject.cs b/src/runtime/classobject.cs index 18816781f..4aa97f648 100644 --- a/src/runtime/classobject.cs +++ b/src/runtime/classobject.cs @@ -1,3 +1,4 @@ +using System.Linq; using System; using System.Reflection; @@ -13,14 +14,14 @@ namespace Python.Runtime internal class ClassObject : ClassBase { internal ConstructorBinder binder; - internal ConstructorInfo[] ctors; + internal int NumCtors = 0; internal ClassObject(Type tp) : base(tp) { - ctors = type.GetConstructors(); - binder = new ConstructorBinder(type); - - foreach (ConstructorInfo t in ctors) + var _ctors = type.Value.GetConstructors(); + NumCtors = _ctors.Length; + binder = new ConstructorBinder(type.Value); + foreach (ConstructorInfo t in _ctors) { binder.AddMethod(t); } @@ -30,7 +31,7 @@ internal ClassObject(Type tp) : base(tp) /// /// Helper to get docstring from reflected constructor info. /// - internal IntPtr GetDocString() + internal NewReference GetDocString() { MethodBase[] methods = binder.GetMethods(); var str = ""; @@ -42,7 +43,7 @@ internal IntPtr GetDocString() } str += t.ToString(); } - return Runtime.PyString_FromString(str); + return NewReference.DangerousFromPointer(Runtime.PyString_FromString(str)); } @@ -61,7 +62,11 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) return Exceptions.RaiseTypeError("invalid object"); } - Type type = self.type; + if (!self.type.Valid) + { + return Exceptions.RaiseTypeError(self.type.DeletedMessage); + } + Type type = self.type.Value; // Primitive types do not have constructors, but they look like // they do from Python. If the ClassObject represents one of the @@ -114,16 +119,21 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) /// public override IntPtr type_subscript(IntPtr idx) { + if (!type.Valid) + { + return Exceptions.RaiseTypeError(type.DeletedMessage); + } + // If this type is the Array type, the [] means we need to // construct and return an array type of the given element type. - if (type == typeof(Array)) + if (type.Value == typeof(Array)) { if (Runtime.PyTuple_Check(idx)) { return Exceptions.RaiseTypeError("type expected"); } var c = GetManagedObject(idx) as ClassBase; - Type t = c != null ? c.type : Converter.GetTypeByAlias(idx); + Type t = c != null ? c.type.Value : Converter.GetTypeByAlias(idx); if (t == null) { return Exceptions.RaiseTypeError("type expected"); @@ -143,7 +153,7 @@ public override IntPtr type_subscript(IntPtr idx) return Exceptions.RaiseTypeError("type(s) expected"); } - Type gtype = AssemblyManager.LookupType($"{type.FullName}`{types.Length}"); + Type gtype = AssemblyManager.LookupTypes($"{type.Value.FullName}`{types.Length}").FirstOrDefault(); if (gtype != null) { var g = ClassManager.GetClass(gtype) as GenericType; diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs index 0b62fecba..9c288b145 100644 --- a/src/runtime/clrobject.cs +++ b/src/runtime/clrobject.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; namespace Python.Runtime @@ -29,10 +30,6 @@ internal CLRObject(object ob, IntPtr tp) tpHandle = tp; pyHandle = py; inst = ob; - - // Fix the BaseException args (and __cause__ in case of Python 3) - // slot if wrapping a CLR exception - Exceptions.SetArgsAndCause(py); } protected CLRObject() @@ -48,10 +45,14 @@ static CLRObject GetInstance(object ob, IntPtr pyType) static CLRObject GetInstance(object ob) { ClassBase cc = ClassManager.GetClass(ob.GetType()); - return GetInstance(ob, cc.tpHandle); + return GetInstance(ob, cc.pyHandle); } - + internal static NewReference GetInstHandle(object ob, BorrowedReference pyType) + { + CLRObject co = GetInstance(ob, pyType.DangerousGetAddress()); + return NewReference.DangerousFromPointer(co.pyHandle); + } internal static IntPtr GetInstHandle(object ob, IntPtr pyType) { CLRObject co = GetInstance(ob, pyType); @@ -62,7 +63,7 @@ internal static IntPtr GetInstHandle(object ob, IntPtr pyType) internal static IntPtr GetInstHandle(object ob, Type type) { ClassBase cc = ClassManager.GetClass(type); - CLRObject co = GetInstance(ob, cc.tpHandle); + CLRObject co = GetInstance(ob, cc.pyHandle); return co.pyHandle; } @@ -81,6 +82,7 @@ internal static CLRObject Restore(object ob, IntPtr pyHandle, InterDomainContext pyHandle = pyHandle, tpHandle = Runtime.PyObject_TYPE(pyHandle) }; + Debug.Assert(co.tpHandle != IntPtr.Zero); co.Load(context); return co; } diff --git a/src/runtime/constructorbinder.cs b/src/runtime/constructorbinder.cs index 0cda3a3d9..83f2c81e4 100644 --- a/src/runtime/constructorbinder.cs +++ b/src/runtime/constructorbinder.cs @@ -14,7 +14,7 @@ namespace Python.Runtime [Serializable] internal class ConstructorBinder : MethodBinder { - private Type _containingType; + private MaybeType _containingType; internal ConstructorBinder(Type containingType) { @@ -51,10 +51,15 @@ internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw) /// internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info) { + if (!_containingType.Valid) + { + return Exceptions.RaiseTypeError(_containingType.DeletedMessage); + } object result; + Type tp = _containingType.Value; - if (_containingType.IsValueType && !_containingType.IsPrimitive && - !_containingType.IsEnum && _containingType != typeof(decimal) && + if (tp.IsValueType && !tp.IsPrimitive && + !tp.IsEnum && tp != typeof(decimal) && Runtime.PyTuple_Size(args) == 0) { // If you are trying to construct an instance of a struct by @@ -64,7 +69,7 @@ internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info) // Activator.CreateInstance(). try { - result = Activator.CreateInstance(_containingType); + result = Activator.CreateInstance(tp); } catch (Exception e) { diff --git a/src/runtime/constructorbinding.cs b/src/runtime/constructorbinding.cs index 0c81c0a93..803823e39 100644 --- a/src/runtime/constructorbinding.cs +++ b/src/runtime/constructorbinding.cs @@ -22,7 +22,7 @@ namespace Python.Runtime [Serializable] internal class ConstructorBinding : ExtensionType { - private Type type; // The managed Type being wrapped in a ClassObject + private MaybeType type; // The managed Type being wrapped in a ClassObject private IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create. private ConstructorBinder ctorBinder; @@ -92,6 +92,11 @@ public static IntPtr tp_descr_get(IntPtr op, IntPtr instance, IntPtr owner) public static IntPtr mp_subscript(IntPtr op, IntPtr key) { var self = (ConstructorBinding)GetManagedObject(op); + if (!self.type.Valid) + { + return Exceptions.RaiseTypeError(self.type.DeletedMessage); + } + Type tp = self.type.Value; Type[] types = Runtime.PythonArgsToTypeArray(key); if (types == null) @@ -100,12 +105,12 @@ public static IntPtr mp_subscript(IntPtr op, IntPtr key) } //MethodBase[] methBaseArray = self.ctorBinder.GetMethods(); //MethodBase ci = MatchSignature(methBaseArray, types); - ConstructorInfo ci = self.type.GetConstructor(types); + ConstructorInfo ci = tp.GetConstructor(types); if (ci == null) { return Exceptions.RaiseTypeError("No match found for constructor signature"); } - var boundCtor = new BoundContructor(self.type, self.pyTypeHndl, self.ctorBinder, ci); + var boundCtor = new BoundContructor(tp, self.pyTypeHndl, self.ctorBinder, ci); return boundCtor.pyHandle; } @@ -122,7 +127,12 @@ public static IntPtr tp_repr(IntPtr ob) return self.repr; } MethodBase[] methods = self.ctorBinder.GetMethods(); - string name = self.type.FullName; + + if (!self.type.Valid) + { + return Exceptions.RaiseTypeError(self.type.DeletedMessage); + } + string name = self.type.Value.FullName; var doc = ""; foreach (MethodBase t in methods) { diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 98fe99141..f3b378113 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -241,9 +241,9 @@ internal static IntPtr ToPython(object value, Type type) // return Runtime.PyFloat_FromDouble((double)((float)value)); string ss = ((float)value).ToString(nfi); IntPtr ps = Runtime.PyString_FromString(ss); - IntPtr op = Runtime.PyFloat_FromString(ps, IntPtr.Zero); + NewReference op = Runtime.PyFloat_FromString(new BorrowedReference(ps));; Runtime.XDecref(ps); - return op; + return op.DangerousMoveToPointerOrNull(); case TypeCode.Double: return Runtime.PyFloat_FromDouble((double)value); @@ -303,6 +303,11 @@ internal static IntPtr ToPythonImplicit(object value) /// Return a managed object for the given Python object, taking funny /// byref types into account. /// + /// A Python object + /// The desired managed type + /// Receives the managed object + /// If true, call Exceptions.SetError with the reason for failure. + /// True on success internal static bool ToManaged(IntPtr value, Type type, out object result, bool setError) { @@ -313,7 +318,9 @@ internal static bool ToManaged(IntPtr value, Type type, return Converter.ToManagedValue(value, type, out result, setError); } - + internal static bool ToManagedValue(BorrowedReference value, Type obType, + out object result, bool setError) + => ToManagedValue(value.DangerousGetAddress(), obType, out result, setError); internal static bool ToManagedValue(IntPtr value, Type obType, out object result, bool setError) { @@ -331,20 +338,29 @@ internal static bool ToManagedValue(IntPtr value, Type obType, if (mt != null) { - if (mt is CLRObject) + if (mt is CLRObject co) { - object tmp = ((CLRObject)mt).inst; + object tmp = co.inst; if (obType.IsInstanceOfType(tmp)) { result = tmp; return true; } - Exceptions.SetError(Exceptions.TypeError, $"value cannot be converted to {obType}"); + if (setError) + { + string typeString = tmp is null ? "null" : tmp.GetType().ToString(); + Exceptions.SetError(Exceptions.TypeError, $"{typeString} value cannot be converted to {obType}"); + } return false; } - if (mt is ClassBase) + if (mt is ClassBase cb) { - result = ((ClassBase)mt).type; + if (!cb.type.Valid) + { + Exceptions.SetError(Exceptions.TypeError, cb.type.DeletedMessage); + return false; + } + result = cb.type.Value; return true; } // shouldn't happen @@ -368,6 +384,15 @@ internal static bool ToManagedValue(IntPtr value, Type obType, obType = obType.GetGenericArguments()[0]; } + if (obType.ContainsGenericParameters) + { + if (setError) + { + Exceptions.SetError(Exceptions.TypeError, $"Cannot create an instance of the open generic type {obType}"); + } + return false; + } + if (obType.IsArray) { return ToArray(value, obType, out result, setError); @@ -509,7 +534,7 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo case TypeCode.Int32: { // Python3 always use PyLong API - long num = Runtime.PyLong_AsLongLong(value); + nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; @@ -539,7 +564,7 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo goto type_error; } - int num = Runtime.PyLong_AsLong(value); + nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; @@ -565,7 +590,7 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo goto type_error; } - int num = Runtime.PyLong_AsLong(value); + nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; @@ -602,7 +627,7 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo } goto type_error; } - int num = Runtime.PyLong_AsLong(value); + nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; @@ -617,7 +642,7 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo case TypeCode.Int16: { - int num = Runtime.PyLong_AsLong(value); + nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; @@ -632,18 +657,35 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo case TypeCode.Int64: { - long num = (long)Runtime.PyLong_AsLongLong(value); - if (num == -1 && Exceptions.ErrorOccurred()) + if (Runtime.Is32Bit) { - goto convert_error; + if (!Runtime.PyLong_Check(value)) + { + goto type_error; + } + long num = Runtime.PyExplicitlyConvertToInt64(value); + if (num == -1 && Exceptions.ErrorOccurred()) + { + goto convert_error; + } + result = num; + return true; + } + else + { + nint num = Runtime.PyLong_AsSignedSize_t(value); + if (num == -1 && Exceptions.ErrorOccurred()) + { + goto convert_error; + } + result = (long)num; + return true; } - result = num; - return true; } case TypeCode.UInt16: { - long num = Runtime.PyLong_AsLong(value); + nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; @@ -658,61 +700,25 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo case TypeCode.UInt32: { - op = value; - if (Runtime.PyObject_TYPE(value) != Runtime.PyLongType) + nuint num = Runtime.PyLong_AsUnsignedSize_t(value); + if (num == unchecked((nuint)(-1)) && Exceptions.ErrorOccurred()) { - op = Runtime.PyNumber_Long(value); - if (op == IntPtr.Zero) - { - goto convert_error; - } - } - if (Runtime.Is32Bit || Runtime.IsWindows) - { - uint num = Runtime.PyLong_AsUnsignedLong32(op); - if (num == uint.MaxValue && Exceptions.ErrorOccurred()) - { - goto convert_error; - } - result = num; + goto convert_error; } - else + if (num > UInt32.MaxValue) { - ulong num = Runtime.PyLong_AsUnsignedLong64(op); - if (num == ulong.MaxValue && Exceptions.ErrorOccurred()) - { - goto convert_error; - } - try - { - result = Convert.ToUInt32(num); - } - catch (OverflowException) - { - // Probably wasn't an overflow in python but was in C# (e.g. if cpython - // longs are 64 bit then 0xFFFFFFFF + 1 will not overflow in - // PyLong_AsUnsignedLong) - goto overflow; - } + goto overflow; } + result = (uint)num; return true; } case TypeCode.UInt64: { - op = value; - if (Runtime.PyObject_TYPE(value) != Runtime.PyLongType) - { - op = Runtime.PyNumber_Long(value); - if (op == IntPtr.Zero) - { - goto convert_error; - } - } - ulong num = Runtime.PyLong_AsUnsignedLongLong(op); + ulong num = Runtime.PyLong_AsUnsignedLongLong(value); if (num == ulong.MaxValue && Exceptions.ErrorOccurred()) { - goto overflow; + goto convert_error; } result = num; return true; @@ -788,7 +794,7 @@ private static void SetConversionError(IntPtr value, Type target) IntPtr ob = Runtime.PyObject_Repr(value); string src = Runtime.GetManagedString(ob); Runtime.XDecref(ob); - Exceptions.SetError(Exceptions.TypeError, $"Cannot convert {src} to {target}"); + Exceptions.RaiseTypeError($"Cannot convert {src} to {target}"); } @@ -802,32 +808,58 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s Type elementType = obType.GetElementType(); result = null; - bool IsSeqObj = Runtime.PySequence_Check(value); - var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1; - IntPtr IterObject = Runtime.PyObject_GetIter(value); - - if(IterObject==IntPtr.Zero) { + if (IterObject == IntPtr.Zero) + { if (setError) { SetConversionError(value, obType); } + else + { + // PyObject_GetIter will have set an error + Exceptions.Clear(); + } return false; } - Array items; + IList list; + try + { + // MakeGenericType can throw because elementType may not be a valid generic argument even though elementType[] is a valid array type. + // For example, if elementType is a pointer type. + // See https://docs.microsoft.com/en-us/dotnet/api/system.type.makegenerictype#System_Type_MakeGenericType_System_Type + var constructedListType = typeof(List<>).MakeGenericType(elementType); + bool IsSeqObj = Runtime.PySequence_Check(value); + if (IsSeqObj) + { + var len = Runtime.PySequence_Size(value); + list = (IList)Activator.CreateInstance(constructedListType, new Object[] { (int)len }); + } + else + { + // CreateInstance can throw even if MakeGenericType succeeded. + // See https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance#System_Activator_CreateInstance_System_Type_ + list = (IList)Activator.CreateInstance(constructedListType); + } + } + catch (Exception e) + { + if (setError) + { + Exceptions.SetError(e); + SetConversionError(value, obType); + } + return false; + } - var listType = typeof(List<>); - var constructedListType = listType.MakeGenericType(elementType); - IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] {(int) len}) : - (IList) Activator.CreateInstance(constructedListType); IntPtr item; while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) { - object obj = null; + object obj; - if (!Converter.ToManaged(item, elementType, out obj, true)) + if (!Converter.ToManaged(item, elementType, out obj, setError)) { Runtime.XDecref(item); return false; @@ -838,7 +870,7 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s } Runtime.XDecref(IterObject); - items = Array.CreateInstance(elementType, list.Count); + Array items = Array.CreateInstance(elementType, list.Count); list.CopyTo(items, 0); result = items; diff --git a/src/runtime/converterextensions.cs b/src/runtime/converterextensions.cs index 667fc6f00..b10d0c59f 100644 --- a/src/runtime/converterextensions.cs +++ b/src/runtime/converterextensions.cs @@ -10,7 +10,6 @@ namespace Python.Runtime /// /// Defines conversion to CLR types (unmarshalling) /// - [Obsolete(Util.UnstableApiMessage)] public interface IPyObjectDecoder { /// @@ -30,7 +29,6 @@ public interface IPyObjectDecoder /// /// Defines conversion from CLR objects into Python objects (e.g. ) (marshalling) /// - [Obsolete(Util.UnstableApiMessage)] public interface IPyObjectEncoder { /// @@ -47,7 +45,6 @@ public interface IPyObjectEncoder /// This class allows to register additional marshalling codecs. /// Python.NET will pick suitable encoder/decoder registered first /// - [Obsolete(Util.UnstableApiMessage)] public static class PyObjectConversions { static readonly DecoderGroup decoders = new DecoderGroup(); diff --git a/src/runtime/debughelper.cs b/src/runtime/debughelper.cs index 3fe9ee5bb..5e854bffd 100644 --- a/src/runtime/debughelper.cs +++ b/src/runtime/debughelper.cs @@ -60,13 +60,13 @@ internal static void DumpType(IntPtr type) //DebugUtil.Print(" mro: ", op); - FieldInfo[] slots = typeof(TypeOffset).GetFields(); + var slots = TypeOffset.GetOffsets(); int size = IntPtr.Size; - for (var i = 0; i < slots.Length; i++) + foreach (var entry in slots) { - int offset = i * size; - name = slots[i].Name; + int offset = entry.Value; + name = entry.Key; op = Marshal.ReadIntPtr(type, offset); Console.WriteLine(" {0}: {1}", name, op); } @@ -137,5 +137,12 @@ public static void PrintHexBytes(byte[] bytes) Console.WriteLine(); } } + + [Conditional("DEBUG")] + public static void AssertHasReferences(IntPtr obj) + { + long refcount = Runtime.Refcount(obj); + Debug.Assert(refcount > 0, "Object refcount is 0 or less"); + } } } diff --git a/src/runtime/delegatemanager.cs b/src/runtime/delegatemanager.cs index bd8f1ee4c..0a848904a 100644 --- a/src/runtime/delegatemanager.cs +++ b/src/runtime/delegatemanager.cs @@ -1,7 +1,9 @@ using System; -using System.Collections; +using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Reflection.Emit; +using System.Text; namespace Python.Runtime { @@ -11,23 +13,20 @@ namespace Python.Runtime /// internal class DelegateManager { - private Hashtable cache; - private Type basetype; - private Type listtype; - private Type voidtype; - private Type typetype; - private Type ptrtype; - private CodeGenerator codeGenerator; + private readonly Dictionary cache = new Dictionary(); + private readonly Type basetype = typeof(Dispatcher); + private readonly Type arrayType = typeof(object[]); + private readonly Type voidtype = typeof(void); + private readonly Type typetype = typeof(Type); + private readonly Type ptrtype = typeof(IntPtr); + private readonly CodeGenerator codeGenerator = new CodeGenerator(); + private readonly ConstructorInfo arrayCtor; + private readonly MethodInfo dispatch; public DelegateManager() { - basetype = typeof(Dispatcher); - listtype = typeof(ArrayList); - voidtype = typeof(void); - typetype = typeof(Type); - ptrtype = typeof(IntPtr); - cache = new Hashtable(); - codeGenerator = new CodeGenerator(); + arrayCtor = arrayType.GetConstructor(new[] { typeof(int) }); + dispatch = basetype.GetMethod("Dispatch"); } /// @@ -58,10 +57,9 @@ private Type GetDispatcher(Type dtype) // unique signatures rather than delegate types, since multiple // delegate types with the same sig could use the same dispatcher. - object item = cache[dtype]; - if (item != null) + if (cache.TryGetValue(dtype, out Type item)) { - return (Type)item; + return item; } string name = $"__{dtype.FullName}Dispatcher"; @@ -103,34 +101,77 @@ private Type GetDispatcher(Type dtype) MethodBuilder mb = tb.DefineMethod("Invoke", MethodAttributes.Public, method.ReturnType, signature); - ConstructorInfo ctor = listtype.GetConstructor(Type.EmptyTypes); - MethodInfo dispatch = basetype.GetMethod("Dispatch"); - MethodInfo add = listtype.GetMethod("Add"); - il = mb.GetILGenerator(); - il.DeclareLocal(listtype); - il.Emit(OpCodes.Newobj, ctor); + // loc_0 = new object[pi.Length] + il.DeclareLocal(arrayType); + il.Emit(OpCodes.Ldc_I4, pi.Length); + il.Emit(OpCodes.Newobj, arrayCtor); il.Emit(OpCodes.Stloc_0); + bool anyByRef = false; + for (var c = 0; c < signature.Length; c++) { Type t = signature[c]; il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldc_I4, c); il.Emit(OpCodes.Ldarg_S, (byte)(c + 1)); + if (t.IsByRef) + { + // The argument is a pointer. We must dereference the pointer to get the value or object it points to. + t = t.GetElementType(); + if (t.IsValueType) + { + il.Emit(OpCodes.Ldobj, t); + } + else + { + il.Emit(OpCodes.Ldind_Ref); + } + anyByRef = true; + } + if (t.IsValueType) { il.Emit(OpCodes.Box, t); } - il.Emit(OpCodes.Callvirt, add); - il.Emit(OpCodes.Pop); + // args[c] = arg + il.Emit(OpCodes.Stelem_Ref); } il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Call, dispatch); + if (anyByRef) + { + // Dispatch() will have modified elements of the args list that correspond to out parameters. + for (var c = 0; c < signature.Length; c++) + { + Type t = signature[c]; + if (t.IsByRef) + { + t = t.GetElementType(); + // *arg = args[c] + il.Emit(OpCodes.Ldarg_S, (byte)(c + 1)); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldc_I4, c); + il.Emit(OpCodes.Ldelem_Ref); + if (t.IsValueType) + { + il.Emit(OpCodes.Unbox_Any, t); + il.Emit(OpCodes.Stobj, t); + } + else + { + il.Emit(OpCodes.Stind_Ref); + } + } + } + } + if (method.ReturnType == voidtype) { il.Emit(OpCodes.Pop); @@ -181,7 +222,7 @@ A possible alternate strategy would be to create custom subclasses too "special" for this to work. It would be more work, so for now the 80/20 rule applies :) */ - public class Dispatcher : IPyDisposable + public class Dispatcher { public IntPtr target; public Type dtype; @@ -202,7 +243,7 @@ public Dispatcher(IntPtr target, Type dtype) return; } _finalized = true; - Finalizer.Instance.AddFinalizedObject(this); + Finalizer.Instance.AddFinalizedObject(ref target); } public void Dispose() @@ -218,26 +259,24 @@ public void Dispose() GC.SuppressFinalize(this); } - public object Dispatch(ArrayList args) + public object Dispatch(object[] args) { IntPtr gs = PythonEngine.AcquireLock(); - object ob = null; + object ob; try { ob = TrueDispatch(args); } - catch (Exception e) + finally { PythonEngine.ReleaseLock(gs); - throw e; } - PythonEngine.ReleaseLock(gs); return ob; } - public object TrueDispatch(ArrayList args) + private object TrueDispatch(object[] args) { MethodInfo method = dtype.GetMethod("Invoke"); ParameterInfo[] pi = method.GetParameters(); @@ -261,37 +300,108 @@ public object TrueDispatch(ArrayList args) throw e; } - if (rtype == typeof(void)) + try { - return null; - } + int byRefCount = pi.Count(parameterInfo => parameterInfo.ParameterType.IsByRef); + if (byRefCount > 0) + { + // By symmetry with MethodBinder.Invoke, when there are out + // parameters we expect to receive a tuple containing + // the result, if any, followed by the out parameters. If there is only + // one out parameter and the return type of the method is void, + // we instead receive the out parameter as the result from Python. + + bool isVoid = rtype == typeof(void); + int tupleSize = byRefCount + (isVoid ? 0 : 1); + if (isVoid && byRefCount == 1) + { + // The return type is void and there is a single out parameter. + for (int i = 0; i < pi.Length; i++) + { + Type t = pi[i].ParameterType; + if (t.IsByRef) + { + if (!Converter.ToManaged(op, t, out object newArg, true)) + { + Exceptions.RaiseTypeError($"The Python function did not return {t.GetElementType()} (the out parameter type)"); + throw new PythonException(); + } + args[i] = newArg; + break; + } + } + return null; + } + else if (Runtime.PyTuple_Check(op) && Runtime.PyTuple_Size(op) == tupleSize) + { + int index = isVoid ? 0 : 1; + for (int i = 0; i < pi.Length; i++) + { + Type t = pi[i].ParameterType; + if (t.IsByRef) + { + IntPtr item = Runtime.PyTuple_GetItem(op, index++); + if (!Converter.ToManaged(item, t, out object newArg, true)) + { + Exceptions.RaiseTypeError($"The Python function returned a tuple where element {i} was not {t.GetElementType()} (the out parameter type)"); + throw new PythonException(); + } + args[i] = newArg; + } + } + if (isVoid) + { + return null; + } + IntPtr item0 = Runtime.PyTuple_GetItem(op, 0); + if (!Converter.ToManaged(item0, rtype, out object result0, true)) + { + Exceptions.RaiseTypeError($"The Python function returned a tuple where element 0 was not {rtype} (the return type)"); + throw new PythonException(); + } + return result0; + } + else + { + string tpName = Runtime.PyObject_GetTypeName(op); + if (Runtime.PyTuple_Check(op)) + { + tpName += $" of size {Runtime.PyTuple_Size(op)}"; + } + StringBuilder sb = new StringBuilder(); + if (!isVoid) sb.Append(rtype.FullName); + for (int i = 0; i < pi.Length; i++) + { + Type t = pi[i].ParameterType; + if (t.IsByRef) + { + if (sb.Length > 0) sb.Append(","); + sb.Append(t.GetElementType().FullName); + } + } + string returnValueString = isVoid ? "" : "the return value and "; + Exceptions.RaiseTypeError($"Expected a tuple ({sb}) of {returnValueString}the values for out and ref parameters, got {tpName}."); + throw new PythonException(); + } + } + + if (rtype == typeof(void)) + { + return null; + } - object result = null; - if (!Converter.ToManaged(op, rtype, out result, false)) + object result; + if (!Converter.ToManaged(op, rtype, out result, true)) + { + throw new PythonException(); + } + + return result; + } + finally { Runtime.XDecref(op); - throw new ConversionException($"could not convert Python result to {rtype}"); } - - Runtime.XDecref(op); - return result; - } - - public IntPtr[] GetTrackedHandles() - { - return new IntPtr[] { target }; - } - } - - - public class ConversionException : Exception - { - public ConversionException() - { - } - - public ConversionException(string msg) : base(msg) - { } } } diff --git a/src/runtime/delegateobject.cs b/src/runtime/delegateobject.cs index c5078740f..e0d29f1a0 100644 --- a/src/runtime/delegateobject.cs +++ b/src/runtime/delegateobject.cs @@ -52,6 +52,12 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { var self = (DelegateObject)GetManagedObject(tp); + if (!self.type.Valid) + { + return Exceptions.RaiseTypeError(self.type.DeletedMessage); + } + Type type = self.type.Value; + if (Runtime.PyTuple_Size(args) != 1) { return Exceptions.RaiseTypeError("class takes exactly one argument"); @@ -64,7 +70,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) return Exceptions.RaiseTypeError("argument must be callable"); } - Delegate d = PythonEngine.DelegateManager.GetDelegate(self.type, method); + Delegate d = PythonEngine.DelegateManager.GetDelegate(type, method); return CLRObject.GetInstHandle(d, self.pyHandle); } diff --git a/src/runtime/eventbinding.cs b/src/runtime/eventbinding.cs index 581095185..3f5b7b007 100644 --- a/src/runtime/eventbinding.cs +++ b/src/runtime/eventbinding.cs @@ -68,35 +68,27 @@ public static IntPtr nb_inplace_subtract(IntPtr ob, IntPtr arg) /// /// EventBinding __hash__ implementation. /// - public static IntPtr tp_hash(IntPtr ob) + public static nint tp_hash(IntPtr ob) { var self = (EventBinding)GetManagedObject(ob); - long x = 0; - long y = 0; + nint x = 0; if (self.target != IntPtr.Zero) { - x = Runtime.PyObject_Hash(self.target).ToInt64(); + x = Runtime.PyObject_Hash(self.target); if (x == -1) { - return new IntPtr(-1); + return x; } } - y = Runtime.PyObject_Hash(self.e.pyHandle).ToInt64(); + nint y = Runtime.PyObject_Hash(self.e.pyHandle); if (y == -1) { - return new IntPtr(-1); + return y; } - x ^= y; - - if (x == -1) - { - x = -1; - } - - return new IntPtr(x); + return x ^ y; } diff --git a/src/runtime/eventobject.cs b/src/runtime/eventobject.cs index 0f2796a14..4dc785ddd 100644 --- a/src/runtime/eventobject.cs +++ b/src/runtime/eventobject.cs @@ -72,6 +72,12 @@ internal bool AddEventHandler(IntPtr target, IntPtr handler) /// internal bool RemoveEventHandler(IntPtr target, IntPtr handler) { + if (reg == null) + { + Exceptions.SetError(Exceptions.ValueError, "unknown event handler"); + return false; + } + object obj = null; if (target != IntPtr.Zero) { @@ -79,10 +85,9 @@ internal bool RemoveEventHandler(IntPtr target, IntPtr handler) obj = co.inst; } - IntPtr hash = Runtime.PyObject_Hash(handler); - if (Exceptions.ErrorOccurred() || reg == null) + nint hash = Runtime.PyObject_Hash(handler); + if (hash == -1 && Exceptions.ErrorOccurred()) { - Exceptions.SetError(Exceptions.ValueError, "unknown event handler"); return false; } diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index 58506bfbb..51c7729e3 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -1,6 +1,7 @@ using System; using System.Reflection; using System.Runtime.InteropServices; +using System.Text; namespace Python.Runtime { @@ -71,17 +72,26 @@ internal static Exception ToException(IntPtr ob) return Exceptions.RaiseTypeError("invalid object"); } - string message = string.Empty; - if (e.Message != string.Empty) + string message = e.ToString(); + string fullTypeName = e.GetType().FullName; + string prefix = fullTypeName + ": "; + if (message.StartsWith(prefix)) { - message = e.Message; + message = message.Substring(prefix.Length); } - if (!string.IsNullOrEmpty(e.StackTrace)) + else if (message.StartsWith(fullTypeName)) { - message = message + "\n" + e.StackTrace; + message = message.Substring(fullTypeName.Length); } return Runtime.PyUnicode_FromString(message); } + + public static int tp_init(IntPtr ob, IntPtr args, IntPtr kwds) + { + Exceptions.SetArgsAndCause(ob); + return 0; + } + } /// @@ -177,27 +187,37 @@ internal static void SetArgsAndCause(IntPtr ob) args = Runtime.PyTuple_New(0); } - Marshal.WriteIntPtr(ob, ExceptionOffset.args, args); - + int baseOffset = OriginalObjectOffsets.Size; + Runtime.Py_SETREF(ob, baseOffset + ExceptionOffset.args, args); + if (e.InnerException != null) { - IntPtr cause = CLRObject.GetInstHandle(e.InnerException); - Marshal.WriteIntPtr(ob, ExceptionOffset.cause, cause); + IntPtr cause = GetExceptHandle(e.InnerException); + Runtime.Py_SETREF(ob, baseOffset + ExceptionOffset.cause, cause); } } + internal static IntPtr GetExceptHandle(Exception e) + { + IntPtr op = CLRObject.GetInstHandle(e); + SetArgsAndCause(op); + return op; + } + /// /// Shortcut for (pointer == NULL) -> throw PythonException /// /// Pointer to a Python object - internal static void ErrorCheck(IntPtr pointer) + internal static void ErrorCheck(BorrowedReference pointer) { - if (pointer == IntPtr.Zero) + if (pointer.IsNull) { throw new PythonException(); } } + internal static void ErrorCheck(IntPtr pointer) => ErrorCheck(new BorrowedReference(pointer)); + /// /// Shortcut for (pointer == NULL or ErrorOccurred()) -> throw PythonException /// @@ -283,13 +303,27 @@ public static void SetError(Exception e) return; } - IntPtr op = CLRObject.GetInstHandle(e); - IntPtr etype = Runtime.PyObject_GetAttrString(op, "__class__"); + IntPtr op = GetExceptHandle(e); + IntPtr etype = Runtime.PyObject_GetAttr(op, PyIdentifier.__class__); Runtime.PyErr_SetObject(new BorrowedReference(etype), new BorrowedReference(op)); Runtime.XDecref(etype); Runtime.XDecref(op); } + /// + /// When called after SetError, sets the cause of the error. + /// + /// The cause of the current error + public static void SetCause(PythonException cause) + { + var currentException = new PythonException(); + currentException.Normalize(); + cause.Normalize(); + Runtime.XIncref(cause.PyValue); + Runtime.PyException_SetCause(currentException.PyValue, cause.PyValue); + currentException.Restore(); + } + /// /// ErrorOccurred Method /// @@ -368,17 +402,31 @@ public static void deprecation(string message) // Internal helper methods for common error handling scenarios. //==================================================================== + /// + /// Raises a TypeError exception and attaches any existing exception as its cause. + /// + /// The exception message + /// IntPtr.Zero internal static IntPtr RaiseTypeError(string message) { + PythonException previousException = null; + if (ErrorOccurred()) + { + previousException = new PythonException(); + } Exceptions.SetError(Exceptions.TypeError, message); + if (previousException != null) + { + SetCause(previousException); + } return IntPtr.Zero; } // 2010-11-16: Arranged in python (2.6 & 2.7) source header file order /* Predefined exceptions are - puplic static variables on the Exceptions class filled in from + public static variables on the Exceptions class filled in from the python class using reflection in Initialize() looked up by - name, not posistion. */ + name, not position. */ public static IntPtr BaseException; public static IntPtr Exception; public static IntPtr StopIteration; diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs index a5f0f1219..e6c95ecaa 100644 --- a/src/runtime/extensiontype.cs +++ b/src/runtime/extensiontype.cs @@ -55,7 +55,7 @@ void SetupGc () /// public static void FinalizeObject(ManagedType self) { - ClearObjectDict(self.pyHandle); + RemoveObjectDict(self.pyHandle); Runtime.PyObject_GC_Del(self.pyHandle); // Not necessary for decref of `tpHandle`. self.FreeGCHandle(); diff --git a/src/runtime/fieldobject.cs b/src/runtime/fieldobject.cs index 86b93dd1b..2850ac6e1 100644 --- a/src/runtime/fieldobject.cs +++ b/src/runtime/fieldobject.cs @@ -3,13 +3,14 @@ namespace Python.Runtime { + using MaybeFieldInfo = MaybeMemberInfo; /// /// Implements a Python descriptor type that provides access to CLR fields. /// [Serializable] internal class FieldObject : ExtensionType { - private FieldInfo info; + private MaybeFieldInfo info; public FieldObject(FieldInfo info) { @@ -30,8 +31,13 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { return IntPtr.Zero; } + else if (!self.info.Valid) + { + Exceptions.SetError(Exceptions.AttributeError, self.info.DeletedMessage); + return IntPtr.Zero; + } - FieldInfo info = self.info; + FieldInfo info = self.info.Value; if (ob == IntPtr.Zero || ob == Runtime.PyNone) { @@ -85,6 +91,11 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { return -1; } + else if (!self.info.Valid) + { + Exceptions.SetError(Exceptions.AttributeError, self.info.DeletedMessage); + return -1; + } if (val == IntPtr.Zero) { @@ -92,7 +103,7 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) return -1; } - FieldInfo info = self.info; + FieldInfo info = self.info.Value; if (info.IsLiteral || info.IsInitOnly) { @@ -147,7 +158,7 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) public static IntPtr tp_repr(IntPtr ob) { var self = (FieldObject)GetManagedObject(ob); - return Runtime.PyString_FromString($""); + return Runtime.PyString_FromString($""); } } } diff --git a/src/runtime/finalizer.cs b/src/runtime/finalizer.cs index 70b69345b..fe2e46aac 100644 --- a/src/runtime/finalizer.cs +++ b/src/runtime/finalizer.cs @@ -27,7 +27,7 @@ public class ErrorArgs : EventArgs public int Threshold { get; set; } public bool Enable { get; set; } - private ConcurrentQueue _objQueue = new ConcurrentQueue(); + private ConcurrentQueue _objQueue = new ConcurrentQueue(); private int _throttled; #region FINALIZER_CHECK @@ -42,7 +42,7 @@ public class ErrorArgs : EventArgs public class IncorrectFinalizeArgs : EventArgs { public IntPtr Handle { get; internal set; } - public ICollection ImpactedObjects { get; internal set; } + public ICollection ImpactedObjects { get; internal set; } } public class IncorrectRefCountException : Exception @@ -62,7 +62,9 @@ public IncorrectRefCountException(IntPtr ptr) } public delegate bool IncorrectRefCntHandler(object sender, IncorrectFinalizeArgs e); - public event IncorrectRefCntHandler IncorrectRefCntResolver; + #pragma warning disable 414 + public event IncorrectRefCntHandler IncorrectRefCntResolver = null; + #pragma warning restore 414 public bool ThrowIfUnhandleIncorrectRefCount { get; set; } = true; #endregion @@ -73,8 +75,6 @@ private Finalizer() Threshold = 200; } - [Obsolete("forceDispose parameter is unused. All objects are disposed regardless.")] - public void Collect(bool forceDispose) => this.DisposeAll(); public void Collect() => this.DisposeAll(); internal void ThrottledCollect() @@ -85,14 +85,14 @@ internal void ThrottledCollect() this.Collect(); } - public List GetCollectedObjects() + internal List GetCollectedObjects() { - return _objQueue.Select(T => new WeakReference(T)).ToList(); + return _objQueue.ToList(); } - internal void AddFinalizedObject(IPyDisposable obj) + internal void AddFinalizedObject(ref IntPtr obj) { - if (!Enable) + if (!Enable || obj == IntPtr.Zero) { return; } @@ -103,6 +103,7 @@ internal void AddFinalizedObject(IPyDisposable obj) { this._objQueue.Enqueue(obj); } + obj = IntPtr.Zero; } internal static void Shutdown() @@ -123,29 +124,44 @@ private void DisposeAll() #if FINALIZER_CHECK ValidateRefCount(); #endif - IPyDisposable obj; - while (_objQueue.TryDequeue(out obj)) + IntPtr obj; + Runtime.PyErr_Fetch(out var errType, out var errVal, out var traceback); + + try { - try - { - obj.Dispose(); - } - catch (Exception e) + while (!_objQueue.IsEmpty) { - var handler = ErrorHandler; - if (handler is null) + if (!_objQueue.TryDequeue(out obj)) + continue; + + Runtime.XDecref(obj); + try { - throw new FinalizationException( - "Python object finalization failed", - disposable: obj, innerException: e); + Runtime.CheckExceptionOccurred(); } - - handler.Invoke(this, new ErrorArgs() + catch (Exception e) { - Error = e - }); + var handler = ErrorHandler; + if (handler is null) + { + throw new FinalizationException( + "Python object finalization failed", + disposable: obj, innerException: e); + } + + handler.Invoke(this, new ErrorArgs() + { + Error = e + }); + } } } + finally + { + // Python requires finalizers to preserve exception: + // https://docs.python.org/3/extending/newtypes.html#finalization-and-de-allocation + Runtime.PyErr_Restore(errType, errVal, traceback); + } } } @@ -158,33 +174,26 @@ private void ValidateRefCount() } var counter = new Dictionary(); var holdRefs = new Dictionary(); - var indexer = new Dictionary>(); + var indexer = new Dictionary>(); foreach (var obj in _objQueue) { - IntPtr[] handles = obj.GetTrackedHandles(); - foreach (var handle in handles) + var handle = obj; + if (!counter.ContainsKey(handle)) { - if (handle == IntPtr.Zero) - { - continue; - } - if (!counter.ContainsKey(handle)) - { - counter[handle] = 0; - } - counter[handle]++; - if (!holdRefs.ContainsKey(handle)) - { - holdRefs[handle] = Runtime.Refcount(handle); - } - List objs; - if (!indexer.TryGetValue(handle, out objs)) - { - objs = new List(); - indexer.Add(handle, objs); - } - objs.Add(obj); + counter[handle] = 0; + } + counter[handle]++; + if (!holdRefs.ContainsKey(handle)) + { + holdRefs[handle] = Runtime.Refcount(handle); + } + List objs; + if (!indexer.TryGetValue(handle, out objs)) + { + objs = new List(); + indexer.Add(handle, objs); } + objs.Add(obj); } foreach (var pair in counter) { @@ -227,12 +236,13 @@ private void ValidateRefCount() public class FinalizationException : Exception { - public IPyDisposable Disposable { get; } + public IntPtr PythonObject { get; } - public FinalizationException(string message, IPyDisposable disposable, Exception innerException) + public FinalizationException(string message, IntPtr disposable, Exception innerException) : base(message, innerException) { - this.Disposable = disposable ?? throw new ArgumentNullException(nameof(disposable)); + if (disposable == IntPtr.Zero) throw new ArgumentNullException(nameof(disposable)); + this.PythonObject = disposable; } } } diff --git a/src/runtime/genericutil.cs b/src/runtime/genericutil.cs index df78d9899..92b847e98 100644 --- a/src/runtime/genericutil.cs +++ b/src/runtime/genericutil.cs @@ -1,3 +1,4 @@ +using System.Linq; using System; using System.Collections.Generic; using System.Resources; @@ -8,14 +9,13 @@ namespace Python.Runtime /// This class is responsible for efficiently maintaining the bits /// of information we need to support aliases with 'nice names'. /// - internal class GenericUtil + internal static class GenericUtil { + /// + /// Maps namespace -> generic base name -> list of generic type names + /// private static Dictionary>> mapping; - private GenericUtil() - { - } - public static void Reset() { mapping = new Dictionary>>(); @@ -24,6 +24,7 @@ public static void Reset() /// /// Register a generic type that appears in a given namespace. /// + /// A generic type definition (t.IsGenericTypeDefinition must be true) internal static void Register(Type t) { if (null == t.Namespace || null == t.Name) @@ -31,22 +32,15 @@ internal static void Register(Type t) return; } - Dictionary> nsmap = null; - mapping.TryGetValue(t.Namespace, out nsmap); - if (nsmap == null) + Dictionary> nsmap; + if (!mapping.TryGetValue(t.Namespace, out nsmap)) { nsmap = new Dictionary>(); mapping[t.Namespace] = nsmap; } - string basename = t.Name; - int tick = basename.IndexOf("`"); - if (tick > -1) - { - basename = basename.Substring(0, tick); - } - List gnames = null; - nsmap.TryGetValue(basename, out gnames); - if (gnames == null) + string basename = GetBasename(t.Name); + List gnames; + if (!nsmap.TryGetValue(basename, out gnames)) { gnames = new List(); nsmap[basename] = gnames; @@ -59,9 +53,8 @@ internal static void Register(Type t) /// public static List GetGenericBaseNames(string ns) { - Dictionary> nsmap = null; - mapping.TryGetValue(ns, out nsmap); - if (nsmap == null) + Dictionary> nsmap; + if (!mapping.TryGetValue(ns, out nsmap)) { return null; } @@ -74,64 +67,41 @@ public static List GetGenericBaseNames(string ns) } /// - /// xxx + /// Finds a generic type with the given number of generic parameters and the same name and namespace as . /// public static Type GenericForType(Type t, int paramCount) { return GenericByName(t.Namespace, t.Name, paramCount); } - public static Type GenericByName(string ns, string name, int paramCount) - { - foreach (Type t in GenericsByName(ns, name)) - { - if (t.GetGenericArguments().Length == paramCount) - { - return t; - } - } - return null; - } - - public static List GenericsForType(Type t) - { - return GenericsByName(t.Namespace, t.Name); - } - - public static List GenericsByName(string ns, string basename) + /// + /// Finds a generic type in the given namespace with the given name and number of generic parameters. + /// + public static Type GenericByName(string ns, string basename, int paramCount) { - Dictionary> nsmap = null; - mapping.TryGetValue(ns, out nsmap); - if (nsmap == null) + Dictionary> nsmap; + if (!mapping.TryGetValue(ns, out nsmap)) { return null; } - int tick = basename.IndexOf("`"); - if (tick > -1) - { - basename = basename.Substring(0, tick); - } - - List names = null; - nsmap.TryGetValue(basename, out names); - if (names == null) + List names; + if (!nsmap.TryGetValue(GetBasename(basename), out names)) { return null; } - var result = new List(); foreach (string name in names) { string qname = ns + "." + name; - Type o = AssemblyManager.LookupType(qname); - if (o != null) + Type o = AssemblyManager.LookupTypes(qname).FirstOrDefault(); + if (o != null && o.GetGenericArguments().Length == paramCount) { - result.Add(o); + return o; } } - return result; + return null; } /// @@ -139,13 +109,12 @@ public static List GenericsByName(string ns, string basename) /// public static string GenericNameForBaseName(string ns, string name) { - Dictionary> nsmap = null; - mapping.TryGetValue(ns, out nsmap); - if (nsmap == null) + Dictionary> nsmap; + if (!mapping.TryGetValue(ns, out nsmap)) { return null; } - List gnames = null; + List gnames; nsmap.TryGetValue(name, out gnames); if (gnames?.Count > 0) { @@ -153,5 +122,18 @@ public static string GenericNameForBaseName(string ns, string name) } return null; } + + private static string GetBasename(string name) + { + int tick = name.IndexOf("`"); + if (tick > -1) + { + return name.Substring(0, tick); + } + else + { + return name; + } + } } } diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs index 8cf57c85d..066c765fe 100644 --- a/src/runtime/importhook.cs +++ b/src/runtime/importhook.cs @@ -13,6 +13,7 @@ internal static class ImportHook private static CLRModule root; private static MethodWrapper hook; private static IntPtr py_clr_module; + static BorrowedReference ClrModuleReference => new BorrowedReference(py_clr_module); private static IntPtr module_def = IntPtr.Zero; @@ -43,11 +44,11 @@ static void InitImport() // look in CLR modules, then if we don't find any call the default // Python __import__. IntPtr builtins = Runtime.GetBuiltins(); - py_import = Runtime.PyObject_GetAttrString(builtins, "__import__"); + py_import = Runtime.PyObject_GetAttr(builtins, PyIdentifier.__import__); PythonException.ThrowIfIsNull(py_import); hook = new MethodWrapper(typeof(ImportHook), "__import__", "TernaryFunc"); - int res = Runtime.PyObject_SetAttrString(builtins, "__import__", hook.ptr); + int res = Runtime.PyObject_SetAttr(builtins, PyIdentifier.__import__, hook.ptr); PythonException.ThrowIfIsNotZero(res); Runtime.XDecref(builtins); @@ -60,7 +61,7 @@ static void RestoreImport() { IntPtr builtins = Runtime.GetBuiltins(); - int res = Runtime.PyObject_SetAttrString(builtins, "__import__", py_import); + int res = Runtime.PyObject_SetAttr(builtins, PyIdentifier.__import__, py_import); PythonException.ThrowIfIsNotZero(res); Runtime.XDecref(py_import); py_import = IntPtr.Zero; @@ -74,7 +75,7 @@ static void RestoreImport() /// /// Initialization performed on startup of the Python runtime. /// - internal static void Initialize() + internal static unsafe void Initialize() { InitImport(); @@ -86,14 +87,13 @@ internal static void Initialize() py_clr_module = Runtime.PyModule_Create2(module_def, 3); // both dicts are borrowed references - IntPtr mod_dict = Runtime.PyModule_GetDict(py_clr_module); - IntPtr clr_dict = Runtime._PyObject_GetDictPtr(root.pyHandle); // PyObject** - clr_dict = (IntPtr)Marshal.PtrToStructure(clr_dict, typeof(IntPtr)); + BorrowedReference mod_dict = Runtime.PyModule_GetDict(ClrModuleReference); + BorrowedReference clr_dict = *Runtime._PyObject_GetDictPtr(root.ObjectReference); Runtime.PyDict_Update(mod_dict, clr_dict); - IntPtr dict = Runtime.PyImport_GetModuleDict(); - Runtime.PyDict_SetItemString(dict, "CLR", py_clr_module); - Runtime.PyDict_SetItemString(dict, "clr", py_clr_module); + BorrowedReference dict = Runtime.PyImport_GetModuleDict(); + Runtime.PyDict_SetItemString(dict, "CLR", ClrModuleReference); + Runtime.PyDict_SetItemString(dict, "clr", ClrModuleReference); } @@ -124,7 +124,7 @@ internal static void Shutdown() internal static void SaveRuntimeData(RuntimeDataStorage storage) { - // Increment the reference counts here so that the objects don't + // Increment the reference counts here so that the objects don't // get freed in Shutdown. Runtime.XIncref(py_clr_module); Runtime.XIncref(root.pyHandle); @@ -143,67 +143,62 @@ internal static void RestoreRuntimeData(RuntimeDataStorage storage) /// /// Return the clr python module (new reference) /// - public static IntPtr GetCLRModule(IntPtr? fromList = null) + public static unsafe NewReference GetCLRModule(BorrowedReference fromList = default) { root.InitializePreload(); // update the module dictionary with the contents of the root dictionary root.LoadNames(); - IntPtr py_mod_dict = Runtime.PyModule_GetDict(py_clr_module); - IntPtr clr_dict = Runtime._PyObject_GetDictPtr(root.pyHandle); // PyObject** - clr_dict = (IntPtr)Marshal.PtrToStructure(clr_dict, typeof(IntPtr)); + BorrowedReference py_mod_dict = Runtime.PyModule_GetDict(ClrModuleReference); + BorrowedReference clr_dict = *Runtime._PyObject_GetDictPtr(root.ObjectReference); Runtime.PyDict_Update(py_mod_dict, clr_dict); // find any items from the from list and get them from the root if they're not // already in the module dictionary - if (fromList != null && fromList != IntPtr.Zero) + if (fromList != null && fromList != default) { - if (Runtime.PyTuple_Check(fromList.GetValueOrDefault())) + if (Runtime.PyTuple_Check(fromList)) { - Runtime.XIncref(py_mod_dict); - using (var mod_dict = new PyDict(py_mod_dict)) + using var mod_dict = new PyDict(py_mod_dict); + using var from = new PyTuple(fromList); + foreach (PyObject item in from) { - Runtime.XIncref(fromList.GetValueOrDefault()); - using (var from = new PyTuple(fromList.GetValueOrDefault())) + if (mod_dict.HasKey(item)) { - foreach (PyObject item in from) - { - if (mod_dict.HasKey(item)) - { - continue; - } - - var s = item.AsManagedObject(typeof(string)) as string; - if (s == null) - { - continue; - } - - ManagedType attr = root.GetAttribute(s, true); - if (attr == null) - { - continue; - } - - Runtime.XIncref(attr.pyHandle); - using (var obj = new PyObject(attr.pyHandle)) - { - mod_dict.SetItem(s, obj); - } - } + continue; + } + + var s = item.AsManagedObject(typeof(string)) as string; + if (s == null) + { + continue; + } + + ManagedType attr = root.GetAttribute(s, true); + if (attr == null) + { + continue; + } + + Runtime.XIncref(attr.pyHandle); + using (var obj = new PyObject(attr.pyHandle)) + { + mod_dict.SetItem(s, obj); } } } } Runtime.XIncref(py_clr_module); - return py_clr_module; + return NewReference.DangerousFromPointer(py_clr_module); } /// /// The actual import hook that ties Python to the managed world. /// - public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) + public static IntPtr __import__(IntPtr self, IntPtr argsRaw, IntPtr kw) { + var args = new BorrowedReference(argsRaw); + // Replacement for the builtin __import__. The original import // hook is saved as this.py_import. This version handles CLR // import and defers to the normal builtin for everything else. @@ -214,9 +209,8 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) return Exceptions.RaiseTypeError("__import__() takes at least 1 argument (0 given)"); } - // borrowed reference - IntPtr py_mod_name = Runtime.PyTuple_GetItem(args, 0); - if (py_mod_name == IntPtr.Zero || + BorrowedReference py_mod_name = Runtime.PyTuple_GetItem(args, 0); + if (py_mod_name.IsNull || !Runtime.IsStringType(py_mod_name)) { return Exceptions.RaiseTypeError("string expected"); @@ -225,12 +219,12 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) // Check whether the import is of the form 'from x import y'. // This determines whether we return the head or tail module. - IntPtr fromList = IntPtr.Zero; + BorrowedReference fromList = default; var fromlist = false; if (num_args >= 4) { fromList = Runtime.PyTuple_GetItem(args, 3); - if (fromList != IntPtr.Zero && + if (fromList != default && Runtime.PyObject_IsTrue(fromList) == 1) { fromlist = true; @@ -241,113 +235,69 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) // Check these BEFORE the built-in import runs; may as well // do the Incref()ed return here, since we've already found // the module. - if (mod_name == "clr" || mod_name == "CLR") + if (mod_name == "clr") { - if (mod_name == "CLR") - { - Exceptions.deprecation("The CLR module is deprecated. Please use 'clr'."); - } - IntPtr clr_module = GetCLRModule(fromList); - if (clr_module != IntPtr.Zero) + NewReference clr_module = GetCLRModule(fromList); + if (!clr_module.IsNull()) { - IntPtr sys_modules = Runtime.PyImport_GetModuleDict(); - if (sys_modules != IntPtr.Zero) + BorrowedReference sys_modules = Runtime.PyImport_GetModuleDict(); + if (!sys_modules.IsNull) { Runtime.PyDict_SetItemString(sys_modules, "clr", clr_module); } } - return clr_module; + return clr_module.DangerousMoveToPointerOrNull(); } string realname = mod_name; string clr_prefix = null; - if (mod_name.StartsWith("CLR.")) - { - clr_prefix = "CLR."; // prepend when adding the module to sys.modules - realname = mod_name.Substring(4); - string msg = $"Importing from the CLR.* namespace is deprecated. Please import '{realname}' directly."; - Exceptions.deprecation(msg); - } - else + // 2010-08-15: Always seemed smart to let python try first... + // This shaves off a few tenths of a second on test_module.py + // and works around a quirk where 'sys' is found by the + // LoadImplicit() deprecation logic. + // Turns out that the AssemblyManager.ResolveHandler() checks to see if any + // Assembly's FullName.ToLower().StartsWith(name.ToLower()), which makes very + // little sense to me. + IntPtr res = Runtime.PyObject_Call(py_import, args.DangerousGetAddress(), kw); + if (res != IntPtr.Zero) { - // 2010-08-15: Always seemed smart to let python try first... - // This shaves off a few tenths of a second on test_module.py - // and works around a quirk where 'sys' is found by the - // LoadImplicit() deprecation logic. - // Turns out that the AssemblyManager.ResolveHandler() checks to see if any - // Assembly's FullName.ToLower().StartsWith(name.ToLower()), which makes very - // little sense to me. - IntPtr res = Runtime.PyObject_Call(py_import, args, kw); - if (res != IntPtr.Zero) - { - // There was no error. - if (fromlist && IsLoadAll(fromList)) - { - var mod = ManagedType.GetManagedObject(res) as ModuleObject; - mod?.LoadNames(); - } - return res; - } - // There was an error - if (!Exceptions.ExceptionMatches(Exceptions.ImportError)) - { - // and it was NOT an ImportError; bail out here. - return IntPtr.Zero; - } - - if (mod_name == string.Empty) + // There was no error. + if (fromlist && IsLoadAll(fromList)) { - // Most likely a missing relative import. - // For example site-packages\bs4\builder\__init__.py uses it to check if a package exists: - // from . import _html5lib - // We don't support them anyway - return IntPtr.Zero; + var mod = ManagedType.GetManagedObject(res) as ModuleObject; + mod?.LoadNames(); } - // Otherwise, just clear the it. - Exceptions.Clear(); + return res; + } + // There was an error + if (!Exceptions.ExceptionMatches(Exceptions.ImportError)) + { + // and it was NOT an ImportError; bail out here. + return IntPtr.Zero; } - string[] names = realname.Split('.'); - - // Now we need to decide if the name refers to a CLR module, - // and may have to do an implicit load (for b/w compatibility) - // using the AssemblyManager. The assembly manager tries - // really hard not to use Python objects or APIs, because - // parts of it can run recursively and on strange threads. - // - // It does need an opportunity from time to time to check to - // see if sys.path has changed, in a context that is safe. Here - // we know we have the GIL, so we'll let it update if needed. - - AssemblyManager.UpdatePath(); - if (!AssemblyManager.IsValidNamespace(realname)) + if (mod_name == string.Empty) { - var loadExceptions = new List(); - if (!AssemblyManager.LoadImplicit(realname, assemblyLoadErrorHandler: loadExceptions.Add)) - { - // May be called when a module being imported imports a module. - // In particular, I've seen decimal import copy import org.python.core - IntPtr importResult = Runtime.PyObject_Call(py_import, args, kw); - // TODO: use ModuleNotFoundError in Python 3.6+ - if (importResult == IntPtr.Zero && loadExceptions.Count > 0 - && Exceptions.ExceptionMatches(Exceptions.ImportError)) - { - loadExceptions.Add(new PythonException()); - var importError = new PyObject(new BorrowedReference(Exceptions.ImportError)); - importError.SetAttr("__cause__", new AggregateException(loadExceptions).ToPython()); - Runtime.PyErr_SetObject(new BorrowedReference(Exceptions.ImportError), importError.Reference); - } - return importResult; - } + // Most likely a missing relative import. + // For example site-packages\bs4\builder\__init__.py uses it to check if a package exists: + // from . import _html5lib + // We don't support them anyway + return IntPtr.Zero; } + // Save the exception + var originalException = new PythonException(); + // Otherwise, just clear the it. + Exceptions.Clear(); + + string[] names = realname.Split('.'); // See if sys.modules for this interpreter already has the // requested module. If so, just return the existing module. - IntPtr modules = Runtime.PyImport_GetModuleDict(); - IntPtr module = Runtime.PyDict_GetItem(modules, py_mod_name); + BorrowedReference modules = Runtime.PyImport_GetModuleDict(); + BorrowedReference module = Runtime.PyDict_GetItem(modules, py_mod_name); - if (module != IntPtr.Zero) + if (module != default) { if (fromlist) { @@ -356,16 +306,14 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) var mod = ManagedType.GetManagedObject(module) as ModuleObject; mod?.LoadNames(); } - Runtime.XIncref(module); - return module; + return new NewReference(module).DangerousMoveToPointer(); } if (clr_prefix != null) { - return GetCLRModule(fromList); + return GetCLRModule(fromList).DangerousMoveToPointerOrNull(); } module = Runtime.PyDict_GetItemString(modules, names[0]); - Runtime.XIncref(module); - return module; + return new NewReference(module, canBeNull: true).DangerousMoveToPointer(); } Exceptions.Clear(); @@ -388,7 +336,7 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) ManagedType mt = tail.GetAttribute(name, true); if (!(mt is ModuleObject)) { - Exceptions.SetError(Exceptions.ImportError, $"No module named {name}"); + originalException.Restore(); return IntPtr.Zero; } if (head == null) @@ -402,12 +350,12 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) } // Add the module to sys.modules - Runtime.PyDict_SetItemString(modules, tail.moduleName, tail.pyHandle); + Runtime.PyDict_SetItemString(modules, tail.moduleName, tail.ObjectReference); - // If imported from CLR add CLR. to sys.modules as well + // If imported from CLR add clr. to sys.modules as well if (clr_prefix != null) { - Runtime.PyDict_SetItemString(modules, clr_prefix + tail.moduleName, tail.pyHandle); + Runtime.PyDict_SetItemString(modules, clr_prefix + tail.moduleName, tail.ObjectReference); } } @@ -424,7 +372,7 @@ public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) } } - private static bool IsLoadAll(IntPtr fromList) + private static bool IsLoadAll(BorrowedReference fromList) { if (CLRModule.preload) { @@ -434,10 +382,8 @@ private static bool IsLoadAll(IntPtr fromList) { return false; } - IntPtr fp = Runtime.PySequence_GetItem(fromList, 0); - bool res = Runtime.GetManagedString(fp) == "*"; - Runtime.XDecref(fp); - return res; + using var fp = Runtime.PySequence_GetItem(fromList, 0); + return Runtime.GetManagedString(fp) == "*"; } } } diff --git a/src/runtime/interfaceobject.cs b/src/runtime/interfaceobject.cs index a2fa86479..976c09be0 100644 --- a/src/runtime/interfaceobject.cs +++ b/src/runtime/interfaceobject.cs @@ -37,8 +37,12 @@ static InterfaceObject() public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { var self = (InterfaceObject)GetManagedObject(tp); + if (!self.type.Valid) + { + return Exceptions.RaiseTypeError(self.type.DeletedMessage); + } var nargs = Runtime.PyTuple_Size(args); - Type type = self.type; + Type type = self.type.Value; object obj; if (nargs == 1) diff --git a/src/runtime/intern.cs b/src/runtime/intern.cs new file mode 100644 index 000000000..ced1e5e92 --- /dev/null +++ b/src/runtime/intern.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Python.Runtime +{ + static partial class InternString + { + private static Dictionary _string2interns; + private static Dictionary _intern2strings; + + static InternString() + { + var identifierNames = typeof(PyIdentifier).GetFields().Select(fi => fi.Name); + var validNames = new HashSet(identifierNames); + if (validNames.Count != _builtinNames.Length) + { + throw new InvalidOperationException("Identifiers args not matching"); + } + foreach (var name in _builtinNames) + { + if (!validNames.Contains(name)) + { + throw new InvalidOperationException($"{name} is not declared"); + } + } + } + + public static void Initialize() + { + _string2interns = new Dictionary(); + _intern2strings = new Dictionary(); + + Type type = typeof(PyIdentifier); + foreach (string name in _builtinNames) + { + IntPtr op = Runtime.PyUnicode_InternFromString(name); + SetIntern(name, op); + type.GetField(name).SetValue(null, op); + } + } + + public static void Shutdown() + { + foreach (var entry in _intern2strings) + { + Runtime.XDecref(entry.Key); + typeof(PyIdentifier).GetField(entry.Value).SetValue(null, IntPtr.Zero); + } + _string2interns = null; + _intern2strings = null; + } + + public static string GetManagedString(IntPtr op) + { + string s; + if (TryGetInterned(op, out s)) + { + return s; + } + return Runtime.GetManagedString(op); + } + + public static bool TryGetInterned(IntPtr op, out string s) + { + return _intern2strings.TryGetValue(op, out s); + } + + private static void SetIntern(string s, IntPtr op) + { + _string2interns.Add(s, op); + _intern2strings.Add(op, s); + } + } +} diff --git a/src/runtime/intern_.cs b/src/runtime/intern_.cs new file mode 100644 index 000000000..f9b3f43ec --- /dev/null +++ b/src/runtime/intern_.cs @@ -0,0 +1,48 @@ +using System; + +namespace Python.Runtime +{ + static class PyIdentifier + { + public static IntPtr __name__; + public static IntPtr __dict__; + public static IntPtr __doc__; + public static IntPtr __class__; + public static IntPtr __module__; + public static IntPtr __file__; + public static IntPtr __slots__; + public static IntPtr __self__; + public static IntPtr __annotations__; + public static IntPtr __init__; + public static IntPtr __repr__; + public static IntPtr __import__; + public static IntPtr __builtins__; + public static IntPtr builtins; + public static IntPtr __overloads__; + public static IntPtr Overloads; + } + + + static partial class InternString + { + private static readonly string[] _builtinNames = new string[] + { + "__name__", + "__dict__", + "__doc__", + "__class__", + "__module__", + "__file__", + "__slots__", + "__self__", + "__annotations__", + "__init__", + "__repr__", + "__import__", + "__builtins__", + "builtins", + "__overloads__", + "Overloads", + }; + } +} diff --git a/src/runtime/intern_.tt b/src/runtime/intern_.tt new file mode 100644 index 000000000..c7142ec9f --- /dev/null +++ b/src/runtime/intern_.tt @@ -0,0 +1,58 @@ +<#@ template debug="true" hostSpecific="true" #> +<#@ output extension=".cs" #> +<# + string[] internNames = new string[] + { + "__name__", + "__dict__", + "__doc__", + "__class__", + "__module__", + "__file__", + "__slots__", + "__self__", + "__annotations__", + + "__init__", + "__repr__", + "__import__", + "__builtins__", + + "builtins", + + "__overloads__", + "Overloads", + }; +#> +using System; + +namespace Python.Runtime +{ + static class PyIdentifier + { +<# + foreach (var name in internNames) + { +#> + public static IntPtr <#= name #>; +<# + } +#> + } + + + static partial class InternString + { + private static readonly string[] _builtinNames = new string[] + { +<# + foreach (var name in internNames) + { +#> + "<#= name #>", +<# + } +#> + }; + } +} diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs index 1caabab17..970ecb20d 100644 --- a/src/runtime/interop.cs +++ b/src/runtime/interop.cs @@ -68,56 +68,32 @@ public ModulePropertyAttribute() } } - internal static partial class TypeOffset - { - static TypeOffset() - { - Type type = typeof(TypeOffset); - FieldInfo[] fields = type.GetFields(); - int size = IntPtr.Size; - for (int i = 0; i < fields.Length; i++) - { - int offset = i * size; - FieldInfo fi = fields[i]; - fi.SetValue(null, offset); - } - } - - public static int magic() => ManagedDataOffsets.Magic; - } - internal static class ManagedDataOffsets { - public static int Magic { get; private set; } public static readonly Dictionary NameMapping = new Dictionary(); static class DataOffsets { - public static readonly int ob_data; - public static readonly int ob_dict; + public static readonly int ob_data = 0; + public static readonly int ob_dict = 0; + public static readonly int ob_weaklist = 0; static DataOffsets() { FieldInfo[] fields = typeof(DataOffsets).GetFields(BindingFlags.Static | BindingFlags.Public); for (int i = 0; i < fields.Length; i++) { - fields[i].SetValue(null, -(i * IntPtr.Size) - IntPtr.Size); + fields[i].SetValue(null, (i) * IntPtr.Size); } } } static ManagedDataOffsets() { - Type type = typeof(TypeOffset); - foreach (FieldInfo fi in type.GetFields()) - { - NameMapping[fi.Name] = (int)fi.GetValue(null); - } - // XXX: Use the members after PyHeapTypeObject as magic slot - Magic = TypeOffset.members; + NameMapping = TypeOffset.GetOffsets(); FieldInfo[] fields = typeof(DataOffsets).GetFields(BindingFlags.Static | BindingFlags.Public); - size = fields.Length * IntPtr.Size; + Size = fields.Length * IntPtr.Size; } public static int GetSlotOffset(string name) @@ -125,29 +101,10 @@ public static int GetSlotOffset(string name) return NameMapping[name]; } - private static int BaseOffset(IntPtr type) - { - Debug.Assert(type != IntPtr.Zero); - int typeSize = Marshal.ReadInt32(type, TypeOffset.tp_basicsize); - Debug.Assert(typeSize > 0); - return typeSize; - } - - public static int DataOffset(IntPtr type) - { - return BaseOffset(type) + DataOffsets.ob_data; - } - - public static int DictOffset(IntPtr type) - { - return BaseOffset(type) + DataOffsets.ob_dict; - } - public static int ob_data => DataOffsets.ob_data; public static int ob_dict => DataOffsets.ob_dict; - public static int Size { get { return size; } } - - private static readonly int size; + public static int ob_weaklist => DataOffsets.ob_weaklist; + public static int Size { get; private set; } } internal static class OriginalObjectOffsets @@ -182,6 +139,26 @@ static OriginalObjectOffsets() public static int ob_type; } + + internal static class ManagedExceptionOffset + { + public static int ob_data { get; private set; } + public static int ob_dict { get; private set; } + public static int ob_weaklist { get; private set; } + + public static int Size { get; private set; } + + static ManagedExceptionOffset() + { + int baseOffset = OriginalObjectOffsets.Size + ExceptionOffset.Size(); + ob_data = baseOffset + ManagedDataOffsets.ob_data; + ob_dict = baseOffset + ManagedDataOffsets.ob_dict; + ob_weaklist = baseOffset + ManagedDataOffsets.ob_weaklist; + Size = ob_weaklist + IntPtr.Size; + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] internal class ObjectOffset { @@ -194,27 +171,17 @@ static ObjectOffset() ob_refcnt = OriginalObjectOffsets.ob_refcnt; ob_type = OriginalObjectOffsets.ob_type; - size = OriginalObjectOffsets.Size + ManagedDataOffsets.Size; + Size = OriginalObjectOffsets.Size + ManagedDataOffsets.Size; } public static int magic(IntPtr type) { - return ManagedDataOffsets.DataOffset(type); + return (int)Marshal.ReadIntPtr(type, TypeOffset.Size); } public static int TypeDictOffset(IntPtr type) { - return ManagedDataOffsets.DictOffset(type); - } - - public static int Size(IntPtr pyType) - { - if (IsException(pyType)) - { - return ExceptionOffset.Size(); - } - - return size; + return (int)Marshal.ReadIntPtr(type, TypeOffset.tp_dictoffset); } #if PYTHON_WITH_PYDEBUG @@ -223,15 +190,8 @@ public static int Size(IntPtr pyType) #endif public static int ob_refcnt; public static int ob_type; - private static readonly int size; - private static bool IsException(IntPtr pyObject) - { - var type = Runtime.PyObject_TYPE(pyObject); - return Runtime.PyType_IsSameAsOrSubtype(type, ofType: Exceptions.BaseException) - || Runtime.PyType_IsSameAsOrSubtype(type, ofType: Runtime.PyTypeType) - && Runtime.PyType_IsSubtype(pyObject, Exceptions.BaseException); - } + public static int Size { get; private set; } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] @@ -243,10 +203,9 @@ static ExceptionOffset() FieldInfo[] fi = type.GetFields(BindingFlags.Static | BindingFlags.Public); for (int i = 0; i < fi.Length; i++) { - fi[i].SetValue(null, (i * IntPtr.Size) + OriginalObjectOffsets.Size); + fi[i].SetValue(null, i * IntPtr.Size); } - - size = fi.Length * IntPtr.Size + OriginalObjectOffsets.Size + ManagedDataOffsets.Size; + size = fi.Length * IntPtr.Size; } public static int Size() { return size; } @@ -510,6 +469,9 @@ internal static Type GetPrototype(string name) return pmap[name] as Type; } + + internal static Dictionary allocatedThunks = new Dictionary(); + internal static ThunkInfo GetThunk(MethodInfo method, string funcType = null) { Type dt; @@ -524,6 +486,7 @@ internal static ThunkInfo GetThunk(MethodInfo method, string funcType = null) } Delegate d = Delegate.CreateDelegate(dt, method); var info = new ThunkInfo(d); + allocatedThunks[info.Address] = d; return info; } diff --git a/src/runtime/interop36.cs b/src/runtime/interop36.cs index d68539d56..4b3b8bfb9 100644 --- a/src/runtime/interop36.cs +++ b/src/runtime/interop36.cs @@ -2,224 +2,135 @@ // Auto-generated by geninterop.py. // DO NOT MODIFY BY HAND. +// Python 3.6: ABI flags: '' + +// ReSharper disable InconsistentNaming +// ReSharper disable IdentifierTypo -#if PYTHON36 using System; -using System.Collections; -using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using System.Reflection; -using System.Text; + +using Python.Runtime.Native; namespace Python.Runtime { + [SuppressMessage("Style", "IDE1006:Naming Styles", + Justification = "Following CPython", + Scope = "type")] [StructLayout(LayoutKind.Sequential)] - internal static partial class TypeOffset + internal class TypeOffset36 : GeneratedTypeOffsets, ITypeOffsets { + public TypeOffset36() { } // Auto-generated from PyHeapTypeObject in Python.h - public static int ob_refcnt = 0; - public static int ob_type = 0; - public static int ob_size = 0; - public static int tp_name = 0; - public static int tp_basicsize = 0; - public static int tp_itemsize = 0; - public static int tp_dealloc = 0; - public static int tp_print = 0; - public static int tp_getattr = 0; - public static int tp_setattr = 0; - public static int tp_as_async = 0; - public static int tp_repr = 0; - public static int tp_as_number = 0; - public static int tp_as_sequence = 0; - public static int tp_as_mapping = 0; - public static int tp_hash = 0; - public static int tp_call = 0; - public static int tp_str = 0; - public static int tp_getattro = 0; - public static int tp_setattro = 0; - public static int tp_as_buffer = 0; - public static int tp_flags = 0; - public static int tp_doc = 0; - public static int tp_traverse = 0; - public static int tp_clear = 0; - public static int tp_richcompare = 0; - public static int tp_weaklistoffset = 0; - public static int tp_iter = 0; - public static int tp_iternext = 0; - public static int tp_methods = 0; - public static int tp_members = 0; - public static int tp_getset = 0; - public static int tp_base = 0; - public static int tp_dict = 0; - public static int tp_descr_get = 0; - public static int tp_descr_set = 0; - public static int tp_dictoffset = 0; - public static int tp_init = 0; - public static int tp_alloc = 0; - public static int tp_new = 0; - public static int tp_free = 0; - public static int tp_is_gc = 0; - public static int tp_bases = 0; - public static int tp_mro = 0; - public static int tp_cache = 0; - public static int tp_subclasses = 0; - public static int tp_weaklist = 0; - public static int tp_del = 0; - public static int tp_version_tag = 0; - public static int tp_finalize = 0; - public static int am_await = 0; - public static int am_aiter = 0; - public static int am_anext = 0; - public static int nb_add = 0; - public static int nb_subtract = 0; - public static int nb_multiply = 0; - public static int nb_remainder = 0; - public static int nb_divmod = 0; - public static int nb_power = 0; - public static int nb_negative = 0; - public static int nb_positive = 0; - public static int nb_absolute = 0; - public static int nb_bool = 0; - public static int nb_invert = 0; - public static int nb_lshift = 0; - public static int nb_rshift = 0; - public static int nb_and = 0; - public static int nb_xor = 0; - public static int nb_or = 0; - public static int nb_int = 0; - public static int nb_reserved = 0; - public static int nb_float = 0; - public static int nb_inplace_add = 0; - public static int nb_inplace_subtract = 0; - public static int nb_inplace_multiply = 0; - public static int nb_inplace_remainder = 0; - public static int nb_inplace_power = 0; - public static int nb_inplace_lshift = 0; - public static int nb_inplace_rshift = 0; - public static int nb_inplace_and = 0; - public static int nb_inplace_xor = 0; - public static int nb_inplace_or = 0; - public static int nb_floor_divide = 0; - public static int nb_true_divide = 0; - public static int nb_inplace_floor_divide = 0; - public static int nb_inplace_true_divide = 0; - public static int nb_index = 0; - public static int nb_matrix_multiply = 0; - public static int nb_inplace_matrix_multiply = 0; - public static int mp_length = 0; - public static int mp_subscript = 0; - public static int mp_ass_subscript = 0; - public static int sq_length = 0; - public static int sq_concat = 0; - public static int sq_repeat = 0; - public static int sq_item = 0; - public static int was_sq_slice = 0; - public static int sq_ass_item = 0; - public static int was_sq_ass_slice = 0; - public static int sq_contains = 0; - public static int sq_inplace_concat = 0; - public static int sq_inplace_repeat = 0; - public static int bf_getbuffer = 0; - public static int bf_releasebuffer = 0; - public static int name = 0; - public static int ht_slots = 0; - public static int qualname = 0; - public static int ht_cached_keys = 0; - - /* here are optional user slots, followed by the members. */ - public static int members = 0; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyNumberMethods - { - public IntPtr nb_add; - public IntPtr nb_subtract; - public IntPtr nb_multiply; - public IntPtr nb_remainder; - public IntPtr nb_divmod; - public IntPtr nb_power; - public IntPtr nb_negative; - public IntPtr nb_positive; - public IntPtr nb_absolute; - public IntPtr nb_bool; - public IntPtr nb_invert; - public IntPtr nb_lshift; - public IntPtr nb_rshift; - public IntPtr nb_and; - public IntPtr nb_xor; - public IntPtr nb_or; - public IntPtr nb_int; - public IntPtr nb_reserved; - public IntPtr nb_float; - public IntPtr nb_inplace_add; - public IntPtr nb_inplace_subtract; - public IntPtr nb_inplace_multiply; - public IntPtr nb_inplace_remainder; - public IntPtr nb_inplace_power; - public IntPtr nb_inplace_lshift; - public IntPtr nb_inplace_rshift; - public IntPtr nb_inplace_and; - public IntPtr nb_inplace_xor; - public IntPtr nb_inplace_or; - public IntPtr nb_floor_divide; - public IntPtr nb_true_divide; - public IntPtr nb_inplace_floor_divide; - public IntPtr nb_inplace_true_divide; - public IntPtr nb_index; - public IntPtr nb_matrix_multiply; - public IntPtr nb_inplace_matrix_multiply; + public int ob_refcnt { get; private set; } + public int ob_type { get; private set; } + public int ob_size { get; private set; } + public int tp_name { get; private set; } + public int tp_basicsize { get; private set; } + public int tp_itemsize { get; private set; } + public int tp_dealloc { get; private set; } + public int tp_print { get; private set; } + public int tp_getattr { get; private set; } + public int tp_setattr { get; private set; } + public int tp_as_async { get; private set; } + public int tp_repr { get; private set; } + public int tp_as_number { get; private set; } + public int tp_as_sequence { get; private set; } + public int tp_as_mapping { get; private set; } + public int tp_hash { get; private set; } + public int tp_call { get; private set; } + public int tp_str { get; private set; } + public int tp_getattro { get; private set; } + public int tp_setattro { get; private set; } + public int tp_as_buffer { get; private set; } + public int tp_flags { get; private set; } + public int tp_doc { get; private set; } + public int tp_traverse { get; private set; } + public int tp_clear { get; private set; } + public int tp_richcompare { get; private set; } + public int tp_weaklistoffset { get; private set; } + public int tp_iter { get; private set; } + public int tp_iternext { get; private set; } + public int tp_methods { get; private set; } + public int tp_members { get; private set; } + public int tp_getset { get; private set; } + public int tp_base { get; private set; } + public int tp_dict { get; private set; } + public int tp_descr_get { get; private set; } + public int tp_descr_set { get; private set; } + public int tp_dictoffset { get; private set; } + public int tp_init { get; private set; } + public int tp_alloc { get; private set; } + public int tp_new { get; private set; } + public int tp_free { get; private set; } + public int tp_is_gc { get; private set; } + public int tp_bases { get; private set; } + public int tp_mro { get; private set; } + public int tp_cache { get; private set; } + public int tp_subclasses { get; private set; } + public int tp_weaklist { get; private set; } + public int tp_del { get; private set; } + public int tp_version_tag { get; private set; } + public int tp_finalize { get; private set; } + public int am_await { get; private set; } + public int am_aiter { get; private set; } + public int am_anext { get; private set; } + public int nb_add { get; private set; } + public int nb_subtract { get; private set; } + public int nb_multiply { get; private set; } + public int nb_remainder { get; private set; } + public int nb_divmod { get; private set; } + public int nb_power { get; private set; } + public int nb_negative { get; private set; } + public int nb_positive { get; private set; } + public int nb_absolute { get; private set; } + public int nb_bool { get; private set; } + public int nb_invert { get; private set; } + public int nb_lshift { get; private set; } + public int nb_rshift { get; private set; } + public int nb_and { get; private set; } + public int nb_xor { get; private set; } + public int nb_or { get; private set; } + public int nb_int { get; private set; } + public int nb_reserved { get; private set; } + public int nb_float { get; private set; } + public int nb_inplace_add { get; private set; } + public int nb_inplace_subtract { get; private set; } + public int nb_inplace_multiply { get; private set; } + public int nb_inplace_remainder { get; private set; } + public int nb_inplace_power { get; private set; } + public int nb_inplace_lshift { get; private set; } + public int nb_inplace_rshift { get; private set; } + public int nb_inplace_and { get; private set; } + public int nb_inplace_xor { get; private set; } + public int nb_inplace_or { get; private set; } + public int nb_floor_divide { get; private set; } + public int nb_true_divide { get; private set; } + public int nb_inplace_floor_divide { get; private set; } + public int nb_inplace_true_divide { get; private set; } + public int nb_index { get; private set; } + public int nb_matrix_multiply { get; private set; } + public int nb_inplace_matrix_multiply { get; private set; } + public int mp_length { get; private set; } + public int mp_subscript { get; private set; } + public int mp_ass_subscript { get; private set; } + public int sq_length { get; private set; } + public int sq_concat { get; private set; } + public int sq_repeat { get; private set; } + public int sq_item { get; private set; } + public int was_sq_slice { get; private set; } + public int sq_ass_item { get; private set; } + public int was_sq_ass_slice { get; private set; } + public int sq_contains { get; private set; } + public int sq_inplace_concat { get; private set; } + public int sq_inplace_repeat { get; private set; } + public int bf_getbuffer { get; private set; } + public int bf_releasebuffer { get; private set; } + public int name { get; private set; } + public int ht_slots { get; private set; } + public int qualname { get; private set; } + public int ht_cached_keys { get; private set; } } - - [StructLayout(LayoutKind.Sequential)] - internal struct PySequenceMethods - { - public IntPtr sq_length; - public IntPtr sq_concat; - public IntPtr sq_repeat; - public IntPtr sq_item; - public IntPtr was_sq_slice; - public IntPtr sq_ass_item; - public IntPtr was_sq_ass_slice; - public IntPtr sq_contains; - public IntPtr sq_inplace_concat; - public IntPtr sq_inplace_repeat; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyMappingMethods - { - public IntPtr mp_length; - public IntPtr mp_subscript; - public IntPtr mp_ass_subscript; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyAsyncMethods - { - public IntPtr am_await; - public IntPtr am_aiter; - public IntPtr am_anext; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyBufferProcs - { - public IntPtr bf_getbuffer; - public IntPtr bf_releasebuffer; - } - - internal static partial class SlotTypes - { - public static readonly Type[] Types = { - typeof(PyNumberMethods), - typeof(PySequenceMethods), - typeof(PyMappingMethods), - typeof(PyAsyncMethods), - typeof(PyBufferProcs), - }; - } - } -#endif diff --git a/src/runtime/interop37.cs b/src/runtime/interop37.cs index c85d06525..951cb1068 100644 --- a/src/runtime/interop37.cs +++ b/src/runtime/interop37.cs @@ -2,224 +2,135 @@ // Auto-generated by geninterop.py. // DO NOT MODIFY BY HAND. +// Python 3.7: ABI flags: '' + +// ReSharper disable InconsistentNaming +// ReSharper disable IdentifierTypo -#if PYTHON37 using System; -using System.Collections; -using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using System.Reflection; -using System.Text; + +using Python.Runtime.Native; namespace Python.Runtime { + [SuppressMessage("Style", "IDE1006:Naming Styles", + Justification = "Following CPython", + Scope = "type")] [StructLayout(LayoutKind.Sequential)] - internal static partial class TypeOffset + internal class TypeOffset37 : GeneratedTypeOffsets, ITypeOffsets { + public TypeOffset37() { } // Auto-generated from PyHeapTypeObject in Python.h - public static int ob_refcnt = 0; - public static int ob_type = 0; - public static int ob_size = 0; - public static int tp_name = 0; - public static int tp_basicsize = 0; - public static int tp_itemsize = 0; - public static int tp_dealloc = 0; - public static int tp_print = 0; - public static int tp_getattr = 0; - public static int tp_setattr = 0; - public static int tp_as_async = 0; - public static int tp_repr = 0; - public static int tp_as_number = 0; - public static int tp_as_sequence = 0; - public static int tp_as_mapping = 0; - public static int tp_hash = 0; - public static int tp_call = 0; - public static int tp_str = 0; - public static int tp_getattro = 0; - public static int tp_setattro = 0; - public static int tp_as_buffer = 0; - public static int tp_flags = 0; - public static int tp_doc = 0; - public static int tp_traverse = 0; - public static int tp_clear = 0; - public static int tp_richcompare = 0; - public static int tp_weaklistoffset = 0; - public static int tp_iter = 0; - public static int tp_iternext = 0; - public static int tp_methods = 0; - public static int tp_members = 0; - public static int tp_getset = 0; - public static int tp_base = 0; - public static int tp_dict = 0; - public static int tp_descr_get = 0; - public static int tp_descr_set = 0; - public static int tp_dictoffset = 0; - public static int tp_init = 0; - public static int tp_alloc = 0; - public static int tp_new = 0; - public static int tp_free = 0; - public static int tp_is_gc = 0; - public static int tp_bases = 0; - public static int tp_mro = 0; - public static int tp_cache = 0; - public static int tp_subclasses = 0; - public static int tp_weaklist = 0; - public static int tp_del = 0; - public static int tp_version_tag = 0; - public static int tp_finalize = 0; - public static int am_await = 0; - public static int am_aiter = 0; - public static int am_anext = 0; - public static int nb_add = 0; - public static int nb_subtract = 0; - public static int nb_multiply = 0; - public static int nb_remainder = 0; - public static int nb_divmod = 0; - public static int nb_power = 0; - public static int nb_negative = 0; - public static int nb_positive = 0; - public static int nb_absolute = 0; - public static int nb_bool = 0; - public static int nb_invert = 0; - public static int nb_lshift = 0; - public static int nb_rshift = 0; - public static int nb_and = 0; - public static int nb_xor = 0; - public static int nb_or = 0; - public static int nb_int = 0; - public static int nb_reserved = 0; - public static int nb_float = 0; - public static int nb_inplace_add = 0; - public static int nb_inplace_subtract = 0; - public static int nb_inplace_multiply = 0; - public static int nb_inplace_remainder = 0; - public static int nb_inplace_power = 0; - public static int nb_inplace_lshift = 0; - public static int nb_inplace_rshift = 0; - public static int nb_inplace_and = 0; - public static int nb_inplace_xor = 0; - public static int nb_inplace_or = 0; - public static int nb_floor_divide = 0; - public static int nb_true_divide = 0; - public static int nb_inplace_floor_divide = 0; - public static int nb_inplace_true_divide = 0; - public static int nb_index = 0; - public static int nb_matrix_multiply = 0; - public static int nb_inplace_matrix_multiply = 0; - public static int mp_length = 0; - public static int mp_subscript = 0; - public static int mp_ass_subscript = 0; - public static int sq_length = 0; - public static int sq_concat = 0; - public static int sq_repeat = 0; - public static int sq_item = 0; - public static int was_sq_slice = 0; - public static int sq_ass_item = 0; - public static int was_sq_ass_slice = 0; - public static int sq_contains = 0; - public static int sq_inplace_concat = 0; - public static int sq_inplace_repeat = 0; - public static int bf_getbuffer = 0; - public static int bf_releasebuffer = 0; - public static int name = 0; - public static int ht_slots = 0; - public static int qualname = 0; - public static int ht_cached_keys = 0; - - /* here are optional user slots, followed by the members. */ - public static int members = 0; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyNumberMethods - { - public IntPtr nb_add; - public IntPtr nb_subtract; - public IntPtr nb_multiply; - public IntPtr nb_remainder; - public IntPtr nb_divmod; - public IntPtr nb_power; - public IntPtr nb_negative; - public IntPtr nb_positive; - public IntPtr nb_absolute; - public IntPtr nb_bool; - public IntPtr nb_invert; - public IntPtr nb_lshift; - public IntPtr nb_rshift; - public IntPtr nb_and; - public IntPtr nb_xor; - public IntPtr nb_or; - public IntPtr nb_int; - public IntPtr nb_reserved; - public IntPtr nb_float; - public IntPtr nb_inplace_add; - public IntPtr nb_inplace_subtract; - public IntPtr nb_inplace_multiply; - public IntPtr nb_inplace_remainder; - public IntPtr nb_inplace_power; - public IntPtr nb_inplace_lshift; - public IntPtr nb_inplace_rshift; - public IntPtr nb_inplace_and; - public IntPtr nb_inplace_xor; - public IntPtr nb_inplace_or; - public IntPtr nb_floor_divide; - public IntPtr nb_true_divide; - public IntPtr nb_inplace_floor_divide; - public IntPtr nb_inplace_true_divide; - public IntPtr nb_index; - public IntPtr nb_matrix_multiply; - public IntPtr nb_inplace_matrix_multiply; + public int ob_refcnt { get; private set; } + public int ob_type { get; private set; } + public int ob_size { get; private set; } + public int tp_name { get; private set; } + public int tp_basicsize { get; private set; } + public int tp_itemsize { get; private set; } + public int tp_dealloc { get; private set; } + public int tp_print { get; private set; } + public int tp_getattr { get; private set; } + public int tp_setattr { get; private set; } + public int tp_as_async { get; private set; } + public int tp_repr { get; private set; } + public int tp_as_number { get; private set; } + public int tp_as_sequence { get; private set; } + public int tp_as_mapping { get; private set; } + public int tp_hash { get; private set; } + public int tp_call { get; private set; } + public int tp_str { get; private set; } + public int tp_getattro { get; private set; } + public int tp_setattro { get; private set; } + public int tp_as_buffer { get; private set; } + public int tp_flags { get; private set; } + public int tp_doc { get; private set; } + public int tp_traverse { get; private set; } + public int tp_clear { get; private set; } + public int tp_richcompare { get; private set; } + public int tp_weaklistoffset { get; private set; } + public int tp_iter { get; private set; } + public int tp_iternext { get; private set; } + public int tp_methods { get; private set; } + public int tp_members { get; private set; } + public int tp_getset { get; private set; } + public int tp_base { get; private set; } + public int tp_dict { get; private set; } + public int tp_descr_get { get; private set; } + public int tp_descr_set { get; private set; } + public int tp_dictoffset { get; private set; } + public int tp_init { get; private set; } + public int tp_alloc { get; private set; } + public int tp_new { get; private set; } + public int tp_free { get; private set; } + public int tp_is_gc { get; private set; } + public int tp_bases { get; private set; } + public int tp_mro { get; private set; } + public int tp_cache { get; private set; } + public int tp_subclasses { get; private set; } + public int tp_weaklist { get; private set; } + public int tp_del { get; private set; } + public int tp_version_tag { get; private set; } + public int tp_finalize { get; private set; } + public int am_await { get; private set; } + public int am_aiter { get; private set; } + public int am_anext { get; private set; } + public int nb_add { get; private set; } + public int nb_subtract { get; private set; } + public int nb_multiply { get; private set; } + public int nb_remainder { get; private set; } + public int nb_divmod { get; private set; } + public int nb_power { get; private set; } + public int nb_negative { get; private set; } + public int nb_positive { get; private set; } + public int nb_absolute { get; private set; } + public int nb_bool { get; private set; } + public int nb_invert { get; private set; } + public int nb_lshift { get; private set; } + public int nb_rshift { get; private set; } + public int nb_and { get; private set; } + public int nb_xor { get; private set; } + public int nb_or { get; private set; } + public int nb_int { get; private set; } + public int nb_reserved { get; private set; } + public int nb_float { get; private set; } + public int nb_inplace_add { get; private set; } + public int nb_inplace_subtract { get; private set; } + public int nb_inplace_multiply { get; private set; } + public int nb_inplace_remainder { get; private set; } + public int nb_inplace_power { get; private set; } + public int nb_inplace_lshift { get; private set; } + public int nb_inplace_rshift { get; private set; } + public int nb_inplace_and { get; private set; } + public int nb_inplace_xor { get; private set; } + public int nb_inplace_or { get; private set; } + public int nb_floor_divide { get; private set; } + public int nb_true_divide { get; private set; } + public int nb_inplace_floor_divide { get; private set; } + public int nb_inplace_true_divide { get; private set; } + public int nb_index { get; private set; } + public int nb_matrix_multiply { get; private set; } + public int nb_inplace_matrix_multiply { get; private set; } + public int mp_length { get; private set; } + public int mp_subscript { get; private set; } + public int mp_ass_subscript { get; private set; } + public int sq_length { get; private set; } + public int sq_concat { get; private set; } + public int sq_repeat { get; private set; } + public int sq_item { get; private set; } + public int was_sq_slice { get; private set; } + public int sq_ass_item { get; private set; } + public int was_sq_ass_slice { get; private set; } + public int sq_contains { get; private set; } + public int sq_inplace_concat { get; private set; } + public int sq_inplace_repeat { get; private set; } + public int bf_getbuffer { get; private set; } + public int bf_releasebuffer { get; private set; } + public int name { get; private set; } + public int ht_slots { get; private set; } + public int qualname { get; private set; } + public int ht_cached_keys { get; private set; } } - - [StructLayout(LayoutKind.Sequential)] - internal struct PySequenceMethods - { - public IntPtr sq_length; - public IntPtr sq_concat; - public IntPtr sq_repeat; - public IntPtr sq_item; - public IntPtr was_sq_slice; - public IntPtr sq_ass_item; - public IntPtr was_sq_ass_slice; - public IntPtr sq_contains; - public IntPtr sq_inplace_concat; - public IntPtr sq_inplace_repeat; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyMappingMethods - { - public IntPtr mp_length; - public IntPtr mp_subscript; - public IntPtr mp_ass_subscript; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyAsyncMethods - { - public IntPtr am_await; - public IntPtr am_aiter; - public IntPtr am_anext; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyBufferProcs - { - public IntPtr bf_getbuffer; - public IntPtr bf_releasebuffer; - } - - internal static partial class SlotTypes - { - public static readonly Type[] Types = { - typeof(PyNumberMethods), - typeof(PySequenceMethods), - typeof(PyMappingMethods), - typeof(PyAsyncMethods), - typeof(PyBufferProcs), - }; - } - } -#endif diff --git a/src/runtime/interop38.cs b/src/runtime/interop38.cs index a87573e90..67a40eabd 100644 --- a/src/runtime/interop38.cs +++ b/src/runtime/interop38.cs @@ -2,226 +2,137 @@ // Auto-generated by geninterop.py. // DO NOT MODIFY BY HAND. +// Python 3.8: ABI flags: '' + +// ReSharper disable InconsistentNaming +// ReSharper disable IdentifierTypo -#if PYTHON38 using System; -using System.Collections; -using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using System.Reflection; -using System.Text; + +using Python.Runtime.Native; namespace Python.Runtime { + [SuppressMessage("Style", "IDE1006:Naming Styles", + Justification = "Following CPython", + Scope = "type")] [StructLayout(LayoutKind.Sequential)] - internal static partial class TypeOffset + internal class TypeOffset38 : GeneratedTypeOffsets, ITypeOffsets { + public TypeOffset38() { } // Auto-generated from PyHeapTypeObject in Python.h - public static int ob_refcnt = 0; - public static int ob_type = 0; - public static int ob_size = 0; - public static int tp_name = 0; - public static int tp_basicsize = 0; - public static int tp_itemsize = 0; - public static int tp_dealloc = 0; - public static int tp_vectorcall_offset = 0; - public static int tp_getattr = 0; - public static int tp_setattr = 0; - public static int tp_as_async = 0; - public static int tp_repr = 0; - public static int tp_as_number = 0; - public static int tp_as_sequence = 0; - public static int tp_as_mapping = 0; - public static int tp_hash = 0; - public static int tp_call = 0; - public static int tp_str = 0; - public static int tp_getattro = 0; - public static int tp_setattro = 0; - public static int tp_as_buffer = 0; - public static int tp_flags = 0; - public static int tp_doc = 0; - public static int tp_traverse = 0; - public static int tp_clear = 0; - public static int tp_richcompare = 0; - public static int tp_weaklistoffset = 0; - public static int tp_iter = 0; - public static int tp_iternext = 0; - public static int tp_methods = 0; - public static int tp_members = 0; - public static int tp_getset = 0; - public static int tp_base = 0; - public static int tp_dict = 0; - public static int tp_descr_get = 0; - public static int tp_descr_set = 0; - public static int tp_dictoffset = 0; - public static int tp_init = 0; - public static int tp_alloc = 0; - public static int tp_new = 0; - public static int tp_free = 0; - public static int tp_is_gc = 0; - public static int tp_bases = 0; - public static int tp_mro = 0; - public static int tp_cache = 0; - public static int tp_subclasses = 0; - public static int tp_weaklist = 0; - public static int tp_del = 0; - public static int tp_version_tag = 0; - public static int tp_finalize = 0; - public static int tp_vectorcall = 0; - public static int tp_print = 0; - public static int am_await = 0; - public static int am_aiter = 0; - public static int am_anext = 0; - public static int nb_add = 0; - public static int nb_subtract = 0; - public static int nb_multiply = 0; - public static int nb_remainder = 0; - public static int nb_divmod = 0; - public static int nb_power = 0; - public static int nb_negative = 0; - public static int nb_positive = 0; - public static int nb_absolute = 0; - public static int nb_bool = 0; - public static int nb_invert = 0; - public static int nb_lshift = 0; - public static int nb_rshift = 0; - public static int nb_and = 0; - public static int nb_xor = 0; - public static int nb_or = 0; - public static int nb_int = 0; - public static int nb_reserved = 0; - public static int nb_float = 0; - public static int nb_inplace_add = 0; - public static int nb_inplace_subtract = 0; - public static int nb_inplace_multiply = 0; - public static int nb_inplace_remainder = 0; - public static int nb_inplace_power = 0; - public static int nb_inplace_lshift = 0; - public static int nb_inplace_rshift = 0; - public static int nb_inplace_and = 0; - public static int nb_inplace_xor = 0; - public static int nb_inplace_or = 0; - public static int nb_floor_divide = 0; - public static int nb_true_divide = 0; - public static int nb_inplace_floor_divide = 0; - public static int nb_inplace_true_divide = 0; - public static int nb_index = 0; - public static int nb_matrix_multiply = 0; - public static int nb_inplace_matrix_multiply = 0; - public static int mp_length = 0; - public static int mp_subscript = 0; - public static int mp_ass_subscript = 0; - public static int sq_length = 0; - public static int sq_concat = 0; - public static int sq_repeat = 0; - public static int sq_item = 0; - public static int was_sq_slice = 0; - public static int sq_ass_item = 0; - public static int was_sq_ass_slice = 0; - public static int sq_contains = 0; - public static int sq_inplace_concat = 0; - public static int sq_inplace_repeat = 0; - public static int bf_getbuffer = 0; - public static int bf_releasebuffer = 0; - public static int name = 0; - public static int ht_slots = 0; - public static int qualname = 0; - public static int ht_cached_keys = 0; - - /* here are optional user slots, followed by the members. */ - public static int members = 0; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyNumberMethods - { - public IntPtr nb_add; - public IntPtr nb_subtract; - public IntPtr nb_multiply; - public IntPtr nb_remainder; - public IntPtr nb_divmod; - public IntPtr nb_power; - public IntPtr nb_negative; - public IntPtr nb_positive; - public IntPtr nb_absolute; - public IntPtr nb_bool; - public IntPtr nb_invert; - public IntPtr nb_lshift; - public IntPtr nb_rshift; - public IntPtr nb_and; - public IntPtr nb_xor; - public IntPtr nb_or; - public IntPtr nb_int; - public IntPtr nb_reserved; - public IntPtr nb_float; - public IntPtr nb_inplace_add; - public IntPtr nb_inplace_subtract; - public IntPtr nb_inplace_multiply; - public IntPtr nb_inplace_remainder; - public IntPtr nb_inplace_power; - public IntPtr nb_inplace_lshift; - public IntPtr nb_inplace_rshift; - public IntPtr nb_inplace_and; - public IntPtr nb_inplace_xor; - public IntPtr nb_inplace_or; - public IntPtr nb_floor_divide; - public IntPtr nb_true_divide; - public IntPtr nb_inplace_floor_divide; - public IntPtr nb_inplace_true_divide; - public IntPtr nb_index; - public IntPtr nb_matrix_multiply; - public IntPtr nb_inplace_matrix_multiply; + public int ob_refcnt { get; private set; } + public int ob_type { get; private set; } + public int ob_size { get; private set; } + public int tp_name { get; private set; } + public int tp_basicsize { get; private set; } + public int tp_itemsize { get; private set; } + public int tp_dealloc { get; private set; } + public int tp_vectorcall_offset { get; private set; } + public int tp_getattr { get; private set; } + public int tp_setattr { get; private set; } + public int tp_as_async { get; private set; } + public int tp_repr { get; private set; } + public int tp_as_number { get; private set; } + public int tp_as_sequence { get; private set; } + public int tp_as_mapping { get; private set; } + public int tp_hash { get; private set; } + public int tp_call { get; private set; } + public int tp_str { get; private set; } + public int tp_getattro { get; private set; } + public int tp_setattro { get; private set; } + public int tp_as_buffer { get; private set; } + public int tp_flags { get; private set; } + public int tp_doc { get; private set; } + public int tp_traverse { get; private set; } + public int tp_clear { get; private set; } + public int tp_richcompare { get; private set; } + public int tp_weaklistoffset { get; private set; } + public int tp_iter { get; private set; } + public int tp_iternext { get; private set; } + public int tp_methods { get; private set; } + public int tp_members { get; private set; } + public int tp_getset { get; private set; } + public int tp_base { get; private set; } + public int tp_dict { get; private set; } + public int tp_descr_get { get; private set; } + public int tp_descr_set { get; private set; } + public int tp_dictoffset { get; private set; } + public int tp_init { get; private set; } + public int tp_alloc { get; private set; } + public int tp_new { get; private set; } + public int tp_free { get; private set; } + public int tp_is_gc { get; private set; } + public int tp_bases { get; private set; } + public int tp_mro { get; private set; } + public int tp_cache { get; private set; } + public int tp_subclasses { get; private set; } + public int tp_weaklist { get; private set; } + public int tp_del { get; private set; } + public int tp_version_tag { get; private set; } + public int tp_finalize { get; private set; } + public int tp_vectorcall { get; private set; } + public int tp_print { get; private set; } + public int am_await { get; private set; } + public int am_aiter { get; private set; } + public int am_anext { get; private set; } + public int nb_add { get; private set; } + public int nb_subtract { get; private set; } + public int nb_multiply { get; private set; } + public int nb_remainder { get; private set; } + public int nb_divmod { get; private set; } + public int nb_power { get; private set; } + public int nb_negative { get; private set; } + public int nb_positive { get; private set; } + public int nb_absolute { get; private set; } + public int nb_bool { get; private set; } + public int nb_invert { get; private set; } + public int nb_lshift { get; private set; } + public int nb_rshift { get; private set; } + public int nb_and { get; private set; } + public int nb_xor { get; private set; } + public int nb_or { get; private set; } + public int nb_int { get; private set; } + public int nb_reserved { get; private set; } + public int nb_float { get; private set; } + public int nb_inplace_add { get; private set; } + public int nb_inplace_subtract { get; private set; } + public int nb_inplace_multiply { get; private set; } + public int nb_inplace_remainder { get; private set; } + public int nb_inplace_power { get; private set; } + public int nb_inplace_lshift { get; private set; } + public int nb_inplace_rshift { get; private set; } + public int nb_inplace_and { get; private set; } + public int nb_inplace_xor { get; private set; } + public int nb_inplace_or { get; private set; } + public int nb_floor_divide { get; private set; } + public int nb_true_divide { get; private set; } + public int nb_inplace_floor_divide { get; private set; } + public int nb_inplace_true_divide { get; private set; } + public int nb_index { get; private set; } + public int nb_matrix_multiply { get; private set; } + public int nb_inplace_matrix_multiply { get; private set; } + public int mp_length { get; private set; } + public int mp_subscript { get; private set; } + public int mp_ass_subscript { get; private set; } + public int sq_length { get; private set; } + public int sq_concat { get; private set; } + public int sq_repeat { get; private set; } + public int sq_item { get; private set; } + public int was_sq_slice { get; private set; } + public int sq_ass_item { get; private set; } + public int was_sq_ass_slice { get; private set; } + public int sq_contains { get; private set; } + public int sq_inplace_concat { get; private set; } + public int sq_inplace_repeat { get; private set; } + public int bf_getbuffer { get; private set; } + public int bf_releasebuffer { get; private set; } + public int name { get; private set; } + public int ht_slots { get; private set; } + public int qualname { get; private set; } + public int ht_cached_keys { get; private set; } } - - [StructLayout(LayoutKind.Sequential)] - internal struct PySequenceMethods - { - public IntPtr sq_length; - public IntPtr sq_concat; - public IntPtr sq_repeat; - public IntPtr sq_item; - public IntPtr was_sq_slice; - public IntPtr sq_ass_item; - public IntPtr was_sq_ass_slice; - public IntPtr sq_contains; - public IntPtr sq_inplace_concat; - public IntPtr sq_inplace_repeat; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyMappingMethods - { - public IntPtr mp_length; - public IntPtr mp_subscript; - public IntPtr mp_ass_subscript; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyAsyncMethods - { - public IntPtr am_await; - public IntPtr am_aiter; - public IntPtr am_anext; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PyBufferProcs - { - public IntPtr bf_getbuffer; - public IntPtr bf_releasebuffer; - } - - internal static partial class SlotTypes - { - public static readonly Type[] Types = { - typeof(PyNumberMethods), - typeof(PySequenceMethods), - typeof(PyMappingMethods), - typeof(PyAsyncMethods), - typeof(PyBufferProcs), - }; - } - } -#endif diff --git a/src/runtime/interop39.cs b/src/runtime/interop39.cs new file mode 100644 index 000000000..cf3acc984 --- /dev/null +++ b/src/runtime/interop39.cs @@ -0,0 +1,138 @@ + +// Auto-generated by geninterop.py. +// DO NOT MODIFY BY HAND. + +// Python 3.9: ABI flags: '' + +// ReSharper disable InconsistentNaming +// ReSharper disable IdentifierTypo + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +using Python.Runtime.Native; + +namespace Python.Runtime +{ + [SuppressMessage("Style", "IDE1006:Naming Styles", + Justification = "Following CPython", + Scope = "type")] + + [StructLayout(LayoutKind.Sequential)] + internal class TypeOffset39 : GeneratedTypeOffsets, ITypeOffsets + { + public TypeOffset39() { } + // Auto-generated from PyHeapTypeObject in Python.h + public int ob_refcnt { get; private set; } + public int ob_type { get; private set; } + public int ob_size { get; private set; } + public int tp_name { get; private set; } + public int tp_basicsize { get; private set; } + public int tp_itemsize { get; private set; } + public int tp_dealloc { get; private set; } + public int tp_vectorcall_offset { get; private set; } + public int tp_getattr { get; private set; } + public int tp_setattr { get; private set; } + public int tp_as_async { get; private set; } + public int tp_repr { get; private set; } + public int tp_as_number { get; private set; } + public int tp_as_sequence { get; private set; } + public int tp_as_mapping { get; private set; } + public int tp_hash { get; private set; } + public int tp_call { get; private set; } + public int tp_str { get; private set; } + public int tp_getattro { get; private set; } + public int tp_setattro { get; private set; } + public int tp_as_buffer { get; private set; } + public int tp_flags { get; private set; } + public int tp_doc { get; private set; } + public int tp_traverse { get; private set; } + public int tp_clear { get; private set; } + public int tp_richcompare { get; private set; } + public int tp_weaklistoffset { get; private set; } + public int tp_iter { get; private set; } + public int tp_iternext { get; private set; } + public int tp_methods { get; private set; } + public int tp_members { get; private set; } + public int tp_getset { get; private set; } + public int tp_base { get; private set; } + public int tp_dict { get; private set; } + public int tp_descr_get { get; private set; } + public int tp_descr_set { get; private set; } + public int tp_dictoffset { get; private set; } + public int tp_init { get; private set; } + public int tp_alloc { get; private set; } + public int tp_new { get; private set; } + public int tp_free { get; private set; } + public int tp_is_gc { get; private set; } + public int tp_bases { get; private set; } + public int tp_mro { get; private set; } + public int tp_cache { get; private set; } + public int tp_subclasses { get; private set; } + public int tp_weaklist { get; private set; } + public int tp_del { get; private set; } + public int tp_version_tag { get; private set; } + public int tp_finalize { get; private set; } + public int tp_vectorcall { get; private set; } + public int am_await { get; private set; } + public int am_aiter { get; private set; } + public int am_anext { get; private set; } + public int nb_add { get; private set; } + public int nb_subtract { get; private set; } + public int nb_multiply { get; private set; } + public int nb_remainder { get; private set; } + public int nb_divmod { get; private set; } + public int nb_power { get; private set; } + public int nb_negative { get; private set; } + public int nb_positive { get; private set; } + public int nb_absolute { get; private set; } + public int nb_bool { get; private set; } + public int nb_invert { get; private set; } + public int nb_lshift { get; private set; } + public int nb_rshift { get; private set; } + public int nb_and { get; private set; } + public int nb_xor { get; private set; } + public int nb_or { get; private set; } + public int nb_int { get; private set; } + public int nb_reserved { get; private set; } + public int nb_float { get; private set; } + public int nb_inplace_add { get; private set; } + public int nb_inplace_subtract { get; private set; } + public int nb_inplace_multiply { get; private set; } + public int nb_inplace_remainder { get; private set; } + public int nb_inplace_power { get; private set; } + public int nb_inplace_lshift { get; private set; } + public int nb_inplace_rshift { get; private set; } + public int nb_inplace_and { get; private set; } + public int nb_inplace_xor { get; private set; } + public int nb_inplace_or { get; private set; } + public int nb_floor_divide { get; private set; } + public int nb_true_divide { get; private set; } + public int nb_inplace_floor_divide { get; private set; } + public int nb_inplace_true_divide { get; private set; } + public int nb_index { get; private set; } + public int nb_matrix_multiply { get; private set; } + public int nb_inplace_matrix_multiply { get; private set; } + public int mp_length { get; private set; } + public int mp_subscript { get; private set; } + public int mp_ass_subscript { get; private set; } + public int sq_length { get; private set; } + public int sq_concat { get; private set; } + public int sq_repeat { get; private set; } + public int sq_item { get; private set; } + public int was_sq_slice { get; private set; } + public int sq_ass_item { get; private set; } + public int was_sq_ass_slice { get; private set; } + public int sq_contains { get; private set; } + public int sq_inplace_concat { get; private set; } + public int sq_inplace_repeat { get; private set; } + public int bf_getbuffer { get; private set; } + public int bf_releasebuffer { get; private set; } + public int name { get; private set; } + public int ht_slots { get; private set; } + public int qualname { get; private set; } + public int ht_cached_keys { get; private set; } + public int ht_module { get; private set; } + } +} diff --git a/src/runtime/iterator.cs b/src/runtime/iterator.cs index f9cf10178..089e8538a 100644 --- a/src/runtime/iterator.cs +++ b/src/runtime/iterator.cs @@ -10,10 +10,12 @@ namespace Python.Runtime internal class Iterator : ExtensionType { private IEnumerator iter; + private Type elemType; - public Iterator(IEnumerator e) + public Iterator(IEnumerator e, Type elemType) { iter = e; + this.elemType = elemType; } @@ -41,7 +43,7 @@ public static IntPtr tp_iternext(IntPtr ob) return IntPtr.Zero; } object item = self.iter.Current; - return Converter.ToPythonImplicit(item); + return Converter.ToPython(item, self.elemType); } public static IntPtr tp_iter(IntPtr ob) diff --git a/src/runtime/loader.cs b/src/runtime/loader.cs new file mode 100644 index 000000000..d5f31b247 --- /dev/null +++ b/src/runtime/loader.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace Python.Runtime +{ + using static Runtime; + + [Obsolete("Only to be used from within Python")] + static class Loader + { + public unsafe static int Initialize(IntPtr data, int size) + { + IntPtr gs = IntPtr.Zero; + try + { + var dllPath = Encoding.UTF8.GetString((byte*)data.ToPointer(), size); + + if (!string.IsNullOrEmpty(dllPath)) + { + PythonDLL = dllPath; + } + else + { + PythonDLL = null; + } + + gs = PyGILState_Ensure(); + + // Console.WriteLine("Startup thread"); + PythonEngine.InitExt(); + // Console.WriteLine("Startup finished"); + } + catch (Exception exc) + { + Console.Error.Write( + $"Failed to initialize pythonnet: {exc}\n{exc.StackTrace}" + ); + return 1; + } + finally + { + if (gs != IntPtr.Zero) + { + PyGILState_Release(gs); + } + } + return 0; + } + + public unsafe static int Shutdown(IntPtr data, int size) + { + IntPtr gs = IntPtr.Zero; + try + { + var command = Encoding.UTF8.GetString((byte*)data.ToPointer(), size); + + if (command == "full_shutdown") + { + gs = PyGILState_Ensure(); + PythonEngine.Shutdown(); + } + } + catch (Exception exc) + { + Console.Error.Write( + $"Failed to shutdown pythonnet: {exc}\n{exc.StackTrace}" + ); + return 1; + } + finally + { + if (gs != IntPtr.Zero) + { + PyGILState_Release(gs); + } + } + return 0; + } + } +} diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs index bc2805d80..12aa7104f 100644 --- a/src/runtime/managedtype.cs +++ b/src/runtime/managedtype.cs @@ -26,6 +26,7 @@ internal enum TrackTypes internal IntPtr pyHandle; // PyObject * internal IntPtr tpHandle; // PyType * + internal BorrowedReference ObjectReference => new BorrowedReference(pyHandle); private static readonly Dictionary _managedObjs = new Dictionary(); @@ -75,34 +76,35 @@ internal void FreeGCHandle() } } + internal static ManagedType GetManagedObject(BorrowedReference ob) + => GetManagedObject(ob.DangerousGetAddress()); /// /// Given a Python object, return the associated managed object or null. /// internal static ManagedType GetManagedObject(IntPtr ob) { - if (ob != IntPtr.Zero) + if (ob == IntPtr.Zero) { - IntPtr tp = Runtime.PyObject_TYPE(ob); - if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType) - { - tp = ob; - } - - 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(tp)); - if (op == IntPtr.Zero) - { - return null; - } - var gc = (GCHandle)op; - return (ManagedType)gc.Target; - } + return null; } - return null; + IntPtr tp = Runtime.PyObject_TYPE(ob); + var flags = Util.ReadCLong(tp, TypeOffset.tp_flags); + if ((flags & TypeFlags.Managed) == 0) + { + return null; + } + int offset = ObjectOffset.magic(tp); + if (offset == 0) + { + return null; + } + IntPtr op = Marshal.ReadIntPtr(ob, offset); + if (op == IntPtr.Zero) + { + return null; + } + var gc = (GCHandle)op; + return (ManagedType)gc.Target; } /// @@ -110,18 +112,12 @@ internal static ManagedType GetManagedObject(IntPtr ob) /// internal static ManagedType GetManagedObjectType(IntPtr ob) { - if (ob != IntPtr.Zero) + if (ob == IntPtr.Zero) { - IntPtr tp = Runtime.PyObject_TYPE(ob); - var flags = Util.ReadCLong(tp, TypeOffset.tp_flags); - if ((flags & TypeFlags.Managed) != 0) - { - tp = Marshal.ReadIntPtr(tp, TypeOffset.magic()); - var gc = (GCHandle)tp; - return (ManagedType)gc.Target; - } + return null; } - return null; + IntPtr tp = Runtime.PyObject_TYPE(ob); + return GetManagedObject(tp); } @@ -136,16 +132,13 @@ internal static ManagedType GetManagedObjectErr(IntPtr ob) } + internal static bool IsManagedType(BorrowedReference ob) + => IsManagedType(ob.DangerousGetAddressOrNull()); internal static bool IsManagedType(IntPtr ob) { if (ob != IntPtr.Zero) { IntPtr tp = Runtime.PyObject_TYPE(ob); - if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType) - { - tp = ob; - } - var flags = Util.ReadCLong(tp, TypeOffset.tp_flags); if ((flags & TypeFlags.Managed) != 0) { @@ -155,11 +148,6 @@ internal static bool IsManagedType(IntPtr ob) return false; } - public bool IsTypeObject() - { - return pyHandle == tpHandle; - } - internal static IDictionary GetManagedObjects() { return _managedObjs; @@ -176,7 +164,7 @@ internal static int PyVisit(IntPtr ob, IntPtr visit, IntPtr arg) { return 0; } - var visitFunc = (Interop.ObjObjFunc)Marshal.GetDelegateForFunctionPointer(visit, typeof(Interop.ObjObjFunc)); + var visitFunc = NativeCall.GetDelegate(visit); return visitFunc(ob, arg); } @@ -194,7 +182,7 @@ internal void CallTypeClear() { return; } - var clearFunc = (Interop.InquiryFunc)Marshal.GetDelegateForFunctionPointer(clearPtr, typeof(Interop.InquiryFunc)); + var clearFunc = NativeCall.GetDelegate(clearPtr); clearFunc(pyHandle); } @@ -212,7 +200,8 @@ internal void CallTypeTraverse(Interop.ObjObjFunc visitproc, IntPtr arg) { return; } - var traverseFunc = (Interop.ObjObjArgFunc)Marshal.GetDelegateForFunctionPointer(traversePtr, typeof(Interop.ObjObjArgFunc)); + var traverseFunc = NativeCall.GetDelegate(traversePtr); + var visiPtr = Marshal.GetFunctionPointerForDelegate(visitproc); traverseFunc(pyHandle, visiPtr, arg); } @@ -236,6 +225,16 @@ protected virtual void OnSave(InterDomainContext context) { } protected virtual void OnLoad(InterDomainContext context) { } protected static void ClearObjectDict(IntPtr ob) + { + IntPtr dict = GetObjectDict(ob); + if (dict == IntPtr.Zero) + { + return; + } + Runtime.PyDict_Clear(dict); + } + + protected static void RemoveObjectDict(IntPtr ob) { IntPtr dict = GetObjectDict(ob); if (dict == IntPtr.Zero) diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs index f7afd5d6d..3f4717190 100644 --- a/src/runtime/metatype.cs +++ b/src/runtime/metatype.cs @@ -1,7 +1,9 @@ using System; using System.Collections; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Runtime.Serialization; namespace Python.Runtime { @@ -103,13 +105,20 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) var cb = GetManagedObject(base_type) as ClassBase; if (cb != null) { - if (!cb.CanSubclass()) + try { - return Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed"); + if (!cb.CanSubclass()) + { + return Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed"); + } + } + catch (SerializationException) + { + return Exceptions.RaiseTypeError($"Underlying C# Base class {cb.type} has been deleted"); } } - IntPtr slots = Runtime.PyDict_GetItemString(dict, "__slots__"); + IntPtr slots = Runtime.PyDict_GetItem(dict, PyIdentifier.__slots__); if (slots != IntPtr.Zero) { return Exceptions.RaiseTypeError("subclasses of managed classes do not support __slots__"); @@ -146,38 +155,17 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) flags |= TypeFlags.Subclass; flags |= TypeFlags.HaveGC; Util.WriteCLong(type, TypeOffset.tp_flags, flags); - - TypeManager.CopySlot(base_type, type, TypeOffset.tp_dealloc); - - // Hmm - the standard subtype_traverse, clear look at ob_size to - // do things, so to allow gc to work correctly we need to move - // our hidden handle out of ob_size. Then, in theory we can - // comment this out and still not crash. - TypeManager.CopySlot(base_type, type, TypeOffset.tp_traverse); - TypeManager.CopySlot(base_type, type, TypeOffset.tp_clear); - - // for now, move up hidden handle... - IntPtr gc = Marshal.ReadIntPtr(base_type, TypeOffset.magic()); - Marshal.WriteIntPtr(type, TypeOffset.magic(), gc); - - return type; - } - - - public static IntPtr tp_alloc(IntPtr mt, int n) - { - IntPtr type = Runtime.PyType_GenericAlloc(mt, n); + unsafe + { + var baseTypeEx = ClrMetaTypeEx.FromType(base_type); + var typeEx = ClrMetaTypeEx.FromType(type); + typeEx->ClrHandleOffset = (IntPtr)(OriginalObjectOffsets.Size + ManagedDataOffsets.ob_data); + typeEx->ClrHandle = baseTypeEx->ClrHandle; + } return type; } - - public static void tp_free(IntPtr tp) - { - Runtime.PyObject_GC_Del(tp); - } - - /// /// Metatype __call__ implementation. This is needed to ensure correct /// initialization (__init__ support), because the tp_call we inherit @@ -197,7 +185,7 @@ public static IntPtr tp_call(IntPtr tp, IntPtr args, IntPtr kw) return IntPtr.Zero; } - var init = Runtime.PyObject_GetAttrString(obj, "__init__"); + var init = Runtime.PyObject_GetAttr(obj, PyIdentifier.__init__); Runtime.PyErr_Clear(); if (init != IntPtr.Zero) @@ -280,7 +268,7 @@ public static void tp_dealloc(IntPtr tp) var flags = Util.ReadCLong(tp, TypeOffset.tp_flags); if ((flags & TypeFlags.Subclass) == 0) { - IntPtr gc = Marshal.ReadIntPtr(tp, TypeOffset.magic()); + IntPtr gc = Marshal.ReadIntPtr(tp, ObjectOffset.magic(Runtime.PyObject_TYPE(tp))); ((GCHandle)gc).Free(); } @@ -296,11 +284,17 @@ public static void tp_dealloc(IntPtr tp) NativeCall.Void_Call_1(op, tp); } + public static int tp_clear(IntPtr ob) + { + ClearObjectDict(ob); + return 0; + } + private static IntPtr DoInstanceCheck(IntPtr tp, IntPtr args, bool checkType) { var cb = GetManagedObject(tp) as ClassBase; - if (cb == null) + if (cb == null || !cb.type.Valid) { Runtime.XIncref(Runtime.PyFalse); return Runtime.PyFalse; @@ -332,13 +326,13 @@ private static IntPtr DoInstanceCheck(IntPtr tp, IntPtr args, bool checkType) } var otherCb = GetManagedObject(otherType.Handle) as ClassBase; - if (otherCb == null) + if (otherCb == null || !otherCb.type.Valid) { Runtime.XIncref(Runtime.PyFalse); return Runtime.PyFalse; } - return Converter.ToPython(cb.type.IsAssignableFrom(otherCb.type)); + return Converter.ToPython(cb.type.Value.IsAssignableFrom(otherCb.type.Value)); } } @@ -352,4 +346,18 @@ public static IntPtr __subclasscheck__(IntPtr tp, IntPtr args) return DoInstanceCheck(tp, args, true); } } + + + [StructLayout(LayoutKind.Sequential)] + struct ClrMetaTypeEx + { + public nint ClrHandleOffset; + public IntPtr ClrHandle; + + public static unsafe ClrMetaTypeEx* FromType(IntPtr type) + { + return (ClrMetaTypeEx*)(type + TypeOffset.Size); + } + } + } diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index c0cc4c75c..8f74e0052 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -7,6 +7,7 @@ namespace Python.Runtime { + using MaybeMethodBase = MaybeMethodBase; /// /// A MethodBinder encapsulates information about a (possibly overloaded) /// managed method, and is responsible for selecting the right method given @@ -16,19 +17,27 @@ namespace Python.Runtime [Serializable] internal class MethodBinder { - public ArrayList list; + /// + /// The overloads of this method + /// + public List list; + + [NonSerialized] public MethodBase[] methods; + + [NonSerialized] public bool init = false; - public bool allow_threads = true; + public const bool DefaultAllowThreads = true; + public bool allow_threads = DefaultAllowThreads; internal MethodBinder() { - list = new ArrayList(); + list = new List(); } internal MethodBinder(MethodInfo mi) { - list = new ArrayList { mi }; + list = new List { new MaybeMethodBase(mi) }; } public int Count @@ -77,6 +86,7 @@ internal static MethodInfo MatchSignature(MethodInfo[] mi, Type[] tp) /// /// Given a sequence of MethodInfo and a sequence of type parameters, /// return the MethodInfo that represents the matching closed generic. + /// If unsuccessful, returns null and may set a Python error. /// internal static MethodInfo MatchParameters(MethodInfo[] mi, Type[] tp) { @@ -96,7 +106,18 @@ internal static MethodInfo MatchParameters(MethodInfo[] mi, Type[] tp) { continue; } - return t.MakeGenericMethod(tp); + try + { + // MakeGenericMethod can throw ArgumentException if the type parameters do not obey the constraints. + MethodInfo method = t.MakeGenericMethod(tp); + Exceptions.Clear(); + return method; + } + catch (ArgumentException e) + { + Exceptions.SetError(e); + // The error will remain set until cleared by a successful match. + } } return null; } @@ -164,7 +185,7 @@ internal MethodBase[] GetMethods() { // I'm sure this could be made more efficient. list.Sort(new MethodSorter()); - methods = (MethodBase[])list.ToArray(typeof(MethodBase)); + methods = (from method in list where method.Valid select method.Value).ToArray(); init = true; } return methods; @@ -180,6 +201,11 @@ internal MethodBase[] GetMethods() /// internal static int GetPrecedence(MethodBase mi) { + if (mi == null) + { + return int.MaxValue; + } + ParameterInfo[] pi = mi.GetParameters(); int val = mi.IsStatic ? 3000 : 0; int num = pi.Length; @@ -266,22 +292,80 @@ internal static int ArgPrecedence(Type t) /// /// Bind the given Python instance and arguments to a particular method - /// overload and return a structure that contains the converted Python + /// overload in and return a structure that contains the converted Python /// instance, converted arguments and the correct method to call. + /// If unsuccessful, may set a Python error. /// + /// The Python target of the method invocation. + /// The Python arguments. + /// The Python keyword arguments. + /// A Binding if successful. Otherwise null. internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw) { return Bind(inst, args, kw, null, null); } + /// + /// Bind the given Python instance and arguments to a particular method + /// overload in and return a structure that contains the converted Python + /// instance, converted arguments and the correct method to call. + /// If unsuccessful, may set a Python error. + /// + /// The Python target of the method invocation. + /// The Python arguments. + /// The Python keyword arguments. + /// If not null, only bind to that method. + /// A Binding if successful. Otherwise null. internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info) { return Bind(inst, args, kw, info, null); } + private readonly struct MatchedMethod + { + public MatchedMethod(int kwargsMatched, int defaultsNeeded, object[] margs, int outs, MethodBase mb) + { + KwargsMatched = kwargsMatched; + DefaultsNeeded = defaultsNeeded; + ManagedArgs = margs; + Outs = outs; + Method = mb; + } + + public int KwargsMatched { get; } + public int DefaultsNeeded { get; } + public object[] ManagedArgs { get; } + public int Outs { get; } + public MethodBase Method { get; } + } + + private readonly struct MismatchedMethod + { + public MismatchedMethod(PythonException exception, MethodBase mb) + { + Exception = exception; + Method = mb; + } + + public PythonException Exception { get; } + public MethodBase Method { get; } + } + + /// + /// Bind the given Python instance and arguments to a particular method + /// overload in and return a structure that contains the converted Python + /// instance, converted arguments and the correct method to call. + /// If unsuccessful, may set a Python error. + /// + /// The Python target of the method invocation. + /// The Python arguments. + /// The Python keyword arguments. + /// If not null, only bind to that method. + /// If not null, additionally attempt to bind to the generic methods in this array by inferring generic type parameters. + /// A Binding if successful. Otherwise null. internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) { - // loop to find match, return invoker w/ or /wo error + // loop to find match, return invoker w/ or w/o error MethodBase[] _methods = null; var kwargDict = new Dictionary(); @@ -311,6 +395,9 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth _methods = GetMethods(); } + var argMatchedMethods = new List(_methods.Length); + var mismatchedMethods = new List(); + // TODO: Clean up foreach (MethodBase mi in _methods) { @@ -321,20 +408,112 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth ParameterInfo[] pi = mi.GetParameters(); ArrayList defaultArgList; bool paramsArray; - - if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList)) + int kwargsMatched; + int defaultsNeeded; + bool isOperator = OperatorMethod.IsOperatorMethod(mi); + // Binary operator methods will have 2 CLR args but only one Python arg + // (unary operators will have 1 less each), since Python operator methods are bound. + isOperator = isOperator && pynargs == pi.Length - 1; + bool isReverse = isOperator && OperatorMethod.IsReverse((MethodInfo)mi); // Only cast if isOperator. + if (isReverse && OperatorMethod.IsComparisonOp((MethodInfo)mi)) + continue; // Comparison operators in Python have no reverse mode. + if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList, out kwargsMatched, out defaultsNeeded) && !isOperator) { continue; } - var outs = 0; + // Preprocessing pi to remove either the first or second argument. + if (isOperator && !isReverse) { + // The first Python arg is the right operand, while the bound instance is the left. + // We need to skip the first (left operand) CLR argument. + pi = pi.Skip(1).ToArray(); + } + else if (isOperator && isReverse) { + // The first Python arg is the left operand. + // We need to take the first CLR argument. + pi = pi.Take(1).ToArray(); + } + int outs; var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList, - needsResolution: _methods.Length > 1, + needsResolution: _methods.Length > 1, // If there's more than one possible match. outs: out outs); - if (margs == null) { + mismatchedMethods.Add(new MismatchedMethod(new PythonException(), mi)); + Exceptions.Clear(); continue; } + if (isOperator) + { + if (inst != IntPtr.Zero) + { + if (ManagedType.GetManagedObject(inst) is CLRObject co) + { + bool isUnary = pynargs == 0; + // Postprocessing to extend margs. + var margsTemp = isUnary ? new object[1] : new object[2]; + // If reverse, the bound instance is the right operand. + int boundOperandIndex = isReverse ? 1 : 0; + // If reverse, the passed instance is the left operand. + int passedOperandIndex = isReverse ? 0 : 1; + margsTemp[boundOperandIndex] = co.inst; + if (!isUnary) + { + margsTemp[passedOperandIndex] = margs[0]; + } + margs = margsTemp; + } + else continue; + } + } + + + var matchedMethod = new MatchedMethod(kwargsMatched, defaultsNeeded, margs, outs, mi); + argMatchedMethods.Add(matchedMethod); + } + if (argMatchedMethods.Count > 0) + { + var bestKwargMatchCount = argMatchedMethods.Max(x => x.KwargsMatched); + var fewestDefaultsRequired = argMatchedMethods.Where(x => x.KwargsMatched == bestKwargMatchCount).Min(x => x.DefaultsNeeded); + + int bestCount = 0; + int bestMatchIndex = -1; + + for (int index = 0; index < argMatchedMethods.Count; index++) + { + var testMatch = argMatchedMethods[index]; + if (testMatch.DefaultsNeeded == fewestDefaultsRequired && testMatch.KwargsMatched == bestKwargMatchCount) + { + bestCount++; + if (bestMatchIndex == -1) + bestMatchIndex = index; + } + } + + if (bestCount > 1 && fewestDefaultsRequired > 0) + { + // Best effort for determining method to match on gives multiple possible + // matches and we need at least one default argument - bail from this point + StringBuilder stringBuilder = new StringBuilder("Not enough arguments provided to disambiguate the method. Found:"); + foreach (var matchedMethod in argMatchedMethods) + { + stringBuilder.AppendLine(); + stringBuilder.Append(matchedMethod.Method.ToString()); + } + Exceptions.SetError(Exceptions.TypeError, stringBuilder.ToString()); + return null; + } + + // If we're here either: + // (a) There is only one best match + // (b) There are multiple best matches but none of them require + // default arguments + // in the case of (a) we're done by default. For (b) regardless of which + // method we choose, all arguments are specified _and_ can be converted + // from python to C# so picking any will suffice + MatchedMethod bestMatch = argMatchedMethods[bestMatchIndex]; + var margs = bestMatch.ManagedArgs; + var outs = bestMatch.Outs; + var mi = bestMatch.Method; object target = null; if (!mi.IsStatic && inst != IntPtr.Zero) @@ -350,6 +529,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth // XXX maybe better to do this before all the other rigmarole. if (co == null) { + Exceptions.SetError(Exceptions.TypeError, "Invoked a non-static method with an invalid instance"); return null; } target = co.inst; @@ -357,19 +537,32 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth return new Binding(mi, target, margs, outs); } - // We weren't able to find a matching method but at least one - // is a generic method and info is null. That happens when a generic - // method was not called using the [] syntax. Let's introspect the - // type of the arguments and use it to construct the correct method. - if (isGeneric && info == null && methodinfo != null) + else if (isGeneric && info == null && methodinfo != null) { + // We weren't able to find a matching method but at least one + // is a generic method and info is null. That happens when a generic + // method was not called using the [] syntax. Let's introspect the + // type of the arguments and use it to construct the correct method. Type[] types = Runtime.PythonArgsToTypeArray(args, true); MethodInfo mi = MatchParameters(methodinfo, types); - return Bind(inst, args, kw, mi, null); + if (mi != null) + { + return Bind(inst, args, kw, mi, null); + } + } + if (mismatchedMethods.Count > 0) + { + var aggregateException = GetAggregateException(mismatchedMethods); + Exceptions.SetError(aggregateException); } return null; } + static AggregateException GetAggregateException(IEnumerable mismatchedMethods) + { + return new AggregateException(mismatchedMethods.Select(m => new ArgumentException($"{m.Exception.Message} in method {m.Method}", m.Exception))); + } + static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out bool isNewReference) { isNewReference = false; @@ -404,6 +597,7 @@ static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out /// /// Attempts to convert Python positional argument tuple and keyword argument table /// into an array of managed objects, that can be passed to a method. + /// If unsuccessful, returns null and may set a Python error. /// /// Information about expected parameters /// true, if the last parameter is a params array. @@ -413,7 +607,7 @@ static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out /// A list of default values for omitted parameters /// true, if overloading resolution is required /// Returns number of output parameters - /// An array of .NET arguments, that can be passed to a method. + /// If successful, an array of .NET arguments that can be passed to the method. Otherwise null. static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray, IntPtr args, int pyArgCount, Dictionary kwargDict, @@ -428,7 +622,7 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray, for (int paramIndex = 0; paramIndex < pi.Length; paramIndex++) { var parameter = pi[paramIndex]; - bool hasNamedParam = kwargDict.ContainsKey(parameter.Name); + bool hasNamedParam = parameter.Name != null ? kwargDict.ContainsKey(parameter.Name) : false; bool isNewReference = false; if (paramIndex >= pyArgCount && !(hasNamedParam || (paramsArray && paramIndex == arrayStart))) @@ -450,7 +644,7 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray, { if(arrayStart == paramIndex) { - op = HandleParamsArray(args, arrayStart, pyArgCount, out isNewReference); + op = HandleParamsArray(args, arrayStart, pyArgCount, out isNewReference); } else { @@ -472,7 +666,7 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray, Runtime.XDecref(op); } - if (parameter.IsOut || isOut) + if (isOut) { outs++; } @@ -481,6 +675,16 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray, return margs; } + /// + /// Try to convert a Python argument object to a managed CLR type. + /// If unsuccessful, may set a Python error. + /// + /// Pointer to the Python argument object. + /// That parameter's managed type. + /// If true, there are multiple overloading methods that need resolution. + /// Converted argument. + /// Whether the CLR type is passed by reference. + /// true on success static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResolution, out object arg, out bool isOut) { @@ -492,9 +696,8 @@ static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResoluti return false; } - if (!Converter.ToManaged(op, clrtype, out arg, false)) + if (!Converter.ToManaged(op, clrtype, out arg, true)) { - Exceptions.Clear(); return false; } @@ -502,6 +705,13 @@ static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResoluti return true; } + /// + /// Determine the managed type that a Python argument object needs to be converted into. + /// + /// The parameter's managed type. + /// Pointer to the Python argument object. + /// If true, there are multiple overloading methods that need resolution. + /// null if conversion is not possible static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool needsResolution) { // this logic below handles cases when multiple overloading methods @@ -513,7 +723,6 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool { // HACK: each overload should be weighted in some way instead pyoptype = Runtime.PyObject_Type(argument); - Exceptions.Clear(); if (pyoptype != IntPtr.Zero) { clrtype = Converter.GetTypeByAlias(pyoptype); @@ -523,12 +732,11 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool if (clrtype != null) { - var typematch = false; if ((parameterType != typeof(object)) && (parameterType != clrtype)) { IntPtr pytype = Converter.GetPythonTypeByAlias(parameterType); pyoptype = Runtime.PyObject_Type(argument); - Exceptions.Clear(); + var typematch = false; if (pyoptype != IntPtr.Zero) { if (pytype != pyoptype) @@ -544,13 +752,17 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool if (!typematch) { // this takes care of enum values - TypeCode argtypecode = Type.GetTypeCode(parameterType); - TypeCode paramtypecode = Type.GetTypeCode(clrtype); - if (argtypecode == paramtypecode) + TypeCode parameterTypeCode = Type.GetTypeCode(parameterType); + TypeCode clrTypeCode = Type.GetTypeCode(clrtype); + if (parameterTypeCode == clrTypeCode) { typematch = true; clrtype = parameterType; } + else + { + Exceptions.RaiseTypeError($"Expected {parameterTypeCode}, got {clrTypeCode}"); + } } Runtime.XDecref(pyoptype); if (!typematch) @@ -560,7 +772,6 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool } else { - typematch = true; clrtype = parameterType; } } @@ -571,25 +782,39 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool return clrtype; } - + /// + /// Check whether the number of Python and .NET arguments match, and compute additional arg information. + /// + /// Number of positional args passed from Python. + /// Parameters of the specified .NET method. + /// Keyword args passed from Python. + /// True if the final param of the .NET method is an array (`params` keyword). + /// List of default values for arguments. + /// Number of kwargs from Python that are also present in the .NET method. + /// Number of non-null defaultsArgs. + /// static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] parameters, Dictionary kwargDict, out bool paramsArray, - out ArrayList defaultArgList) + out ArrayList defaultArgList, + out int kwargsMatched, + out int defaultsNeeded) { defaultArgList = null; var match = false; paramsArray = parameters.Length > 0 ? Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)) : false; - + kwargsMatched = 0; + defaultsNeeded = 0; if (positionalArgumentCount == parameters.Length && kwargDict.Count == 0) { match = true; } - else if (positionalArgumentCount < parameters.Length) + else if (positionalArgumentCount < parameters.Length && (!paramsArray || positionalArgumentCount == parameters.Length - 1)) { - // every parameter past 'positionalArgumentCount' must have either - // a corresponding keyword argument or a default parameter match = true; + // every parameter past 'positionalArgumentCount' must have either + // a corresponding keyword arg or a default param, unless the method + // method accepts a params array (which cannot have a default value) defaultArgList = new ArrayList(); for (var v = positionalArgumentCount; v < parameters.Length; v++) { @@ -599,6 +824,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa // no need to check for a default parameter, but put a null // placeholder in defaultArgList defaultArgList.Add(null); + kwargsMatched++; } else if (parameters[v].IsOptional) { @@ -607,8 +833,9 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa // The GetDefaultValue() extension method will return the value // to be passed in as the parameter value defaultArgList.Add(parameters[v].GetDefaultValue()); + defaultsNeeded++; } - else if(!paramsArray) + else if (!paramsArray) { match = false; } @@ -671,6 +898,17 @@ protected static void AppendArgumentTypes(StringBuilder to, IntPtr args) internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) { + // No valid methods, nothing to bind. + if (GetMethods().Length == 0) + { + var msg = new StringBuilder("The underlying C# method(s) have been deleted"); + if (list.Count > 0 && list[0].Name != null) + { + msg.Append($": {list[0]}"); + } + return Exceptions.RaiseTypeError(msg.ToString()); + } + Binding binding = Bind(inst, args, kw, info, methodinfo); object result; IntPtr ts = IntPtr.Zero; @@ -682,10 +920,14 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i { value.Append($" for {methodinfo[0].Name}"); } + else if (list.Count > 0 && list[0].Valid) + { + value.Append($" for {list[0].Value.Name}"); + } value.Append(": "); AppendArgumentTypes(to: value, args); - Exceptions.SetError(Exceptions.TypeError, value.ToString()); + Exceptions.RaiseTypeError(value.ToString()); return IntPtr.Zero; } @@ -718,34 +960,35 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i } // If there are out parameters, we return a tuple containing - // the result followed by the out parameters. If there is only + // the result, if any, followed by the out parameters. If there is only // one out parameter and the return type of the method is void, // we return the out parameter as the result to Python (for // code compatibility with ironpython). var mi = (MethodInfo)binding.info; - if (binding.outs == 1 && mi.ReturnType == typeof(void)) - { - } - if (binding.outs > 0) { ParameterInfo[] pi = mi.GetParameters(); int c = pi.Length; var n = 0; - IntPtr t = Runtime.PyTuple_New(binding.outs + 1); - IntPtr v = Converter.ToPython(result, mi.ReturnType); - Runtime.PyTuple_SetItem(t, n, v); - n++; + bool isVoid = mi.ReturnType == typeof(void); + int tupleSize = binding.outs + (isVoid ? 0 : 1); + IntPtr t = Runtime.PyTuple_New(tupleSize); + if (!isVoid) + { + IntPtr v = Converter.ToPython(result, mi.ReturnType); + Runtime.PyTuple_SetItem(t, n, v); + n++; + } for (var i = 0; i < c; i++) { Type pt = pi[i].ParameterType; - if (pi[i].IsOut || pt.IsByRef) + if (pt.IsByRef) { - v = Converter.ToPython(binding.args[i], pt.GetElementType()); + IntPtr v = Converter.ToPython(binding.args[i], pt.GetElementType()); Runtime.PyTuple_SetItem(t, n, v); n++; } @@ -753,7 +996,7 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i if (binding.outs == 1 && mi.ReturnType == typeof(void)) { - v = Runtime.PyTuple_GetItem(t, 1); + IntPtr v = Runtime.PyTuple_GetItem(t, 0); Runtime.XIncref(v); Runtime.XDecref(t); return v; @@ -770,12 +1013,25 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i /// /// Utility class to sort method info by parameter type precedence. /// - internal class MethodSorter : IComparer + internal class MethodSorter : IComparer { - int IComparer.Compare(object m1, object m2) + int IComparer.Compare(MaybeMethodBase m1, MaybeMethodBase m2) { - var me1 = (MethodBase)m1; - var me2 = (MethodBase)m2; + MethodBase me1 = m1.UnsafeValue; + MethodBase me2 = m2.UnsafeValue; + if (me1 == null && me2 == null) + { + return 0; + } + else if (me1 == null) + { + return -1; + } + else if (me2 == null) + { + return 1; + } + if (me1.DeclaringType != me2.DeclaringType) { // m2's type derives from m1's type, favor m2 @@ -787,8 +1043,8 @@ int IComparer.Compare(object m1, object m2) return -1; } - int p1 = MethodBinder.GetPrecedence((MethodBase)m1); - int p2 = MethodBinder.GetPrecedence((MethodBase)m2); + int p1 = MethodBinder.GetPrecedence(me1); + int p2 = MethodBinder.GetPrecedence(me2); if (p1 < p2) { return -1; diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs index 011d8217d..f33015ba4 100644 --- a/src/runtime/methodbinding.cs +++ b/src/runtime/methodbinding.cs @@ -4,6 +4,7 @@ namespace Python.Runtime { + using MaybeMethodInfo = MaybeMethodBase; /// /// Implements a Python binding type for CLR methods. These work much like /// standard Python method bindings, but the same type is used to bind @@ -12,7 +13,7 @@ namespace Python.Runtime [Serializable] internal class MethodBinding : ExtensionType { - internal MethodInfo info; + internal MaybeMethodInfo info; internal MethodObject m; internal IntPtr target; internal IntPtr targetType; @@ -84,7 +85,7 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key) return IntPtr.Zero; } - string name = Runtime.GetManagedString(key); + string name = InternString.GetManagedString(key); switch (name) { case "__doc__": @@ -111,15 +112,16 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) // This works around a situation where the wrong generic method is picked, // for example this method in the tests: string Overloaded(int arg1, int arg2, string arg3) - if (self.info != null) + if (self.info.Valid) { - if (self.info.IsGenericMethod) + var info = self.info.Value; + if (info.IsGenericMethod) { var len = Runtime.PyTuple_Size(args); //FIXME: Never used Type[] sigTp = Runtime.PythonArgsToTypeArray(args, true); if (sigTp != null) { - Type[] genericTp = self.info.GetGenericArguments(); + Type[] genericTp = info.GetGenericArguments(); MethodInfo betterMatch = MethodBinder.MatchSignatureAndParameters(self.m.info, genericTp, sigTp); if (betterMatch != null) { @@ -164,9 +166,9 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) if (inst?.inst is IPythonDerivedType) { var baseType = GetManagedObject(self.targetType) as ClassBase; - if (baseType != null) + if (baseType != null && baseType.type.Valid) { - string baseMethodName = "_" + baseType.type.Name + "__" + self.m.name; + string baseMethodName = "_" + baseType.type.Value.Name + "__" + self.m.name; IntPtr baseMethod = Runtime.PyObject_GetAttrString(target, baseMethodName); if (baseMethod != IntPtr.Zero) { @@ -184,8 +186,7 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) } } } - - return self.m.Invoke(target, args, kw, self.info); + return self.m.Invoke(target, args, kw, self.info.UnsafeValue); } finally { @@ -200,35 +201,27 @@ public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) /// /// MethodBinding __hash__ implementation. /// - public static IntPtr tp_hash(IntPtr ob) + public static nint tp_hash(IntPtr ob) { var self = (MethodBinding)GetManagedObject(ob); - long x = 0; - long y = 0; + nint x = 0; if (self.target != IntPtr.Zero) { - x = Runtime.PyObject_Hash(self.target).ToInt64(); + x = Runtime.PyObject_Hash(self.target); if (x == -1) { - return new IntPtr(-1); + return x; } } - y = Runtime.PyObject_Hash(self.m.pyHandle).ToInt64(); + nint y = Runtime.PyObject_Hash(self.m.pyHandle); if (y == -1) { - return new IntPtr(-1); - } - - x ^= y; - - if (x == -1) - { - x = -1; + return y; } - return new IntPtr(x); + return x ^ y; } /// diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs index eb3ce8a18..630f286da 100644 --- a/src/runtime/methodobject.cs +++ b/src/runtime/methodobject.cs @@ -1,8 +1,12 @@ using System; +using System.Collections.Generic; using System.Reflection; +using System.Linq; namespace Python.Runtime { + using MaybeMethodInfo = MaybeMethodBase; + /// /// Implements a Python type that represents a CLR method. Method objects /// support a subscript syntax [] to allow explicit overload selection. @@ -13,40 +17,45 @@ namespace Python.Runtime [Serializable] internal class MethodObject : ExtensionType { - internal MethodInfo[] info; + [NonSerialized] + private MethodInfo[] _info = null; + private readonly List infoList; internal string name; internal MethodBinding unbound; - internal MethodBinder binder; + internal readonly MethodBinder binder; internal bool is_static = false; internal IntPtr doc; internal Type type; - public MethodObject(Type type, string name, MethodInfo[] info) - { - _MethodObject(type, name, info); - } - - public MethodObject(Type type, string name, MethodInfo[] info, bool allow_threads) - { - _MethodObject(type, name, info); - binder.allow_threads = allow_threads; - } - - private void _MethodObject(Type type, string name, MethodInfo[] info) + public MethodObject(Type type, string name, MethodInfo[] info, bool allow_threads = MethodBinder.DefaultAllowThreads) { this.type = type; this.name = name; - this.info = info; + this.infoList = new List(); binder = new MethodBinder(); foreach (MethodInfo item in info) { + this.infoList.Add(item); binder.AddMethod(item); if (item.IsStatic) { this.is_static = true; } } + binder.allow_threads = allow_threads; + } + + internal MethodInfo[] info + { + get + { + if (_info == null) + { + _info = (from i in infoList where i.Valid select i.Value).ToArray(); + } + return _info; + } } public virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) @@ -133,8 +142,7 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key) return Exceptions.RaiseTypeError("string expected"); } - string name = Runtime.GetManagedString(key); - if (name == "__doc__") + if (Runtime.PyUnicode_Compare(key, PyIdentifier.__doc__) == 0) { IntPtr doc = self.GetDocString(); Runtime.XIncref(doc); @@ -209,7 +217,7 @@ public static IntPtr tp_repr(IntPtr ob) { var self = (MethodObject)GetManagedObject(ob); self.ClearMembers(); - ClearObjectDict(ob); + RemoveObjectDict(ob); self.Dealloc(); } diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs index 6313975da..41167e322 100644 --- a/src/runtime/moduleobject.cs +++ b/src/runtime/moduleobject.cs @@ -18,6 +18,7 @@ internal class ModuleObject : ExtensionType internal string moduleName; internal IntPtr dict; + internal BorrowedReference DictRef => new BorrowedReference(dict); protected string _namespace; public ModuleObject(string name) @@ -44,17 +45,14 @@ public ModuleObject(string name) } dict = Runtime.PyDict_New(); - IntPtr pyname = Runtime.PyString_FromString(moduleName); - IntPtr pyfilename = Runtime.PyString_FromString(filename); - IntPtr pydocstring = Runtime.PyString_FromString(docstring); - IntPtr pycls = TypeManager.GetTypeHandle(GetType()); - Runtime.PyDict_SetItemString(dict, "__name__", pyname); - Runtime.PyDict_SetItemString(dict, "__file__", pyfilename); - Runtime.PyDict_SetItemString(dict, "__doc__", pydocstring); - Runtime.PyDict_SetItemString(dict, "__class__", pycls); - Runtime.XDecref(pyname); - Runtime.XDecref(pyfilename); - Runtime.XDecref(pydocstring); + using var pyname = NewReference.DangerousFromPointer(Runtime.PyString_FromString(moduleName)); + using var pyfilename = NewReference.DangerousFromPointer(Runtime.PyString_FromString(filename)); + using var pydocstring = NewReference.DangerousFromPointer(Runtime.PyString_FromString(docstring)); + BorrowedReference pycls = TypeManager.GetTypeReference(GetType()); + Runtime.PyDict_SetItem(DictRef, PyIdentifier.__name__, pyname); + Runtime.PyDict_SetItem(DictRef, PyIdentifier.__file__, pyfilename); + Runtime.PyDict_SetItem(DictRef, PyIdentifier.__doc__, pydocstring); + Runtime.PyDict_SetItem(DictRef, PyIdentifier.__class__, pycls); Runtime.XIncref(dict); SetObjectDict(pyHandle, dict); @@ -118,30 +116,6 @@ public ManagedType GetAttribute(string name, bool guess) return c; } - // This is a little repetitive, but it ensures that the right - // thing happens with implicit assembly loading at a reasonable - // cost. Ask the AssemblyManager to do implicit loading for each - // of the steps in the qualified name, then try it again. - bool ignore = name.StartsWith("__"); - if (AssemblyManager.LoadImplicit(qname, assemblyLoadErrorHandler: ImportWarning, !ignore)) - { - if (AssemblyManager.IsValidNamespace(qname)) - { - m = new ModuleObject(qname); - StoreAttribute(name, m); - m.DecrRefCount(); - return m; - } - - type = AssemblyManager.LookupTypes(qname).FirstOrDefault(t => t.IsPublic); - if (type != null) - { - c = ClassManager.GetClass(type); - StoreAttribute(name, c); - return c; - } - } - // We didn't find the name, so we may need to see if there is a // generic type with this base name. If so, we'll go ahead and // return it. Note that we store the mapping of the unmangled @@ -201,9 +175,9 @@ public void LoadNames() { continue; } - IntPtr attr = Runtime.PyDict_GetItemString(dict, name); + BorrowedReference attr = Runtime.PyDict_GetItemString(DictRef, name); // If __dict__ has already set a custom property, skip it. - if (attr != IntPtr.Zero) + if (!attr.IsNull) { continue; } @@ -282,7 +256,7 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key) return op; } - string name = Runtime.GetManagedString(key); + string name = InternString.GetManagedString(key); if (name == "__dict__") { Runtime.XIncref(self.dict); @@ -365,6 +339,24 @@ protected override void OnSave(InterDomainContext context) // Decref twice in tp_clear, equilibrate them. Runtime.XIncref(dict); Runtime.XIncref(dict); + // destroy the cache(s) + foreach (var pair in cache) + { + if ((Runtime.PyDict_DelItemString(DictRef, pair.Key) == -1) && + (Exceptions.ExceptionMatches(Exceptions.KeyError))) + { + // Trying to remove a key that's not in the dictionary + // raises an error. We don't care about it. + Runtime.PyErr_Clear(); + } + else if (Exceptions.ErrorOccurred()) + { + throw new PythonException(); + } + pair.Value.DecrRefCount(); + } + + cache.Clear(); } protected override void OnLoad(InterDomainContext context) @@ -542,11 +534,5 @@ public static string[] ListAssemblies(bool verbose) } return names; } - - [ModuleFunction] - public static int _AtExit() - { - return Runtime.AtExit(); - } } } diff --git a/src/runtime/native/ABI.cs b/src/runtime/native/ABI.cs new file mode 100644 index 000000000..21890d57e --- /dev/null +++ b/src/runtime/native/ABI.cs @@ -0,0 +1,34 @@ +namespace Python.Runtime.Native +{ + using System; + using System.Globalization; + using System.Linq; + using System.Reflection; + using System.Runtime.InteropServices; + + static class ABI + { + internal static void Initialize(Version version, BorrowedReference pyType) + { + string offsetsClassSuffix = string.Format(CultureInfo.InvariantCulture, + "{0}{1}", version.Major, version.Minor); + + var thisAssembly = Assembly.GetExecutingAssembly(); + + const string nativeTypeOffsetClassName = "Python.Runtime.NativeTypeOffset"; + string className = "Python.Runtime.TypeOffset" + offsetsClassSuffix; + Type typeOffsetsClass = + // Try platform native offsets first. It is only present when generated by setup.py + thisAssembly.GetType(nativeTypeOffsetClassName, throwOnError: false) + ?? thisAssembly.GetType(className, throwOnError: false); + if (typeOffsetsClass is null) + { + var types = thisAssembly.GetTypes().Select(type => type.Name).Where(name => name.StartsWith("TypeOffset")); + string message = $"Searching for {className}, found {string.Join(",", types)}."; + throw new NotSupportedException($"Python ABI v{version} is not supported: {message}"); + } + var typeOffsets = (ITypeOffsets)Activator.CreateInstance(typeOffsetsClass); + TypeOffset.Use(typeOffsets); + } + } +} diff --git a/src/runtime/native/GeneratedTypeOffsets.cs b/src/runtime/native/GeneratedTypeOffsets.cs new file mode 100644 index 000000000..bf10df124 --- /dev/null +++ b/src/runtime/native/GeneratedTypeOffsets.cs @@ -0,0 +1,22 @@ +namespace Python.Runtime.Native +{ + using System; + using System.Reflection; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential)] + abstract class GeneratedTypeOffsets + { + protected GeneratedTypeOffsets() + { + var type = this.GetType(); + var offsetProperties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); + int fieldSize = IntPtr.Size; + for (int fieldIndex = 0; fieldIndex < offsetProperties.Length; fieldIndex++) + { + int offset = fieldIndex * fieldSize; + offsetProperties[fieldIndex].SetValue(this, offset, index: null); + } + } + } +} diff --git a/src/runtime/native/ITypeOffsets.cs b/src/runtime/native/ITypeOffsets.cs new file mode 100644 index 000000000..438e96fe8 --- /dev/null +++ b/src/runtime/native/ITypeOffsets.cs @@ -0,0 +1,73 @@ +// ReSharper disable InconsistentNaming +// ReSharper disable IdentifierTypo +namespace Python.Runtime.Native +{ + using System.Diagnostics.CodeAnalysis; + + [SuppressMessage("Style", "IDE1006:Naming Styles", + Justification = "Following CPython", + Scope = "type")] + interface ITypeOffsets + { + int bf_getbuffer { get; } + int mp_ass_subscript { get; } + int mp_length { get; } + int mp_subscript { get; } + int name { get; } + int nb_positive { get; } + int nb_negative { get; } + int nb_add { get; } + int nb_subtract { get; } + int nb_multiply { get; } + int nb_true_divide { get; } + int nb_and { get; } + int nb_or { get; } + int nb_xor { get; } + int nb_lshift { get; } + int nb_rshift { get; } + int nb_remainder { get; } + int nb_invert { get; } + int nb_inplace_add { get; } + int nb_inplace_subtract { get; } + int ob_size { get; } + int ob_refcnt { get; } + int ob_type { get; } + int qualname { get; } + int sq_contains { get; } + int sq_length { get; } + int tp_alloc { get; } + int tp_as_buffer { get; } + int tp_as_mapping { get; } + int tp_as_number { get; } + int tp_as_sequence { get; } + int tp_base { get; } + int tp_bases { get; } + int tp_basicsize { get; } + int tp_call { get; } + int tp_clear { get; } + int tp_dealloc { get; } + int tp_descr_get { get; } + int tp_descr_set { get; } + int tp_dict { get; } + int tp_dictoffset { get; } + int tp_init { get; } + int tp_flags { get; } + int tp_free { get; } + int tp_getattro { get; } + int tp_hash { get; } + int tp_is_gc { get; } + int tp_itemsize { get; } + int tp_iter { get; } + int tp_iternext { get; } + int tp_methods { get; } + int tp_mro { get; } + int tp_name { get; } + int tp_new { get; } + int tp_repr { get; } + int tp_richcompare { get; } + int tp_weaklistoffset { get; } + int tp_setattro { get; } + int tp_str { get; } + int tp_traverse { get; } + } +} diff --git a/src/runtime/native/PyCompilerFlags.cs b/src/runtime/native/PyCompilerFlags.cs new file mode 100644 index 000000000..9e8242c58 --- /dev/null +++ b/src/runtime/native/PyCompilerFlags.cs @@ -0,0 +1,13 @@ +using System; + +namespace Python.Runtime.Native +{ + [Flags] + enum PyCompilerFlags + { + SOURCE_IS_UTF8 = 0x0100, + DONT_IMPLY_DEDENT = 0x0200, + ONLY_AST = 0x0400, + IGNORE_COOKIE = 0x0800, + } +} diff --git a/src/runtime/native/StrPtr.cs b/src/runtime/native/StrPtr.cs new file mode 100644 index 000000000..99aa35ddd --- /dev/null +++ b/src/runtime/native/StrPtr.cs @@ -0,0 +1,73 @@ +#nullable enable +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Python.Runtime.Native +{ + [StructLayout(LayoutKind.Sequential)] + struct StrPtr : IDisposable + { + public IntPtr RawPointer { get; set; } + unsafe byte* Bytes => (byte*)this.RawPointer; + + public unsafe StrPtr(string value, Encoding encoding) + { + if (value is null) throw new ArgumentNullException(nameof(value)); + if (encoding is null) throw new ArgumentNullException(nameof(encoding)); + + var bytes = encoding.GetBytes(value); + this.RawPointer = Marshal.AllocHGlobal(checked(bytes.Length + 1)); + try + { + Marshal.Copy(bytes, 0, this.RawPointer, bytes.Length); + this.Bytes[bytes.Length] = 0; + } + catch + { + this.Dispose(); + throw; + } + } + + public unsafe string? ToString(Encoding encoding) + { + if (encoding is null) throw new ArgumentNullException(nameof(encoding)); + if (this.RawPointer == IntPtr.Zero) return null; + + return encoding.GetString((byte*)this.RawPointer, byteCount: checked((int)this.ByteCount)); + } + + public unsafe nuint ByteCount + { + get + { + if (this.RawPointer == IntPtr.Zero) throw new NullReferenceException(); + + nuint zeroIndex = 0; + while (this.Bytes[zeroIndex] != 0) + { + zeroIndex++; + } + return zeroIndex; + } + } + + public void Dispose() + { + if (this.RawPointer == IntPtr.Zero) + return; + + Marshal.FreeHGlobal(this.RawPointer); + this.RawPointer = IntPtr.Zero; + } + + internal static Encoding GetEncodingByPythonName(string pyEncodingName) + { + // https://stackoverflow.com/a/7798749/231238 + if (pyEncodingName == "mbcs") return Encoding.Default; + + return Encoding.GetEncoding(pyEncodingName); + } + } +} diff --git a/src/runtime/native/TypeOffset.cs b/src/runtime/native/TypeOffset.cs new file mode 100644 index 000000000..62bd90b4a --- /dev/null +++ b/src/runtime/native/TypeOffset.cs @@ -0,0 +1,177 @@ +// ReSharper disable InconsistentNaming +// ReSharper disable IdentifierTypo +namespace Python.Runtime +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Reflection; + using System.Runtime.InteropServices; + + using Python.Runtime.Native; + + [SuppressMessage("Style", "IDE1006:Naming Styles", + Justification = "Following CPython", + Scope = "type")] + static partial class TypeOffset + { + internal static int bf_getbuffer { get; private set; } + internal static int mp_ass_subscript { get; private set; } + internal static int mp_length { get; private set; } + internal static int mp_subscript { get; private set; } + internal static int name { get; private set; } + internal static int nb_positive { get; private set; } + internal static int nb_negative { get; private set; } + internal static int nb_add { get; private set; } + internal static int nb_subtract { get; private set; } + internal static int nb_multiply { get; private set; } + internal static int nb_true_divide { get; private set; } + internal static int nb_and { get; private set; } + internal static int nb_or { get; private set; } + internal static int nb_xor { get; private set; } + internal static int nb_lshift { get; private set; } + internal static int nb_rshift { get; private set; } + internal static int nb_remainder { get; private set; } + internal static int nb_invert { get; private set; } + internal static int nb_inplace_add { get; private set; } + internal static int nb_inplace_subtract { get; private set; } + internal static int ob_size { get; private set; } + internal static int ob_refcnt { get; private set; } + internal static int ob_type { get; private set; } + internal static int qualname { get; private set; } + internal static int sq_contains { get; private set; } + internal static int sq_length { get; private set; } + internal static int tp_alloc { get; private set; } + internal static int tp_as_buffer { get; private set; } + internal static int tp_as_mapping { get; private set; } + internal static int tp_as_number { get; private set; } + internal static int tp_as_sequence { get; private set; } + internal static int tp_base { get; private set; } + internal static int tp_bases { get; private set; } + internal static int tp_basicsize { get; private set; } + internal static int tp_call { get; private set; } + internal static int tp_clear { get; private set; } + internal static int tp_dealloc { get; private set; } + internal static int tp_descr_get { get; private set; } + internal static int tp_descr_set { get; private set; } + internal static int tp_dict { get; private set; } + internal static int tp_dictoffset { get; private set; } + internal static int tp_init { get; private set; } + internal static int tp_flags { get; private set; } + internal static int tp_free { get; private set; } + internal static int tp_getattro { get; private set; } + internal static int tp_hash { get; private set; } + internal static int tp_is_gc { get; private set; } + internal static int tp_itemsize { get; private set; } + internal static int tp_iter { get; private set; } + internal static int tp_iternext { get; private set; } + internal static int tp_methods { get; private set; } + internal static int tp_mro { get; private set; } + internal static int tp_name { get; private set; } + internal static int tp_new { get; private set; } + internal static int tp_repr { get; private set; } + internal static int tp_richcompare { get; private set; } + internal static int tp_weaklistoffset { get; private set; } + internal static int tp_setattro { get; private set; } + internal static int tp_str { get; private set; } + internal static int tp_traverse { get; private set; } + + internal static int Size; + + internal static void Use(ITypeOffsets offsets) + { + if (offsets is null) throw new ArgumentNullException(nameof(offsets)); + + slotNames.Clear(); + var offsetProperties = typeof(TypeOffset).GetProperties(FieldFlags); + foreach (var offsetProperty in offsetProperties) + { + slotNames.Add(offsetProperty.Name); + + var sourceProperty = typeof(ITypeOffsets).GetProperty(offsetProperty.Name); + int value = (int)sourceProperty.GetValue(offsets, null); + offsetProperty.SetValue(obj: null, value: value, index: null); + } + Size = (int)Marshal.ReadIntPtr(Runtime.PyTypeType, tp_basicsize); + ValidateUnusedTypeOffsetProperties(offsetProperties); + ValidateRequiredOffsetsPresent(offsetProperties); + } + + static readonly BindingFlags FieldFlags = BindingFlags.NonPublic | BindingFlags.Static; + internal static Dictionary GetOffsets() + { + var properties = typeof(TypeOffset).GetProperties(FieldFlags); + var result = properties.ToDictionary( + keySelector: p => p.Name, + elementSelector: p => (int)p.GetValue(obj: null, index: null)); + Debug.Assert(result.Values.Any(v => v != 0)); + return result; + } + + internal static int GetOffsetUncached(string name) + { + var property = typeof(TypeOffset).GetProperty(name, FieldFlags); + return (int)property.GetValue(obj: null, index: null); + } + + static readonly HashSet slotNames = new HashSet(); + internal static bool IsSupportedSlotName(string name) => slotNames.Contains(name); + + [Conditional("DEBUG")] + static void ValidateUnusedTypeOffsetProperties(PropertyInfo[] offsetProperties) + { + var extras = new List(); + foreach (var property in typeof(ITypeOffsets).GetProperties(FieldFlags)) + { + if (!offsetProperties.Any(prop => prop.Name == property.Name)) + extras.Add(property.Name); + } + extras.Sort(); + Debug.Assert(extras.Count == 0, message: string.Join(", ", extras)); + } + + [Conditional("DEBUG")] + static void ValidateRequiredOffsetsPresent(PropertyInfo[] offsetProperties) + { + var present = new HashSet(offsetProperties.Select(p => p.Name)); + var missing = new HashSet(); + + var thisAssembly = Assembly.GetExecutingAssembly(); + var managedTypes = thisAssembly.GetTypes() + .Where(typeof(ManagedType).IsAssignableFrom) + .ToList(); + foreach (var managedType in managedTypes) + { + var slots = managedType.GetMethods(BindingFlags.Public | BindingFlags.Static); + foreach(var slot in slots) + if (!present.Contains(slot.Name)) + missing.Add(slot.Name); + } + foreach (string notSlot in new[] + { + "__instancecheck__", + "__subclasscheck__", + "AddReference", + "FinalizeObject", + "FindAssembly", + "get_SuppressDocs", + "get_SuppressOverloads", + "GetClrType", + "getPreload", + "Initialize", + "ListAssemblies", + "Release", + "Reset", + "set_SuppressDocs", + "set_SuppressOverloads", + "setPreload", + }) + missing.Remove(notSlot); + + Debug.Assert(missing.Count == 0, + "Missing slots: " + string.Join(", ", missing)); + } + } +} diff --git a/src/runtime/nativecall.cs b/src/runtime/nativecall.cs index ec0bf338c..60259dcd0 100644 --- a/src/runtime/nativecall.cs +++ b/src/runtime/nativecall.cs @@ -12,14 +12,6 @@ namespace Python.Runtime /// C API can just be wrapped with p/invoke, but there are some /// situations (specifically, calling functions through Python /// type structures) where we need to call functions indirectly. - /// This class uses Reflection.Emit to generate IJW thunks that - /// support indirect calls to native code using various common - /// call signatures. This is mainly a workaround for the fact - /// that you can't spell an indirect call in C# (but can in IL). - /// Another approach that would work is for this to be turned - /// into a separate utility program that could be run during the - /// build process to generate the thunks as a separate assembly - /// that could then be referenced by the main Python runtime. /// internal class NativeCall { @@ -48,10 +40,15 @@ public static int Int_Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3) return d(a1, a2, a3); } - private static T GetDelegate(IntPtr fp) where T: Delegate + internal static T GetDelegate(IntPtr fp) where T: Delegate { - // Use Marshal.GetDelegateForFunctionPointer<> directly after upgrade the framework - return (T)Marshal.GetDelegateForFunctionPointer(fp, typeof(T)); + Delegate d = null; + if (!Interop.allocatedThunks.TryGetValue(fp, out d)) + { + // We don't cache this delegate because this is a pure delegate ot unmanaged. + d = Marshal.GetDelegateForFunctionPointer(fp); + } + return (T)d; } } } diff --git a/src/runtime/operatormethod.cs b/src/runtime/operatormethod.cs new file mode 100644 index 000000000..59bf944bc --- /dev/null +++ b/src/runtime/operatormethod.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; + +namespace Python.Runtime +{ + internal static class OperatorMethod + { + /// + /// Maps the compiled method name in .NET CIL (e.g. op_Addition) to + /// the equivalent Python operator (e.g. __add__) as well as the offset + /// that identifies that operator's slot (e.g. nb_add) in heap space. + /// + public static Dictionary OpMethodMap { get; private set; } + public static Dictionary ComparisonOpMap { get; private set; } + public readonly struct SlotDefinition + { + public SlotDefinition(string methodName, int typeOffset) + { + MethodName = methodName; + TypeOffset = typeOffset; + } + public string MethodName { get; } + public int TypeOffset { get; } + + } + private static PyObject _opType; + + static OperatorMethod() + { + // .NET operator method names are documented at: + // https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/operator-overloads + // Python operator methods and slots are documented at: + // https://docs.python.org/3/c-api/typeobj.html + // TODO: Rich compare, inplace operator support + OpMethodMap = new Dictionary + { + ["op_Addition"] = new SlotDefinition("__add__", TypeOffset.nb_add), + ["op_Subtraction"] = new SlotDefinition("__sub__", TypeOffset.nb_subtract), + ["op_Multiply"] = new SlotDefinition("__mul__", TypeOffset.nb_multiply), + ["op_Division"] = new SlotDefinition("__truediv__", TypeOffset.nb_true_divide), + ["op_Modulus"] = new SlotDefinition("__mod__", TypeOffset.nb_remainder), + ["op_BitwiseAnd"] = new SlotDefinition("__and__", TypeOffset.nb_and), + ["op_BitwiseOr"] = new SlotDefinition("__or__", TypeOffset.nb_or), + ["op_ExclusiveOr"] = new SlotDefinition("__xor__", TypeOffset.nb_xor), + ["op_LeftShift"] = new SlotDefinition("__lshift__", TypeOffset.nb_lshift), + ["op_RightShift"] = new SlotDefinition("__rshift__", TypeOffset.nb_rshift), + ["op_OnesComplement"] = new SlotDefinition("__invert__", TypeOffset.nb_invert), + ["op_UnaryNegation"] = new SlotDefinition("__neg__", TypeOffset.nb_negative), + ["op_UnaryPlus"] = new SlotDefinition("__pos__", TypeOffset.nb_positive), + ["op_OneComplement"] = new SlotDefinition("__invert__", TypeOffset.nb_invert), + }; + ComparisonOpMap = new Dictionary + { + ["op_Equality"] = "__eq__", + ["op_Inequality"] = "__ne__", + ["op_LessThanOrEqual"] = "__le__", + ["op_GreaterThanOrEqual"] = "__ge__", + ["op_LessThan"] = "__lt__", + ["op_GreaterThan"] = "__gt__", + }; + } + + public static void Initialize() + { + _opType = GetOperatorType(); + } + + public static void Shutdown() + { + if (_opType != null) + { + _opType.Dispose(); + _opType = null; + } + } + + public static bool IsOperatorMethod(MethodBase method) + { + if (!method.IsSpecialName) + { + return false; + } + return OpMethodMap.ContainsKey(method.Name) || ComparisonOpMap.ContainsKey(method.Name); + } + + public static bool IsComparisonOp(MethodInfo method) + { + return ComparisonOpMap.ContainsKey(method.Name); + } + + /// + /// For the operator methods of a CLR type, set the special slots of the + /// corresponding Python type's operator methods. + /// + /// + /// + public static void FixupSlots(IntPtr pyType, Type clrType) + { + const BindingFlags flags = BindingFlags.Public | BindingFlags.Static; + Debug.Assert(_opType != null); + foreach (var method in clrType.GetMethods(flags)) + { + // We only want to override slots for operators excluding + // comparison operators, which are handled by ClassBase.tp_richcompare. + if (!OpMethodMap.ContainsKey(method.Name)) + { + continue; + } + int offset = OpMethodMap[method.Name].TypeOffset; + // Copy the default implementation of e.g. the nb_add slot, + // which simply calls __add__ on the type. + IntPtr func = Marshal.ReadIntPtr(_opType.Handle, offset); + // Write the slot definition of the target Python type, so + // that we can later modify __add___ and it will be called + // when used with a Python operator. + // https://tenthousandmeters.com/blog/python-behind-the-scenes-6-how-python-object-system-works/ + Marshal.WriteIntPtr(pyType, offset, func); + } + } + + public static string GetPyMethodName(string clrName) + { + if (OpMethodMap.ContainsKey(clrName)) + { + return OpMethodMap[clrName].MethodName; + } else + { + return ComparisonOpMap[clrName]; + } + } + + private static string GenerateDummyCode() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("class OperatorMethod(object):"); + foreach (var item in OpMethodMap.Values) + { + string def = string.Format(" def {0}(self, other): pass", item.MethodName); + sb.AppendLine(def); + } + return sb.ToString(); + } + + private static PyObject GetOperatorType() + { + using (PyDict locals = new PyDict()) + { + // A hack way for getting typeobject.c::slotdefs + string code = GenerateDummyCode(); + // The resulting OperatorMethod class is stored in a PyDict. + PythonEngine.Exec(code, null, locals.Handle); + // Return the class itself, which is a type. + return locals.GetItem("OperatorMethod"); + } + } + + public static string ReversePyMethodName(string pyName) + { + return pyName.Insert(2, "r"); + } + + /// + /// Check if the method is performing a reverse operation. + /// + /// The operator method. + /// + public static bool IsReverse(MethodInfo method) + { + Type declaringType = method.DeclaringType; + Type leftOperandType = method.GetParameters()[0].ParameterType; + return leftOperandType != declaringType; + } + + public static void FilterMethods(MethodInfo[] methods, out MethodInfo[] forwardMethods, out MethodInfo[] reverseMethods) + { + List forwardMethodsList = new List(); + List reverseMethodsList = new List(); + foreach (var method in methods) + { + if (IsReverse(method)) + { + reverseMethodsList.Add(method); + } else + { + forwardMethodsList.Add(method); + } + + } + forwardMethods = forwardMethodsList.ToArray(); + reverseMethods = reverseMethodsList.ToArray(); + } + } +} diff --git a/src/runtime/platform/LibraryLoader.cs b/src/runtime/platform/LibraryLoader.cs index a6d88cd19..099b9d6cd 100644 --- a/src/runtime/platform/LibraryLoader.cs +++ b/src/runtime/platform/LibraryLoader.cs @@ -1,5 +1,7 @@ using System; using System.ComponentModel; +using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; namespace Python.Runtime.Platform @@ -15,18 +17,27 @@ interface ILibraryLoader static class LibraryLoader { - public static ILibraryLoader Get(OperatingSystemType os) + static ILibraryLoader _instance = null; + + public static ILibraryLoader Instance { - switch (os) + get { - case OperatingSystemType.Windows: - return new WindowsLoader(); - case OperatingSystemType.Darwin: - return new DarwinLoader(); - case OperatingSystemType.Linux: - return new LinuxLoader(); - default: - throw new PlatformNotSupportedException($"This operating system ({os}) is not supported"); + if (_instance == null) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + _instance = new WindowsLoader(); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + _instance = new LinuxLoader(); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + _instance = new DarwinLoader(); + else + throw new PlatformNotSupportedException( + "This operating system is not supported" + ); + } + + return _instance; } } } @@ -40,13 +51,12 @@ class LinuxLoader : ILibraryLoader public IntPtr Load(string dllToLoad) { - var filename = $"lib{dllToLoad}.so"; ClearError(); - var res = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); + var res = dlopen(dllToLoad, RTLD_NOW | RTLD_GLOBAL); if (res == IntPtr.Zero) { var err = GetError(); - throw new DllNotFoundException($"Could not load {filename} with flags RTLD_NOW | RTLD_GLOBAL: {err}"); + throw new DllNotFoundException($"Could not load {dllToLoad} with flags RTLD_NOW | RTLD_GLOBAL: {err}"); } return res; @@ -111,13 +121,12 @@ class DarwinLoader : ILibraryLoader public IntPtr Load(string dllToLoad) { - var filename = $"lib{dllToLoad}.dylib"; ClearError(); - var res = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); + var res = dlopen(dllToLoad, RTLD_NOW | RTLD_GLOBAL); if (res == IntPtr.Zero) { var err = GetError(); - throw new DllNotFoundException($"Could not load {filename} with flags RTLD_NOW | RTLD_GLOBAL: {err}"); + throw new DllNotFoundException($"Could not load {dllToLoad} with flags RTLD_NOW | RTLD_GLOBAL: {err}"); } return res; @@ -188,6 +197,15 @@ public IntPtr Load(string dllToLoad) public IntPtr GetFunction(IntPtr hModule, string procedureName) { + if (hModule == IntPtr.Zero) + { + foreach(var module in GetAllModules()) + { + var func = GetProcAddress(module, procedureName); + if (func != IntPtr.Zero) return func; + } + } + var res = WindowsLoader.GetProcAddress(hModule, procedureName); if (res == IntPtr.Zero) throw new MissingMethodException($"Failed to load symbol {procedureName}", new Win32Exception()); @@ -196,6 +214,24 @@ public IntPtr GetFunction(IntPtr hModule, string procedureName) public void Free(IntPtr hModule) => WindowsLoader.FreeLibrary(hModule); + static IntPtr[] GetAllModules() + { + var self = Process.GetCurrentProcess().Handle; + + uint bytes = 0; + var result = new IntPtr[0]; + if (!EnumProcessModules(self, result, bytes, out var needsBytes)) + throw new Win32Exception(); + while (bytes < needsBytes) + { + bytes = needsBytes; + result = new IntPtr[bytes / IntPtr.Size]; + if (!EnumProcessModules(self, result, bytes, out needsBytes)) + throw new Win32Exception(); + } + return result.Take((int)(needsBytes / IntPtr.Size)).ToArray(); + } + [DllImport(NativeDll, SetLastError = true)] static extern IntPtr LoadLibrary(string dllToLoad); @@ -204,5 +240,8 @@ public IntPtr GetFunction(IntPtr hModule, string procedureName) [DllImport(NativeDll)] static extern bool FreeLibrary(IntPtr hModule); + + [DllImport("Psapi.dll", SetLastError = true)] + static extern bool EnumProcessModules(IntPtr hProcess, [In, Out] IntPtr[] lphModule, uint lphModuleByteCount, out uint byteCountNeeded); } } diff --git a/src/runtime/platform/NativeCodePage.cs b/src/runtime/platform/NativeCodePage.cs deleted file mode 100644 index ab2ee3bcf..000000000 --- a/src/runtime/platform/NativeCodePage.cs +++ /dev/null @@ -1,291 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace Python.Runtime.Platform -{ - class NativeCodePageHelper - { - /// - /// 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(). - /// - [Obsolete] - public static string OperatingSystemName => PythonEngine.Platform; - - /// - /// 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(). - /// - [Obsolete] - public static string MachineName { get; private set; } - - /// - /// 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; - - - static readonly Dictionary OperatingSystemTypeMapping = new Dictionary() - { - { "Windows", OperatingSystemType.Windows }, - { "Darwin", OperatingSystemType.Darwin }, - { "Linux", OperatingSystemType.Linux }, - }; - - /// - /// Map lower-case version of the python machine name to the processor - /// type. There are aliases, e.g. x86_64 and amd64 are two names for - /// the same thing. Make sure to lower-case the search string, because - /// capitalization can differ. - /// - static readonly Dictionary MachineTypeMapping = new Dictionary() - { - ["i386"] = MachineType.i386, - ["i686"] = MachineType.i386, - ["x86"] = MachineType.i386, - ["x86_64"] = MachineType.x86_64, - ["amd64"] = MachineType.x86_64, - ["x64"] = MachineType.x86_64, - ["em64t"] = MachineType.x86_64, - ["armv7l"] = MachineType.armv7l, - ["armv8"] = MachineType.armv8, - ["aarch64"] = MachineType.aarch64, - }; - - /// - /// 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 (Machine) - { - case MachineType.i386: - return I386; - case MachineType.x86_64: - return X86_64; - default: - return null; - } - } - } - - /// - /// 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 (OperatingSystem) - { - case OperatingSystemType.Darwin: - return 0x1000; - case OperatingSystemType.Linux: - return 0x20; - default: - throw new NotImplementedException($"mmap is not supported on {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); - } - } - - /// - /// 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. - /// - public static void InitializePlatformData() - { - MachineName = SystemInfo.GetArchitecture(); - Machine = SystemInfo.GetMachineType(); - OperatingSystem = SystemInfo.GetSystemType(); - } - - internal static IMemoryMapper CreateMemoryMapper() - { - switch (OperatingSystem) - { - case OperatingSystemType.Darwin: - case OperatingSystemType.Linux: - return new UnixMemoryMapper(); - case OperatingSystemType.Windows: - return new WindowsMemoryMapper(); - default: - throw new NotImplementedException($"No support for {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); - } - } -} diff --git a/src/runtime/platform/Types.cs b/src/runtime/platform/Types.cs deleted file mode 100644 index 15235da5a..000000000 --- a/src/runtime/platform/Types.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Python.Runtime.Platform -{ - public enum MachineType - { - i386, - x86_64, - armv7l, - armv8, - aarch64, - Other - }; - - /// - /// Operating system type as reported by Python. - /// - public enum OperatingSystemType - { - Windows, - Darwin, - Linux, - Other - } - - - static class SystemInfo - { - public static MachineType GetMachineType() - { - return Runtime.IsWindows ? GetMachineType_Windows() : GetMachineType_Unix(); - } - - public static string GetArchitecture() - { - return Runtime.IsWindows ? GetArchName_Windows() : GetArchName_Unix(); - } - - public static OperatingSystemType GetSystemType() - { - if (Runtime.IsWindows) - { - return OperatingSystemType.Windows; - } - switch (PythonEngine.Platform) - { - case "linux": - return OperatingSystemType.Linux; - - case "darwin": - return OperatingSystemType.Darwin; - - default: - return OperatingSystemType.Other; - } - } - - #region WINDOWS - - static string GetArchName_Windows() - { - // https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details - return Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); - } - - static MachineType GetMachineType_Windows() - { - if (Runtime.Is32Bit) - { - return MachineType.i386; - } - switch (GetArchName_Windows()) - { - case "AMD64": - return MachineType.x86_64; - case "ARM64": - return MachineType.aarch64; - default: - return MachineType.Other; - } - } - - #endregion - - #region UNIX - - - [StructLayout(LayoutKind.Sequential)] - unsafe struct utsname_linux - { - const int NameLength = 65; - - /* Name of the implementation of the operating system. */ - public fixed byte sysname[NameLength]; - - /* Name of this node on the network. */ - public fixed byte nodename[NameLength]; - - /* Current release level of this implementation. */ - public fixed byte release[NameLength]; - - /* Current version level of this release. */ - public fixed byte version[NameLength]; - - /* Name of the hardware type the system is running on. */ - public fixed byte machine[NameLength]; - - // GNU extension - fixed byte domainname[NameLength]; /* NIS or YP domain name */ - } - - [StructLayout(LayoutKind.Sequential)] - unsafe struct utsname_darwin - { - const int NameLength = 256; - - /* Name of the implementation of the operating system. */ - public fixed byte sysname[NameLength]; - - /* Name of this node on the network. */ - public fixed byte nodename[NameLength]; - - /* Current release level of this implementation. */ - public fixed byte release[NameLength]; - - /* Current version level of this release. */ - public fixed byte version[NameLength]; - - /* Name of the hardware type the system is running on. */ - public fixed byte machine[NameLength]; - } - - [DllImport("libc")] - static extern int uname(IntPtr buf); - - - static unsafe string GetArchName_Unix() - { - switch (GetSystemType()) - { - case OperatingSystemType.Linux: - { - var buf = stackalloc utsname_linux[1]; - if (uname((IntPtr)buf) != 0) - { - return null; - } - return Marshal.PtrToStringAnsi((IntPtr)buf->machine); - } - - case OperatingSystemType.Darwin: - { - var buf = stackalloc utsname_darwin[1]; - if (uname((IntPtr)buf) != 0) - { - return null; - } - return Marshal.PtrToStringAnsi((IntPtr)buf->machine); - } - - default: - return null; - } - } - - static unsafe MachineType GetMachineType_Unix() - { - switch (GetArchName_Unix()) - { - case "x86_64": - case "em64t": - return Runtime.Is32Bit ? MachineType.i386 : MachineType.x86_64; - case "i386": - case "i686": - return MachineType.i386; - - case "armv7l": - return MachineType.armv7l; - case "armv8": - return Runtime.Is32Bit ? MachineType.armv7l : MachineType.armv8; - case "aarch64": - return Runtime.Is32Bit ? MachineType.armv7l : MachineType.aarch64; - - default: - return MachineType.Other; - } - } - - #endregion - } -} diff --git a/src/runtime/polyfill/ReflectionPolifills.cs b/src/runtime/polyfill/ReflectionPolyfills.cs similarity index 84% rename from src/runtime/polyfill/ReflectionPolifills.cs rename to src/runtime/polyfill/ReflectionPolyfills.cs index b9ce78d63..65f9b83de 100644 --- a/src/runtime/polyfill/ReflectionPolifills.cs +++ b/src/runtime/polyfill/ReflectionPolyfills.cs @@ -5,10 +5,8 @@ namespace Python.Runtime { - [Obsolete("This API is for internal use only")] - public static class ReflectionPolifills + internal static class ReflectionPolyfills { -#if NETSTANDARD public static AssemblyBuilder DefineDynamicAssembly(this AppDomain appDomain, AssemblyName assemblyName, AssemblyBuilderAccess assemblyBuilderAccess) { return AssemblyBuilder.DefineDynamicAssembly(assemblyName, assemblyBuilderAccess); @@ -16,9 +14,9 @@ public static AssemblyBuilder DefineDynamicAssembly(this AppDomain appDomain, As public static Type CreateType(this TypeBuilder typeBuilder) { - return typeBuilder.GetTypeInfo().GetType(); + return typeBuilder.CreateTypeInfo(); } -#endif + public static T GetCustomAttribute(this Type type) where T: Attribute { return type.GetCustomAttributes(typeof(T), inherit: false) diff --git a/src/runtime/propertyobject.cs b/src/runtime/propertyobject.cs index ac1d077f9..20061b358 100644 --- a/src/runtime/propertyobject.cs +++ b/src/runtime/propertyobject.cs @@ -4,15 +4,16 @@ namespace Python.Runtime { + using MaybeMethodInfo = MaybeMethodBase; /// /// Implements a Python descriptor type that manages CLR properties. /// [Serializable] internal class PropertyObject : ExtensionType { - private PropertyInfo info; - private MethodInfo getter; - private MethodInfo setter; + private MaybeMemberInfo info; + private MaybeMethodInfo getter; + private MaybeMethodInfo setter; [StrongNameIdentityPermission(SecurityAction.Assert)] public PropertyObject(PropertyInfo md) @@ -31,7 +32,12 @@ public PropertyObject(PropertyInfo md) public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { var self = (PropertyObject)GetManagedObject(ds); - MethodInfo getter = self.getter; + if (!self.info.Valid) + { + return Exceptions.RaiseTypeError(self.info.DeletedMessage); + } + var info = self.info.Value; + MethodInfo getter = self.getter.UnsafeValue; object result; @@ -51,8 +57,8 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) try { - result = self.info.GetValue(null, null); - return Converter.ToPython(result, self.info.PropertyType); + result = info.GetValue(null, null); + return Converter.ToPython(result, info.PropertyType); } catch (Exception e) { @@ -68,8 +74,8 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) try { - result = self.info.GetValue(co.inst, null); - return Converter.ToPython(result, self.info.PropertyType); + result = info.GetValue(co.inst, null); + return Converter.ToPython(result, info.PropertyType); } catch (Exception e) { @@ -91,7 +97,14 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) public new static int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val) { var self = (PropertyObject)GetManagedObject(ds); - MethodInfo setter = self.setter; + if (!self.info.Valid) + { + Exceptions.RaiseTypeError(self.info.DeletedMessage); + return -1; + } + var info = self.info.Value; + + MethodInfo setter = self.setter.UnsafeValue; object newval; if (val == IntPtr.Zero) @@ -107,7 +120,7 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) } - if (!Converter.ToManaged(val, self.info.PropertyType, out newval, true)) + if (!Converter.ToManaged(val, info.PropertyType, out newval, true)) { return -1; } @@ -133,11 +146,11 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) Exceptions.RaiseTypeError("invalid target"); return -1; } - self.info.SetValue(co.inst, newval, null); + info.SetValue(co.inst, newval, null); } else { - self.info.SetValue(null, newval, null); + info.SetValue(null, newval, null); } return 0; } @@ -159,7 +172,7 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) public static IntPtr tp_repr(IntPtr ob) { var self = (PropertyObject)GetManagedObject(ob); - return Runtime.PyString_FromString($""); + return Runtime.PyString_FromString($""); } } } diff --git a/src/runtime/pybuffer.cs b/src/runtime/pybuffer.cs index eadf4e2a7..cf657a033 100644 --- a/src/runtime/pybuffer.cs +++ b/src/runtime/pybuffer.cs @@ -7,7 +7,7 @@ namespace Python.Runtime { - public sealed class PyBuffer : IPyDisposable + public sealed class PyBuffer : IDisposable { private PyObject _exporter; private Py_buffer _view; @@ -236,7 +236,7 @@ private void Dispose(bool disposing) { return; } - Finalizer.Instance.AddFinalizedObject(this); + Finalizer.Instance.AddFinalizedObject(ref _view.obj); } /// @@ -248,10 +248,5 @@ public void Dispose() Dispose(true); GC.SuppressFinalize(this); } - - public IntPtr[] GetTrackedHandles() - { - return new IntPtr[] { _view.obj }; - } } } diff --git a/src/runtime/pydict.cs b/src/runtime/pydict.cs index ade873f7a..4b850a9f9 100644 --- a/src/runtime/pydict.cs +++ b/src/runtime/pydict.cs @@ -22,6 +22,7 @@ public PyDict(IntPtr ptr) : base(ptr) { } + internal PyDict(BorrowedReference reference) : base(reference) { } /// /// PyDict Constructor @@ -103,12 +104,12 @@ public bool HasKey(string key) /// public PyObject Keys() { - IntPtr items = Runtime.PyDict_Keys(obj); - if (items == IntPtr.Zero) + using var items = Runtime.PyDict_Keys(Reference); + if (items.IsNull()) { throw new PythonException(); } - return new PyObject(items); + return items.MoveToPyObject(); } @@ -137,7 +138,7 @@ public PyObject Values() /// public PyObject Items() { - var items = Runtime.PyDict_Items(this.obj); + var items = Runtime.PyDict_Items(this.Reference); try { if (items.IsNull()) @@ -179,7 +180,7 @@ public PyDict Copy() /// public void Update(PyObject other) { - int result = Runtime.PyDict_Update(obj, other.obj); + int result = Runtime.PyDict_Update(Reference, other.Reference); if (result < 0) { throw new PythonException(); diff --git a/src/runtime/pyfloat.cs b/src/runtime/pyfloat.cs index b07b95de1..a1752ff68 100644 --- a/src/runtime/pyfloat.cs +++ b/src/runtime/pyfloat.cs @@ -66,9 +66,9 @@ private static IntPtr FromString(string value) { using (var s = new PyString(value)) { - IntPtr val = Runtime.PyFloat_FromString(s.obj, IntPtr.Zero); + NewReference val = Runtime.PyFloat_FromString(s.Reference); PythonException.ThrowIfIsNull(val); - return val; + return val.DangerousMoveToPointerOrNull(); } } diff --git a/src/runtime/pyint.cs b/src/runtime/pyint.cs index e49e62a8a..7b02c68e5 100644 --- a/src/runtime/pyint.cs +++ b/src/runtime/pyint.cs @@ -69,7 +69,6 @@ public PyInt(int value) : base(FromInt(value)) /// /// Creates a new Python int from a uint32 value. /// - [CLSCompliant(false)] public PyInt(uint value) : base(FromLong(value)) { } @@ -99,7 +98,6 @@ private static IntPtr FromLong(long value) /// /// Creates a new Python int from a uint64 value. /// - [CLSCompliant(false)] public PyInt(ulong value) : base(FromLong((long)value)) { } @@ -122,7 +120,6 @@ public PyInt(short value) : this((int)value) /// /// Creates a new Python int from a uint16 value. /// - [CLSCompliant(false)] public PyInt(ushort value) : this((int)value) { } @@ -145,7 +142,6 @@ public PyInt(byte value) : this((int)value) /// /// Creates a new Python int from an sbyte value. /// - [CLSCompliant(false)] public PyInt(sbyte value) : this((int)value) { } @@ -153,7 +149,7 @@ public PyInt(sbyte value) : this((int)value) private static IntPtr FromString(string value) { - IntPtr val = Runtime.PyInt_FromString(value, IntPtr.Zero, 0); + IntPtr val = Runtime.PyLong_FromString(value, IntPtr.Zero, 0); PythonException.ThrowIfIsNull(val); return val; } diff --git a/src/runtime/pyiter.cs b/src/runtime/pyiter.cs index 4ad761db7..2016ef4f8 100644 --- a/src/runtime/pyiter.cs +++ b/src/runtime/pyiter.cs @@ -9,7 +9,7 @@ namespace Python.Runtime /// PY3: https://docs.python.org/3/c-api/iterator.html /// for details. /// - public class PyIter : PyObject, IEnumerator + public class PyIter : PyObject, IEnumerator { private PyObject _current; @@ -46,41 +46,35 @@ public static PyIter GetIter(PyObject iterable) protected override void Dispose(bool disposing) { - if (null != _current) - { - _current.Dispose(); - _current = null; - } + _current = null; base.Dispose(disposing); } public bool MoveNext() { - // dispose of the previous object, if there was one - if (null != _current) + NewReference next = Runtime.PyIter_Next(Reference); + if (next.IsNull()) { - _current.Dispose(); - _current = null; - } + if (Exceptions.ErrorOccurred()) + { + throw new PythonException(); + } - IntPtr next = Runtime.PyIter_Next(obj); - if (next == IntPtr.Zero) - { + // stop holding the previous object, if there was one + _current = null; return false; } - _current = new PyObject(next); + _current = next.MoveToPyObject(); return true; } public void Reset() { - //Not supported in python. + throw new NotSupportedException(); } - public object Current - { - get { return _current; } - } + public PyObject Current => _current; + object System.Collections.IEnumerator.Current => _current; } } diff --git a/src/runtime/pylong.cs b/src/runtime/pylong.cs index 87cc7d2a5..fdfd26aba 100644 --- a/src/runtime/pylong.cs +++ b/src/runtime/pylong.cs @@ -69,13 +69,12 @@ public PyLong(int value) : base(FromInt(value)) /// /// Creates a new PyLong from a uint32 value. /// - [CLSCompliant(false)] public PyLong(uint value) : base(FromInt((int)value)) { } - private static IntPtr FromLong(long value) + internal static IntPtr FromLong(long value) { IntPtr val = Runtime.PyLong_FromLongLong(value); PythonException.ThrowIfIsNull(val); @@ -105,7 +104,6 @@ private static IntPtr FromULong(ulong value) /// /// Creates a new PyLong from a uint64 value. /// - [CLSCompliant(false)] public PyLong(ulong value) : base(FromULong(value)) { } @@ -128,7 +126,6 @@ public PyLong(short value) : base(FromInt((int)value)) /// /// Creates a new PyLong from an uint16 value. /// - [CLSCompliant(false)] public PyLong(ushort value) : base(FromInt((int)value)) { } @@ -151,7 +148,6 @@ public PyLong(byte value) : base(FromInt((int)value)) /// /// Creates a new PyLong from an sbyte value. /// - [CLSCompliant(false)] public PyLong(sbyte value) : base(FromInt((int)value)) { } @@ -250,7 +246,7 @@ public int ToInt32() /// public long ToInt64() { - return Runtime.PyLong_AsLongLong(obj); + return Runtime.PyExplicitlyConvertToInt64(obj); } } } diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs index 9328312ce..aaffa29fe 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -8,11 +8,6 @@ namespace Python.Runtime { - public interface IPyDisposable : IDisposable - { - IntPtr[] GetTrackedHandles(); - } - /// /// Represents a generic Python object. The methods of this class are /// generally equivalent to the Python "abstract object API". See @@ -21,7 +16,7 @@ public interface IPyDisposable : IDisposable /// for details. /// [Serializable] - public partial class PyObject : DynamicObject, IEnumerable, IPyDisposable + public partial class PyObject : DynamicObject, IEnumerable, IDisposable { #if TRACE_ALLOC /// @@ -54,6 +49,19 @@ public PyObject(IntPtr ptr) #endif } + [Obsolete("for testing purposes only")] + internal PyObject(IntPtr ptr, bool skipCollect) + { + if (ptr == IntPtr.Zero) throw new ArgumentNullException(nameof(ptr)); + + obj = ptr; + if (!skipCollect) + Finalizer.Instance.ThrottledCollect(); +#if TRACE_ALLOC + Traceback = new StackTrace(1); +#endif + } + /// /// Creates new pointing to the same object as /// the . Increments refcount, allowing @@ -78,7 +86,7 @@ internal PyObject(BorrowedReference reference) { return; } - Finalizer.Instance.AddFinalizedObject(this); + Finalizer.Instance.AddFinalizedObject(ref obj); } @@ -111,7 +119,9 @@ public static PyObject FromManagedObject(object ob) Runtime.XIncref(Runtime.PyNone); return new PyObject(Runtime.PyNone); } - IntPtr op = CLRObject.GetInstHandle(ob); + IntPtr op = typeof(Exception).IsAssignableFrom(ob.GetType()) ? + Exceptions.GetExceptHandle((Exception)ob) + : CLRObject.GetInstHandle(ob); return new PyObject(op); } @@ -126,9 +136,9 @@ public static PyObject FromManagedObject(object ob) public object AsManagedObject(Type t) { object result; - if (!Converter.ToManaged(obj, t, out result, false)) + if (!Converter.ToManaged(obj, t, out result, true)) { - throw new InvalidCastException("cannot convert object to target type"); + throw new InvalidCastException("cannot convert object to target type", new PythonException()); } return result; } @@ -146,14 +156,10 @@ public T As() { 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; + return (T)AsManagedObject(typeof(T)); } + internal bool IsDisposed => obj == IntPtr.Zero; /// /// Dispose Method @@ -202,6 +208,10 @@ protected virtual void Dispose(bool disposing) Runtime.XDecref(this.obj); } } + else + { + throw new InvalidOperationException("Runtime is already finalizing"); + } this.obj = IntPtr.Zero; } @@ -211,11 +221,6 @@ public void Dispose() GC.SuppressFinalize(this); } - public IntPtr[] GetTrackedHandles() - { - return new IntPtr[] { obj }; - } - /// /// GetPythonType Method /// @@ -255,7 +260,7 @@ public bool HasAttr(string name) { if (name == null) throw new ArgumentNullException(nameof(name)); - return Runtime.PyObject_HasAttrString(obj, name) != 0; + return Runtime.PyObject_HasAttrString(Reference, name) != 0; } @@ -270,7 +275,7 @@ public bool HasAttr(PyObject name) { if (name == null) throw new ArgumentNullException(nameof(name)); - return Runtime.PyObject_HasAttr(obj, name.obj) != 0; + return Runtime.PyObject_HasAttr(Reference, name.Reference) != 0; } @@ -698,10 +703,11 @@ public PyObject GetIterator() /// python object to be iterated over in C#. A PythonException will be /// raised if the object is not iterable. /// - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return PyIter.GetIter(this); } + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); /// diff --git a/src/runtime/pyscope.cs b/src/runtime/pyscope.cs index fee78b40a..9d68b76fa 100644 --- a/src/runtime/pyscope.cs +++ b/src/runtime/pyscope.cs @@ -22,22 +22,21 @@ public class PyGILAttribute : Attribute } [PyGIL] - public class PyScope : DynamicObject, IPyDisposable + public class PyScope : DynamicObject, IDisposable { public readonly string Name; /// /// the python Module object the scope associated with. /// - internal readonly IntPtr obj; + readonly PyObject obj; + internal BorrowedReference Reference => obj.Reference; /// - /// the variable dict of the scope. + /// the variable dict of the scope. Borrowed. /// internal readonly IntPtr variables; - - private bool _isDisposed; - private bool _finalized = false; + internal BorrowedReference VarsRef => new BorrowedReference(variables); /// /// The Manager this scope associated with. @@ -56,20 +55,20 @@ public class PyScope : DynamicObject, IPyDisposable /// /// Create a scope based on a Python Module. /// - internal PyScope(IntPtr ptr, PyScopeManager manager) + internal PyScope(ref NewReference 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; + obj = ptr.MoveToPyObject(); //Refcount of the variables not increase - variables = Runtime.PyModule_GetDict(obj); + variables = Runtime.PyModule_GetDict(Reference).DangerousGetAddress(); PythonException.ThrowIfIsNull(variables); - int res = Runtime.PyDict_SetItemString( - variables, "__builtins__", + int res = Runtime.PyDict_SetItem( + VarsRef, PyIdentifier.__builtins__, Runtime.PyEval_GetBuiltins() ); PythonException.ThrowIfIsNotZero(res); @@ -79,7 +78,6 @@ internal PyScope(IntPtr ptr, PyScopeManager manager) /// /// return the variable dict of the scope. /// - /// public PyDict Variables() { Runtime.XIncref(variables); @@ -134,7 +132,7 @@ public dynamic Import(string name, string asname = null) /// public void Import(PyScope scope, string asname) { - this.Set(asname, scope.obj); + this.SetPyValue(asname, scope.obj.Handle); } /// @@ -184,7 +182,7 @@ public void ImportAll(string name) /// public void ImportAll(PyScope scope) { - int result = Runtime.PyDict_Update(variables, scope.variables); + int result = Runtime.PyDict_Update(VarsRef, scope.VarsRef); if (result < 0) { throw new PythonException(); @@ -203,8 +201,8 @@ public void ImportAll(PyObject module) { throw new PyScopeException("object is not a module"); } - var module_dict = Runtime.PyModule_GetDict(module.obj); - int result = Runtime.PyDict_Update(variables, module_dict); + var module_dict = Runtime.PyModule_GetDict(module.Reference); + int result = Runtime.PyDict_Update(VarsRef, module_dict); if (result < 0) { throw new PythonException(); @@ -219,7 +217,7 @@ public void ImportAll(PyObject module) /// public void ImportAll(PyDict dict) { - int result = Runtime.PyDict_Update(variables, dict.obj); + int result = Runtime.PyDict_Update(VarsRef, dict.Reference); if (result < 0) { throw new PythonException(); @@ -277,10 +275,10 @@ public T Execute(PyObject script, PyDict locals = null) public PyObject Eval(string code, PyDict locals = null) { Check(); - IntPtr _locals = locals == null ? variables : locals.obj; + BorrowedReference _locals = locals == null ? VarsRef : locals.Reference; NewReference reference = Runtime.PyRun_String( - code, RunFlagType.Eval, variables, _locals + code, RunFlagType.Eval, VarsRef, _locals ); PythonException.ThrowIfIsNull(reference); return reference.MoveToPyObject(); @@ -310,11 +308,11 @@ public T Eval(string code, PyDict locals = null) public void Exec(string code, PyDict locals = null) { Check(); - IntPtr _locals = locals == null ? variables : locals.obj; - Exec(code, variables, _locals); + BorrowedReference _locals = locals == null ? VarsRef : locals.Reference; + Exec(code, VarsRef, _locals); } - private void Exec(string code, IntPtr _globals, IntPtr _locals) + private void Exec(string code, BorrowedReference _globals, BorrowedReference _locals) { NewReference reference = Runtime.PyRun_String( code, RunFlagType.File, _globals, _locals @@ -333,11 +331,11 @@ private void Exec(string code, IntPtr _globals, IntPtr _locals) public void Set(string name, object value) { IntPtr _value = Converter.ToPython(value, value?.GetType()); - Set(name, _value); + SetPyValue(name, _value); Runtime.XDecref(_value); } - private void Set(string name, IntPtr value) + private void SetPyValue(string name, IntPtr value) { Check(); using (var pyKey = new PyString(name)) @@ -505,7 +503,7 @@ public override bool TrySetMember(SetMemberBinder binder, object value) private void Check() { - if (_isDisposed) + if (this.obj.IsDisposed) { throw new PyScopeException($"The scope of name '{Name}' object has been disposed"); } @@ -513,28 +511,8 @@ private void Check() public void Dispose() { - if (_isDisposed) - { - return; - } - _isDisposed = true; - Runtime.XDecref(obj); this.OnDispose?.Invoke(this); - } - - public IntPtr[] GetTrackedHandles() - { - return new IntPtr[] { obj }; - } - - ~PyScope() - { - if (_finalized || _isDisposed) - { - return; - } - _finalized = true; - Finalizer.Instance.AddFinalizedObject(this); + this.obj.Dispose(); } } @@ -556,11 +534,11 @@ internal PyScope NewScope(string name) name = ""; } var module = Runtime.PyModule_New(name); - if (module == IntPtr.Zero) + if (module.IsNull()) { throw new PythonException(); } - return new PyScope(module, this); + return new PyScope(ref module, this); } /// diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 1d688ef9a..35ea3f6d2 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -198,28 +198,16 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true, Py.SetArgv(args); } - if (mode == ShutdownMode.Normal) - { - // TOOD: Check if this can be remove completely or not. - // register the atexit callback (this doesn't use Py_AtExit as the C atexit - // callbacks are called after python is fully finalized but the python ones - // are called while the python engine is still running). - //string code = - // "import atexit, clr\n" + - // "atexit.register(clr._AtExit)\n"; - //PythonEngine.Exec(code); - } - // Load the clr.py resource into the clr module - IntPtr clr = Python.Runtime.ImportHook.GetCLRModule(); - IntPtr clr_dict = Runtime.PyModule_GetDict(clr); + NewReference clr = Python.Runtime.ImportHook.GetCLRModule(); + BorrowedReference clr_dict = Runtime.PyModule_GetDict(clr); var locals = new PyDict(); try { - IntPtr module = Runtime.PyImport_AddModule("clr._extras"); - IntPtr module_globals = Runtime.PyModule_GetDict(module); - IntPtr builtins = Runtime.PyEval_GetBuiltins(); + BorrowedReference module = Runtime.PyImport_AddModule("clr._extras"); + BorrowedReference module_globals = Runtime.PyModule_GetDict(module); + BorrowedReference builtins = Runtime.PyEval_GetBuiltins(); Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins); Assembly assembly = Assembly.GetExecutingAssembly(); @@ -228,7 +216,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true, { // add the contents of clr.py to the module string clr_py = reader.ReadToEnd(); - Exec(clr_py, module_globals, locals.Handle); + Exec(clr_py, module_globals, locals.Reference); } // add the imported module to the clr module, and copy the API functions @@ -240,7 +228,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true, if (!key.ToString().StartsWith("_") || key.ToString().Equals("__version__")) { PyObject value = locals[key]; - Runtime.PyDict_SetItem(clr_dict, key.Handle, value.Handle); + Runtime.PyDict_SetItem(clr_dict, key.Reference, value.Reference); value.Dispose(); } key.Dispose(); @@ -266,7 +254,7 @@ public static IntPtr InitExt() { try { - Initialize(setSysArgv: false); + Initialize(setSysArgv: false, mode: ShutdownMode.Extension); // Trickery - when the import hook is installed into an already // running Python, the standard import machinery is still in @@ -305,7 +293,8 @@ public static IntPtr InitExt() return IntPtr.Zero; } - return Python.Runtime.ImportHook.GetCLRModule(); + return Python.Runtime.ImportHook.GetCLRModule() + .DangerousMoveToPointerOrNull(); } /// @@ -544,7 +533,9 @@ public static PyObject Compile(string code, string filename = "", RunFlagType mo /// public static PyObject Eval(string code, IntPtr? globals = null, IntPtr? locals = null) { - PyObject result = RunString(code, globals, locals, RunFlagType.Eval); + var globalsRef = new BorrowedReference(globals.GetValueOrDefault()); + var localsRef = new BorrowedReference(locals.GetValueOrDefault()); + PyObject result = RunString(code, globalsRef, localsRef, RunFlagType.Eval); return result; } @@ -558,15 +549,54 @@ public static PyObject Eval(string code, IntPtr? globals = null, IntPtr? locals /// public static void Exec(string code, IntPtr? globals = null, IntPtr? locals = null) { - using (PyObject result = RunString(code, globals, locals, RunFlagType.File)) + var globalsRef = new BorrowedReference(globals.GetValueOrDefault()); + var localsRef = new BorrowedReference(locals.GetValueOrDefault()); + using PyObject result = RunString(code, globalsRef, localsRef, RunFlagType.File); + if (result.obj != Runtime.PyNone) { - if (result.obj != Runtime.PyNone) - { - throw new PythonException(); - } + throw new PythonException(); + } + } + /// + /// Exec Method + /// + /// + /// Run a string containing Python code. + /// It's a subset of Python exec function. + /// + internal static void Exec(string code, BorrowedReference globals, BorrowedReference locals = default) + { + using PyObject result = RunString(code, globals: globals, locals: locals, RunFlagType.File); + if (result.obj != Runtime.PyNone) + { + throw new PythonException(); } } + /// + /// Gets the Python thread ID. + /// + /// The Python thread ID. + public static ulong GetPythonThreadID() + { + dynamic threading = Py.Import("threading"); + return threading.InvokeMethod("get_ident"); + } + + /// + /// Interrupts the execution of a thread. + /// + /// The Python thread ID. + /// The number of thread states modified; this is normally one, but will be zero if the thread id isn’t found. + public static int Interrupt(ulong pythonThreadID) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Runtime.PyThreadState_SetAsyncExcLLP64((uint)pythonThreadID, Exceptions.KeyboardInterrupt); + } + + return Runtime.PyThreadState_SetAsyncExcLP64(pythonThreadID, Exceptions.KeyboardInterrupt); + } /// /// RunString Method. Function has been deprecated and will be removed. @@ -575,7 +605,7 @@ public static void Exec(string code, IntPtr? globals = null, IntPtr? locals = nu [Obsolete("RunString is deprecated and will be removed. Use Exec/Eval/RunSimpleString instead.")] public static PyObject RunString(string code, IntPtr? globals = null, IntPtr? locals = null) { - return RunString(code, globals, locals, RunFlagType.File); + return RunString(code, new BorrowedReference(globals.GetValueOrDefault()), new BorrowedReference(locals.GetValueOrDefault()), RunFlagType.File); } /// @@ -586,20 +616,19 @@ public static PyObject RunString(string code, IntPtr? globals = null, IntPtr? lo /// executing the code string as a PyObject instance, or null if /// an exception was raised. /// - internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals, RunFlagType flag) + internal static PyObject RunString(string code, BorrowedReference globals, BorrowedReference locals, RunFlagType flag) { - var borrowedGlobals = true; - if (globals == null) + NewReference tempGlobals = default; + if (globals.IsNull) { globals = Runtime.PyEval_GetGlobals(); - if (globals == IntPtr.Zero) + if (globals.IsNull) { - globals = Runtime.PyDict_New(); - Runtime.PyDict_SetItemString( - globals.Value, "__builtins__", + globals = tempGlobals = NewReference.DangerousFromPointer(Runtime.PyDict_New()); + Runtime.PyDict_SetItem( + globals, PyIdentifier.__builtins__, Runtime.PyEval_GetBuiltins() ); - borrowedGlobals = false; } } @@ -611,17 +640,14 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals, try { NewReference result = Runtime.PyRun_String( - code, flag, globals.Value, locals.Value + code, flag, globals, locals ); PythonException.ThrowIfIsNull(result); return result.MoveToPyObject(); } finally { - if (!borrowedGlobals) - { - Runtime.XDecref(globals.Value); - } + tempGlobals.Dispose(); } } } diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs index 893bd9491..7dd4f0811 100644 --- a/src/runtime/pythonexception.cs +++ b/src/runtime/pythonexception.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Runtime.CompilerServices; using System.Text; @@ -8,14 +9,14 @@ namespace Python.Runtime /// Provides a managed interface to exceptions thrown by the Python /// runtime. /// - public class PythonException : System.Exception, IPyDisposable + public class PythonException : System.Exception, IDisposable { private IntPtr _pyType = IntPtr.Zero; private IntPtr _pyValue = IntPtr.Zero; private IntPtr _pyTB = IntPtr.Zero; - private string _tb = ""; - private string _message = ""; - private string _pythonTypeName = ""; + private readonly string _tb = ""; + private readonly string _message = ""; + private readonly string _pythonTypeName = ""; private bool disposed = false; private bool _finalized = false; @@ -36,6 +37,7 @@ public PythonException() _pythonTypeName = type; + // TODO: If pyValue has a __cause__ attribute, then we could set this.InnerException to the equivalent managed exception. Runtime.XIncref(_pyValue); using (var pyValue = new PyObject(_pyValue)) { @@ -43,16 +45,23 @@ public PythonException() } _message = type + " : " + message; } + if (_pyTB != IntPtr.Zero) { - using (PyObject tb_module = PythonEngine.ImportModule("traceback")) - { - Runtime.XIncref(_pyTB); - using (var pyTB = new PyObject(_pyTB)) - { - _tb = tb_module.InvokeMethod("format_tb", pyTB).ToString(); - } + using PyObject tb_module = PythonEngine.ImportModule("traceback"); + + Runtime.XIncref(_pyTB); + using var pyTB = new PyObject(_pyTB); + + using var tbList = tb_module.InvokeMethod("format_tb", pyTB); + + var sb = new StringBuilder(); + // Reverse Python's traceback list to match the order used in C# + // stacktraces + foreach (var line in tbList.Reverse()) { + sb.Append(line.ToString()); } + _tb = sb.ToString(); } PythonEngine.ReleaseLock(gs); } @@ -67,7 +76,9 @@ public PythonException() return; } _finalized = true; - Finalizer.Instance.AddFinalizedObject(this); + Finalizer.Instance.AddFinalizedObject(ref _pyType); + Finalizer.Instance.AddFinalizedObject(ref _pyValue); + Finalizer.Instance.AddFinalizedObject(ref _pyTB); } /// @@ -133,10 +144,7 @@ public override string Message /// /// A string representing the python exception stack trace. /// - public override string StackTrace - { - get { return _tb + base.StackTrace; } - } + public override string StackTrace => $"{_tb}===\n{base.StackTrace}"; /// /// Python error type name. @@ -146,6 +154,26 @@ public string PythonTypeName get { return _pythonTypeName; } } + /// + /// Replaces PyValue with an instance of PyType, if PyValue is not already an instance of PyType. + /// Often PyValue is a string and this method will replace it with a proper exception object. + /// Must not be called when an error is set. + /// + public void Normalize() + { + IntPtr gs = PythonEngine.AcquireLock(); + try + { + if (Exceptions.ErrorOccurred()) throw new InvalidOperationException("Cannot normalize when an error is set"); + // If an error is set and this PythonException is unnormalized, the error will be cleared and the PythonException will be replaced by a different error. + Runtime.PyErr_NormalizeException(ref _pyType, ref _pyValue, ref _pyTB); + } + finally + { + PythonEngine.ReleaseLock(gs); + } + } + /// /// Formats this PythonException object into a message as would be printed /// out via the Python console. See traceback.format_exception @@ -158,12 +186,18 @@ public string Format() { if (_pyTB != IntPtr.Zero && _pyType != IntPtr.Zero && _pyValue != IntPtr.Zero) { - Runtime.XIncref(_pyType); - Runtime.XIncref(_pyValue); - Runtime.XIncref(_pyTB); - using (PyObject pyType = new PyObject(_pyType)) - using (PyObject pyValue = new PyObject(_pyValue)) - using (PyObject pyTB = new PyObject(_pyTB)) + IntPtr tb = _pyTB; + IntPtr type = _pyType; + IntPtr value = _pyValue; + + Runtime.XIncref(type); + Runtime.XIncref(value); + Runtime.XIncref(tb); + Runtime.PyErr_NormalizeException(ref type, ref value, ref tb); + + using (PyObject pyType = new PyObject(type)) + using (PyObject pyValue = new PyObject(value)) + using (PyObject pyTB = new PyObject(tb)) using (PyObject tb_mod = PythonEngine.ImportModule("traceback")) { var buffer = new StringBuilder(); @@ -233,11 +267,6 @@ public void Dispose() } } - public IntPtr[] GetTrackedHandles() - { - return new IntPtr[] { _pyType, _pyValue, _pyTB }; - } - /// /// Matches Method /// @@ -250,6 +279,7 @@ public static bool Matches(IntPtr ob) return Runtime.PyErr_ExceptionMatches(ob) != 0; } + [System.Diagnostics.DebuggerHidden] public static void ThrowIfIsNull(IntPtr ob) { if (ob == IntPtr.Zero) @@ -258,6 +288,7 @@ public static void ThrowIfIsNull(IntPtr ob) } } + [System.Diagnostics.DebuggerHidden] internal static void ThrowIfIsNull(BorrowedReference reference) { if (reference.IsNull) @@ -266,6 +297,7 @@ internal static void ThrowIfIsNull(BorrowedReference reference) } } + [System.Diagnostics.DebuggerHidden] public static void ThrowIfIsNotZero(int value) { if (value != 0) diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 83d404f9d..cd581d381 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -6,8 +6,10 @@ using System.Text; using System.Threading; using System.Collections.Generic; +using Python.Runtime.Native; using Python.Runtime.Platform; using System.Linq; +using static System.FormattableString; namespace Python.Runtime { @@ -16,72 +18,48 @@ namespace Python.Runtime /// the responsibility of the caller to have acquired the GIL /// before calling any of these methods. /// - public class Runtime + public unsafe 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 - internal const int _UCS = 4; - - /// - /// EntryPoint to be used in DllImport to map to correct Unicode - /// methods prior to PEP393. Only used for PY27. - /// - private const string PyUnicodeEntryPoint = "PyUnicodeUCS4_"; -#elif UCS2 - internal const int _UCS = 2; - - /// - /// EntryPoint to be used in DllImport to map to correct Unicode - /// methods prior to PEP393. Only used for PY27. - /// - private const string PyUnicodeEntryPoint = "PyUnicodeUCS2_"; -#else -#error You must define either UCS2 or UCS4! -#endif - -#if PYTHON36 - const string _minor = "6"; -#elif PYTHON37 - const string _minor = "7"; -#elif PYTHON38 - const string _minor = "8"; -#else -#error You must define one of PYTHON36 to PYTHON38 -#endif + public static string PythonDLL + { + get => _PythonDll; + set + { + if (_isInitialized) + throw new InvalidOperationException("This property must be set before runtime is initialized"); + _PythonDll = value; + } + } -#if WINDOWS - internal const string dllBase = "python3" + _minor; -#else - internal const string dllBase = "python3." + _minor; -#endif + static string _PythonDll = GetDefaultDllName(); + private static string GetDefaultDllName() + { + string dll = Environment.GetEnvironmentVariable("PYTHONNET_PYDLL"); + if (dll is not null) return dll; -#if PYTHON_WITH_PYDEBUG - internal const string dllWithPyDebug = "d"; -#else - internal const string dllWithPyDebug = ""; -#endif -#if PYTHON_WITH_PYMALLOC - internal const string dllWithPyMalloc = "m"; -#else - internal const string dllWithPyMalloc = ""; -#endif + try + { + LibraryLoader.Instance.GetFunction(IntPtr.Zero, "PyUnicode_GetMax"); + return null; + } catch (MissingMethodException) { } - // 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. + string verString = Environment.GetEnvironmentVariable("PYTHONNET_PYVER"); + if (!Version.TryParse(verString, out var version)) return null; - public static readonly string PythonDLL = _PythonDll; + return GetDefaultDllName(version); + } -#if PYTHON_WITHOUT_ENABLE_SHARED && !NETSTANDARD - internal const string _PythonDll = "__Internal"; -#else - internal const string _PythonDll = dllBase + dllWithPyDebug + dllWithPyMalloc; -#endif + private static string GetDefaultDllName(Version version) + { + string prefix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "" : "lib"; + string suffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? Invariant($"{version.Major}{version.Minor}") + : Invariant($"{version.Major}.{version.Minor}"); + string ext = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".dll" + : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? ".dylib" + : ".so"; + return prefix + "python" + suffix + ext; + } // set to true when python is finalizing internal static object IsFinalizingLock = new object(); @@ -99,11 +77,6 @@ public class Runtime public static int MainManagedThreadId { get; private set; } - /// - /// Encoding to use to convert Unicode to/from Managed to Native - /// - internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32; - public static ShutdownMode ShutdownMode { get; internal set; } private static PyReferenceCollection _pyRefs = new PyReferenceCollection(); @@ -111,11 +84,13 @@ internal static Version PyVersion { get { - var versionTuple = new PyTuple(PySys_GetObject("version_info")); - var major = versionTuple[0].As(); - var minor = versionTuple[1].As(); - var micro = versionTuple[2].As(); - return new Version(major, minor, micro); + using (var versionTuple = new PyTuple(PySys_GetObject("version_info"))) + { + var major = versionTuple[0].As(); + var minor = versionTuple[1].As(); + var micro = versionTuple[2].As(); + return new Version(major, minor, micro); + } } } @@ -123,7 +98,7 @@ internal static Version PyVersion /// /// Initialize the runtime... /// - /// Always call this method from the Main thread. After the + /// Always call this method from the Main thread. After the /// first call to this method, the main thread has acquired the GIL. internal static void Initialize(bool initSigs = false, ShutdownMode mode = ShutdownMode.Default) { @@ -158,11 +133,20 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd // If we're coming back from a domain reload or a soft shutdown, // we have previously released the thread state. Restore the main // thread state here. - PyGILState_Ensure(); + if (mode != ShutdownMode.Extension) + { + PyGILState_Ensure(); + } } MainManagedThreadId = Thread.CurrentThread.ManagedThreadId; IsFinalizing = false; + InternString.Initialize(); + + InitPyMembers(); + + ABI.Initialize(PyVersion, + pyType: new BorrowedReference(PyTypeType)); GenericUtil.Reset(); PyScopeManager.Reset(); @@ -170,15 +154,9 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd ClassDerivedObject.Reset(); TypeManager.Initialize(); - InitPyMembers(); - - // 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. - NativeCodePageHelper.InitializePlatformData(); - // Initialize modules that depend on the runtime class. AssemblyManager.Initialize(); + OperatorMethod.Initialize(); if (mode == ShutdownMode.Reload && RuntimeData.HasStashData()) { RuntimeData.RestoreRuntimeData(); @@ -237,7 +215,7 @@ private static void InitPyMembers() // a wrapper_descriptor, even though dict.__setitem__ is. // // object.__init__ seems safe, though. - op = PyObject_GetAttrString(PyBaseObjectType, "__init__"); + op = PyObject_GetAttr(PyBaseObjectType, PyIdentifier.__init__); SetPyMember(ref PyWrapperDescriptorType, PyObject_Type(op), () => PyWrapperDescriptorType = IntPtr.Zero); XDecref(op); @@ -258,7 +236,7 @@ private static void InitPyMembers() () => PyUnicodeType = IntPtr.Zero); XDecref(op); - op = PyBytes_FromString("bytes"); + op = EmptyPyBytes(); SetPyMember(ref PyBytesType, PyObject_Type(op), () => PyBytesType = IntPtr.Zero); XDecref(op); @@ -364,13 +342,14 @@ internal static void Shutdown(ShutdownMode mode) RuntimeData.Stash(); } AssemblyManager.Shutdown(); + OperatorMethod.Shutdown(); ImportHook.Shutdown(); ClearClrModules(); RemoveClrRootModule(); - MoveClrInstancesOnwershipToPython(); ClassManager.DisposePythonWrappersForClrTypes(); + MoveClrInstancesOnwershipToPython(); TypeManager.RemoveTypes(); MetaType.Release(); @@ -378,8 +357,9 @@ internal static void Shutdown(ShutdownMode mode) Exceptions.Shutdown(); Finalizer.Shutdown(); + InternString.Shutdown(); - if (mode != ShutdownMode.Normal) + if (mode != ShutdownMode.Normal && mode != ShutdownMode.Extension) { PyGC_Collect(); if (mode == ShutdownMode.Soft) @@ -405,12 +385,15 @@ internal static void Shutdown(ShutdownMode mode) { PyEval_SaveThread(); } - + } else { ResetPyMembers(); - Py_Finalize(); + if (mode != ShutdownMode.Extension) + { + Py_Finalize(); + } } } @@ -435,16 +418,6 @@ internal static ShutdownMode GetDefaultShutdownMode() return ShutdownMode.Normal; } - // called *without* the GIL acquired by clr._AtExit - internal static int AtExit() - { - lock (IsFinalizingLock) - { - IsFinalizing = true; - } - return 0; - } - private static void RunExitFuncs() { PyObject atexit; @@ -497,8 +470,8 @@ private static void ClearClrModules() for (long i = 0; i < length; i++) { var item = PyList_GetItem(items, i); - var name = PyTuple_GetItem(item.DangerousGetAddress(), 0); - var module = PyTuple_GetItem(item.DangerousGetAddress(), 1); + var name = PyTuple_GetItem(item, 0); + var module = PyTuple_GetItem(item, 1); if (ManagedType.IsManagedType(module)) { PyDict_DelItem(modules, name); @@ -510,12 +483,11 @@ private static void ClearClrModules() private static void RemoveClrRootModule() { var modules = PyImport_GetModuleDict(); - PyDictTryDelItem(modules, "CLR"); PyDictTryDelItem(modules, "clr"); PyDictTryDelItem(modules, "clr._extra"); } - private static void PyDictTryDelItem(IntPtr dict, string key) + private static void PyDictTryDelItem(BorrowedReference dict, string key) { if (PyDict_DelItemString(dict, key) == 0) { @@ -531,30 +503,44 @@ private static void PyDictTryDelItem(IntPtr dict, string key) private static void MoveClrInstancesOnwershipToPython() { var objs = ManagedType.GetManagedObjects(); - var copyObjs = objs.ToArray(); - foreach (var entry in copyObjs) + var copyObjs = new KeyValuePair[objs.Count]; { - ManagedType obj = entry.Key; - if (!objs.ContainsKey(obj)) + int i = 0; + foreach (var entry in objs) { - System.Diagnostics.Debug.Assert(obj.gcHandle == default); - continue; + ManagedType obj = entry.Key; + XIncref(obj.pyHandle); + copyObjs[i++] = entry; } + } + foreach (var entry in copyObjs) + { + ManagedType obj = entry.Key; if (entry.Value == ManagedType.TrackTypes.Extension) { obj.CallTypeClear(); // obj's tp_type will degenerate to a pure Python type after TypeManager.RemoveTypes(), // thus just be safe to give it back to GC chain. - if (!_PyObject_GC_IS_TRACKED(obj.pyHandle)) + if (!_PyObject_GC_IS_TRACKED(obj.ObjectReference)) { PyObject_GC_Track(obj.pyHandle); } } - if (obj.gcHandle.IsAllocated) + } + foreach (var entry in copyObjs) + { + ManagedType obj = entry.Key; + if (!objs.ContainsKey(obj)) + { + System.Diagnostics.Debug.Assert(obj.gcHandle == default); + continue; + } + if (obj.RefCount > 1) { - obj.gcHandle.Free(); + obj.FreeGCHandle(); + Marshal.WriteIntPtr(obj.pyHandle, ObjectOffset.magic(obj.tpHandle), IntPtr.Zero); } - obj.gcHandle = default; + XDecref(obj.pyHandle); } ManagedType.ClearTrackedObjects(); } @@ -683,7 +669,8 @@ internal static Type[] PythonArgsToTypeArray(IntPtr arg, bool mangleObjects) if (mt is ClassBase) { - t = ((ClassBase)mt).type; + MaybeType _type = ((ClassBase)mt).type; + t = _type.Valid ? _type.Value : null; } else if (mt is CLRObject) { @@ -719,7 +706,7 @@ internal static Type[] PythonArgsToTypeArray(IntPtr arg, bool mangleObjects) /// internal static unsafe void XIncref(IntPtr op) { -#if PYTHON_WITH_PYDEBUG || NETSTANDARD +#if !CUSTOM_INCDEC_REF Py_IncRef(op); return; #else @@ -749,7 +736,7 @@ internal static IntPtr SelfIncRef(IntPtr op) internal static unsafe void XDecref(IntPtr op) { -#if PYTHON_WITH_PYDEBUG || NETSTANDARD +#if !CUSTOM_INCDEC_REF Py_DecRef(op); return; #else @@ -804,146 +791,161 @@ internal static unsafe long Refcount(IntPtr op) /// Limit this function usage for Testing and Py_Debug builds /// /// PyObject Ptr - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void Py_IncRef(IntPtr ob); + + internal static void Py_IncRef(IntPtr ob) => Delegates.Py_IncRef(ob); /// /// Export of Macro Py_XDecRef. Use XDecref instead. /// Limit this function usage for Testing and Py_Debug builds /// /// PyObject Ptr - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void Py_DecRef(IntPtr ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void Py_Initialize(); + internal static void Py_DecRef(IntPtr ob) => Delegates.Py_DecRef(ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void Py_InitializeEx(int initsigs); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int Py_IsInitialized(); + internal static void Py_Initialize() => Delegates.Py_Initialize(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void Py_Finalize(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_NewInterpreter(); + internal static void Py_InitializeEx(int initsigs) => Delegates.Py_InitializeEx(initsigs); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void Py_EndInterpreter(IntPtr threadState); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyThreadState_New(IntPtr istate); + internal static int Py_IsInitialized() => Delegates.Py_IsInitialized(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyThreadState_Get(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr _PyThreadState_UncheckedGet(); + internal static void Py_Finalize() => Delegates.Py_Finalize(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyThread_get_key_value(IntPtr key); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyThread_get_thread_ident(); + internal static IntPtr Py_NewInterpreter() => Delegates.Py_NewInterpreter(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyThread_set_key_value(IntPtr key, IntPtr value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyThreadState_Swap(IntPtr key); + internal static void Py_EndInterpreter(IntPtr threadState) => Delegates.Py_EndInterpreter(threadState); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyGILState_Ensure(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyGILState_Release(IntPtr gs); + internal static IntPtr PyThreadState_New(IntPtr istate) => Delegates.PyThreadState_New(istate); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyGILState_GetThisThreadState(); + internal static IntPtr PyThreadState_Get() => Delegates.PyThreadState_Get(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Py_Main( - int argc, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StrArrayMarshaler))] string[] argv - ); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyEval_InitThreads(); + internal static IntPtr _PyThreadState_UncheckedGet() => Delegates._PyThreadState_UncheckedGet(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyEval_ThreadsInitialized(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyEval_AcquireLock(); + internal static IntPtr PyThread_get_key_value(IntPtr key) => Delegates.PyThread_get_key_value(key); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyEval_ReleaseLock(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyEval_AcquireThread(IntPtr tstate); + internal static int PyThread_get_thread_ident() => Delegates.PyThread_get_thread_ident(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyEval_ReleaseThread(IntPtr tstate); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyEval_SaveThread(); + internal static int PyThread_set_key_value(IntPtr key, IntPtr value) => Delegates.PyThread_set_key_value(key, value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyEval_RestoreThread(IntPtr tstate); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyEval_GetBuiltins(); + internal static IntPtr PyThreadState_Swap(IntPtr key) => Delegates.PyThreadState_Swap(key); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyEval_GetGlobals(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyEval_GetLocals(); + internal static IntPtr PyGILState_Ensure() => Delegates.PyGILState_Ensure(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_GetProgramName(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void Py_SetProgramName(IntPtr name); + internal static void PyGILState_Release(IntPtr gs) => Delegates.PyGILState_Release(gs); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_GetPythonHome(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void Py_SetPythonHome(IntPtr home); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_GetPath(); + internal static IntPtr PyGILState_GetThisThreadState() => Delegates.PyGILState_GetThisThreadState(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void Py_SetPath(IntPtr home); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_GetVersion(); + public static int Py_Main(int argc, string[] argv) + { + var marshaler = StrArrayMarshaler.GetInstance(null); + var argvPtr = marshaler.MarshalManagedToNative(argv); + try + { + return Delegates.Py_Main(argc, argvPtr); + } + finally + { + marshaler.CleanUpNativeData(argvPtr); + } + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_GetPlatform(); + internal static void PyEval_InitThreads() => Delegates.PyEval_InitThreads(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_GetCopyright(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_GetCompiler(); + internal static int PyEval_ThreadsInitialized() => Delegates.PyEval_ThreadsInitialized(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_GetBuildInfo(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyRun_SimpleString(string code); + internal static void PyEval_AcquireLock() => Delegates.PyEval_AcquireLock(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern NewReference PyRun_String([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string code, RunFlagType st, IntPtr globals, IntPtr locals); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals); + internal static void PyEval_ReleaseLock() => Delegates.PyEval_ReleaseLock(); + + + internal static void PyEval_AcquireThread(IntPtr tstate) => Delegates.PyEval_AcquireThread(tstate); + + + internal static void PyEval_ReleaseThread(IntPtr tstate) => Delegates.PyEval_ReleaseThread(tstate); + + + internal static IntPtr PyEval_SaveThread() => Delegates.PyEval_SaveThread(); + + + internal static void PyEval_RestoreThread(IntPtr tstate) => Delegates.PyEval_RestoreThread(tstate); + + + internal static BorrowedReference PyEval_GetBuiltins() => Delegates.PyEval_GetBuiltins(); + + + internal static BorrowedReference PyEval_GetGlobals() => Delegates.PyEval_GetGlobals(); + + + internal static IntPtr PyEval_GetLocals() => Delegates.PyEval_GetLocals(); + + + internal static IntPtr Py_GetProgramName() => Delegates.Py_GetProgramName(); + + + internal static void Py_SetProgramName(IntPtr name) => Delegates.Py_SetProgramName(name); + + + internal static IntPtr Py_GetPythonHome() => Delegates.Py_GetPythonHome(); + + + internal static void Py_SetPythonHome(IntPtr home) => Delegates.Py_SetPythonHome(home); + + + internal static IntPtr Py_GetPath() => Delegates.Py_GetPath(); + + + internal static void Py_SetPath(IntPtr home) => Delegates.Py_SetPath(home); + + + internal static IntPtr Py_GetVersion() => Delegates.Py_GetVersion(); + + + internal static IntPtr Py_GetPlatform() => Delegates.Py_GetPlatform(); + + + internal static IntPtr Py_GetCopyright() => Delegates.Py_GetCopyright(); + + + internal static IntPtr Py_GetCompiler() => Delegates.Py_GetCompiler(); + + + internal static IntPtr Py_GetBuildInfo() => Delegates.Py_GetBuildInfo(); + + const PyCompilerFlags Utf8String = PyCompilerFlags.IGNORE_COOKIE | PyCompilerFlags.SOURCE_IS_UTF8; + + internal static int PyRun_SimpleString(string code) + { + using var codePtr = new StrPtr(code, Encoding.UTF8); + return Delegates.PyRun_SimpleStringFlags(codePtr, Utf8String); + } + + internal static NewReference PyRun_String(string code, RunFlagType st, BorrowedReference globals, BorrowedReference locals) + { + using var codePtr = new StrPtr(code, Encoding.UTF8); + return Delegates.PyRun_StringFlags(codePtr, st, globals, locals, Utf8String); + } + + internal static IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals) => Delegates.PyEval_EvalCode(co, globals, locals); /// /// Return value: New reference. @@ -951,37 +953,24 @@ public static extern int Py_Main( /// internal static IntPtr Py_CompileString(string str, string file, int start) { - return Py_CompileStringFlags(str, file, start, IntPtr.Zero); + using var strPtr = new StrPtr(str, Encoding.UTF8); + using var fileObj = new PyString(file); + return Delegates.Py_CompileStringObject(strPtr, fileObj.Reference, start, Utf8String, -1); } - /// - /// Return value: New reference. - /// This is a simplified interface to Py_CompileStringExFlags() below, with optimize set to -1. - /// - internal static IntPtr Py_CompileStringFlags(string str, string file, int start, IntPtr flags) + internal static IntPtr PyImport_ExecCodeModule(string name, IntPtr code) { - return Py_CompileStringExFlags(str, file, start, flags, -1); + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PyImport_ExecCodeModule(namePtr, code); } - /// - /// Return value: New reference. - /// Like Py_CompileStringObject(), but filename is a byte string decoded from the filesystem encoding(os.fsdecode()). - /// - /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr Py_CompileStringExFlags(string str, string file, int start, IntPtr flags, int optimize); + internal static IntPtr PyCFunction_NewEx(IntPtr ml, IntPtr self, IntPtr mod) => Delegates.PyCFunction_NewEx(ml, self, mod); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyImport_ExecCodeModule(string name, IntPtr code); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyCFunction_NewEx(IntPtr ml, IntPtr self, IntPtr mod); + internal static IntPtr PyCFunction_Call(IntPtr func, IntPtr args, IntPtr kw) => Delegates.PyCFunction_Call(func, args, kw); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyCFunction_Call(IntPtr func, IntPtr args, IntPtr kw); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyMethod_New(IntPtr func, IntPtr self, IntPtr cls); + internal static IntPtr PyMethod_New(IntPtr func, IntPtr self, IntPtr cls) => Delegates.PyMethod_New(func, self, cls); //==================================================================== @@ -1010,6 +999,8 @@ internal static unsafe IntPtr PyObject_TYPE(IntPtr op) ? new IntPtr((void*)(*((uint*)p + n))) : new IntPtr((void*)(*((ulong*)p + n))); } + internal static unsafe BorrowedReference PyObject_TYPE(BorrowedReference op) + => new BorrowedReference(PyObject_TYPE(op.DangerousGetAddress())); /// /// Managed version of the standard Python C API PyObject_Type call. @@ -1040,47 +1031,62 @@ internal static bool PyObject_IsIterable(IntPtr pointer) return tp_iter != IntPtr.Zero; } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_HasAttrString(IntPtr pointer, string name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_GetAttrString(IntPtr pointer, string name); + internal static int PyObject_HasAttrString(BorrowedReference pointer, string name) + { + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PyObject_HasAttrString(pointer, namePtr); + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_GetAttrString(IntPtr pointer, IntPtr name); + internal static IntPtr PyObject_GetAttrString(IntPtr pointer, string name) + { + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PyObject_GetAttrString(pointer, namePtr); + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_SetAttrString(IntPtr pointer, string name, IntPtr value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_HasAttr(IntPtr pointer, IntPtr name); + internal static IntPtr PyObject_GetAttrString(IntPtr pointer, StrPtr name) => Delegates.PyObject_GetAttrString(pointer, name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_GetAttr(IntPtr pointer, IntPtr name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_SetAttr(IntPtr pointer, IntPtr name, IntPtr value); + internal static int PyObject_SetAttrString(IntPtr pointer, string name, IntPtr value) + { + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PyObject_SetAttrString(pointer, namePtr, value); + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_GetItem(IntPtr pointer, IntPtr key); + internal static int PyObject_HasAttr(BorrowedReference pointer, BorrowedReference name) => Delegates.PyObject_HasAttr(pointer, name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_SetItem(IntPtr pointer, IntPtr key, IntPtr value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_DelItem(IntPtr pointer, IntPtr key); + internal static NewReference PyObject_GetAttr(BorrowedReference pointer, IntPtr name) + => Delegates.PyObject_GetAttr(pointer, new BorrowedReference(name)); + internal static IntPtr PyObject_GetAttr(IntPtr pointer, IntPtr name) + => Delegates.PyObject_GetAttr(new BorrowedReference(pointer), new BorrowedReference(name)) + .DangerousMoveToPointerOrNull(); + internal static NewReference PyObject_GetAttr(BorrowedReference pointer, BorrowedReference name) => Delegates.PyObject_GetAttr(pointer, name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_GetIter(IntPtr op); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_Call(IntPtr pointer, IntPtr args, IntPtr kw); + internal static int PyObject_SetAttr(IntPtr pointer, IntPtr name, IntPtr value) => Delegates.PyObject_SetAttr(pointer, name, value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_CallObject(IntPtr pointer, IntPtr args); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_RichCompareBool(IntPtr value1, IntPtr value2, int opid); + internal static IntPtr PyObject_GetItem(IntPtr pointer, IntPtr key) => Delegates.PyObject_GetItem(pointer, key); + + + internal static int PyObject_SetItem(IntPtr pointer, IntPtr key, IntPtr value) => Delegates.PyObject_SetItem(pointer, key, value); + + + internal static int PyObject_DelItem(IntPtr pointer, IntPtr key) => Delegates.PyObject_DelItem(pointer, key); + + + internal static IntPtr PyObject_GetIter(IntPtr op) => Delegates.PyObject_GetIter(op); + + + internal static IntPtr PyObject_Call(IntPtr pointer, IntPtr args, IntPtr kw) => Delegates.PyObject_Call(pointer, args, kw); + + + internal static IntPtr PyObject_CallObject(IntPtr pointer, IntPtr args) => Delegates.PyObject_CallObject(pointer, args); + + + internal static int PyObject_RichCompareBool(IntPtr value1, IntPtr value2, int opid) => Delegates.PyObject_RichCompareBool(value1, value2, opid); internal static int PyObject_Compare(IntPtr value1, IntPtr value2) { @@ -1107,44 +1113,44 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2) return -1; } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_IsInstance(IntPtr ob, IntPtr type); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_IsSubclass(IntPtr ob, IntPtr type); + internal static int PyObject_IsInstance(IntPtr ob, IntPtr type) => Delegates.PyObject_IsInstance(ob, type); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyCallable_Check(IntPtr pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_IsTrue(IntPtr pointer); + internal static int PyObject_IsSubclass(IntPtr ob, IntPtr type) => Delegates.PyObject_IsSubclass(ob, type); + + + internal static int PyCallable_Check(IntPtr pointer) => Delegates.PyCallable_Check(pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_Not(IntPtr pointer); + + internal static int PyObject_IsTrue(IntPtr pointer) => PyObject_IsTrue(new BorrowedReference(pointer)); + internal static int PyObject_IsTrue(BorrowedReference pointer) => Delegates.PyObject_IsTrue(pointer); + + + internal static int PyObject_Not(IntPtr pointer) => Delegates.PyObject_Not(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, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_Hash(IntPtr op); + private static IntPtr _PyObject_Size(IntPtr pointer) => Delegates._PyObject_Size(pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_Repr(IntPtr pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_Str(IntPtr pointer); + internal static nint PyObject_Hash(IntPtr op) => Delegates.PyObject_Hash(op); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "PyObject_Str")] - internal static extern IntPtr PyObject_Unicode(IntPtr pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_Dir(IntPtr pointer); + internal static IntPtr PyObject_Repr(IntPtr pointer) => Delegates.PyObject_Repr(pointer); + + + internal static IntPtr PyObject_Str(IntPtr pointer) => Delegates.PyObject_Str(pointer); + + + internal static IntPtr PyObject_Unicode(IntPtr pointer) => Delegates.PyObject_Unicode(pointer); + + + internal static IntPtr PyObject_Dir(IntPtr pointer) => Delegates.PyObject_Dir(pointer); #if PYTHON_WITH_PYDEBUG [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] @@ -1155,52 +1161,56 @@ internal static long PyObject_Size(IntPtr pointer) // Python buffer API //==================================================================== - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_GetBuffer(IntPtr exporter, ref Py_buffer view, int flags); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyBuffer_Release(ref Py_buffer view); + internal static int PyObject_GetBuffer(IntPtr exporter, ref Py_buffer view, int flags) => Delegates.PyObject_GetBuffer(exporter, ref view, flags); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - internal static extern IntPtr PyBuffer_SizeFromFormat([MarshalAs(UnmanagedType.LPStr)] string format); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyBuffer_IsContiguous(ref Py_buffer view, char order); + internal static void PyBuffer_Release(ref Py_buffer view) => Delegates.PyBuffer_Release(ref view); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyBuffer_GetPointer(ref Py_buffer view, IntPtr[] indices); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyBuffer_FromContiguous(ref Py_buffer view, IntPtr buf, IntPtr len, char fort); + internal static IntPtr PyBuffer_SizeFromFormat(string format) + { + using var formatPtr = new StrPtr(format, Encoding.ASCII); + return Delegates.PyBuffer_SizeFromFormat(formatPtr); + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyBuffer_ToContiguous(IntPtr buf, ref Py_buffer src, IntPtr len, char order); + internal static int PyBuffer_IsContiguous(ref Py_buffer view, char order) => Delegates.PyBuffer_IsContiguous(ref view, order); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyBuffer_FillContiguousStrides(int ndims, IntPtr shape, IntPtr strides, int itemsize, char order); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyBuffer_FillInfo(ref Py_buffer view, IntPtr exporter, IntPtr buf, IntPtr len, int _readonly, int flags); + internal static IntPtr PyBuffer_GetPointer(ref Py_buffer view, IntPtr[] indices) => Delegates.PyBuffer_GetPointer(ref view, indices); + + + internal static int PyBuffer_FromContiguous(ref Py_buffer view, IntPtr buf, IntPtr len, char fort) => Delegates.PyBuffer_FromContiguous(ref view, buf, len, fort); + + + internal static int PyBuffer_ToContiguous(IntPtr buf, ref Py_buffer src, IntPtr len, char order) => Delegates.PyBuffer_ToContiguous(buf, ref src, len, order); + + + internal static void PyBuffer_FillContiguousStrides(int ndims, IntPtr shape, IntPtr strides, int itemsize, char order) => Delegates.PyBuffer_FillContiguousStrides(ndims, shape, strides, itemsize, order); + + + internal static int PyBuffer_FillInfo(ref Py_buffer view, IntPtr exporter, IntPtr buf, IntPtr len, int _readonly, int flags) => Delegates.PyBuffer_FillInfo(ref view, exporter, buf, len, _readonly, flags); //==================================================================== // Python number API //==================================================================== - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "PyNumber_Long")] - internal static extern IntPtr PyNumber_Int(IntPtr ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Long(IntPtr ob); + internal static IntPtr PyNumber_Int(IntPtr ob) => Delegates.PyNumber_Int(ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Float(IntPtr ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern bool PyNumber_Check(IntPtr ob); + internal static IntPtr PyNumber_Long(IntPtr ob) => Delegates.PyNumber_Long(ob); - internal static bool PyInt_Check(IntPtr ob) - { + + internal static IntPtr PyNumber_Float(IntPtr ob) => Delegates.PyNumber_Float(ob); + + + internal static bool PyNumber_Check(IntPtr ob) => Delegates.PyNumber_Check(ob); + + internal static bool PyInt_Check(BorrowedReference ob) + => PyObject_TypeCheck(ob, new BorrowedReference(PyIntType)); + internal static bool PyInt_Check(IntPtr ob) + { return PyObject_TypeCheck(ob, PyIntType); } @@ -1221,33 +1231,26 @@ internal static IntPtr PyInt_FromInt64(long value) return PyInt_FromLong(v); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "PyLong_FromLong")] - private static extern IntPtr PyInt_FromLong(IntPtr value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "PyLong_AsLong")] - internal static extern int PyInt_AsLong(IntPtr value); + private static IntPtr PyInt_FromLong(IntPtr value) => Delegates.PyInt_FromLong(value); + + + internal static int PyInt_AsLong(IntPtr value) => Delegates.PyInt_AsLong(value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "PyLong_FromString")] - internal static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix); internal static bool PyLong_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyLongType; } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyLong_FromLong(long value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "PyLong_FromUnsignedLong")] - internal static extern IntPtr PyLong_FromUnsignedLong32(uint value); + internal static IntPtr PyLong_FromLong(long value) => Delegates.PyLong_FromLong(value); + + + internal static IntPtr PyLong_FromUnsignedLong32(uint value) => Delegates.PyLong_FromUnsignedLong32(value); + - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "PyLong_FromUnsignedLong")] - internal static extern IntPtr PyLong_FromUnsignedLong64(ulong value); + internal static IntPtr PyLong_FromUnsignedLong64(ulong value) => Delegates.PyLong_FromUnsignedLong64(value); internal static IntPtr PyLong_FromUnsignedLong(object value) { @@ -1257,42 +1260,42 @@ internal static IntPtr PyLong_FromUnsignedLong(object value) return PyLong_FromUnsignedLong64(Convert.ToUInt64(value)); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyLong_FromDouble(double value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyLong_FromLongLong(long value); + internal static IntPtr PyLong_FromDouble(double value) => Delegates.PyLong_FromDouble(value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyLong_FromUnsignedLongLong(ulong value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyLong_FromString(string value, IntPtr end, int radix); + internal static IntPtr PyLong_FromLongLong(long value) => Delegates.PyLong_FromLongLong(value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyLong_AsLong(IntPtr value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "PyLong_AsUnsignedLong")] - internal static extern uint PyLong_AsUnsignedLong32(IntPtr value); + internal static IntPtr PyLong_FromUnsignedLongLong(ulong value) => Delegates.PyLong_FromUnsignedLongLong(value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, - EntryPoint = "PyLong_AsUnsignedLong")] - internal static extern ulong PyLong_AsUnsignedLong64(IntPtr value); - internal static object PyLong_AsUnsignedLong(IntPtr value) + internal static IntPtr PyLong_FromString(string value, IntPtr end, int radix) { - if (Is32Bit || IsWindows) - return PyLong_AsUnsignedLong32(value); - else - return PyLong_AsUnsignedLong64(value); + using var valPtr = new StrPtr(value, Encoding.UTF8); + return Delegates.PyLong_FromString(valPtr, end, radix); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern long PyLong_AsLongLong(IntPtr value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern ulong PyLong_AsUnsignedLongLong(IntPtr value); + + internal static nuint PyLong_AsUnsignedSize_t(IntPtr value) => Delegates.PyLong_AsUnsignedSize_t(value); + + internal static nint PyLong_AsSignedSize_t(IntPtr value) => Delegates.PyLong_AsSignedSize_t(new BorrowedReference(value)); + + internal static nint PyLong_AsSignedSize_t(BorrowedReference value) => Delegates.PyLong_AsSignedSize_t(value); + + /// + /// This function is a rename of PyLong_AsLongLong, which has a commonly undesired + /// behavior to convert everything (including floats) to integer type, before returning + /// the value as . + /// + /// In most cases you need to check that value is an instance of PyLongObject + /// before using this function using . + /// + + internal static long PyExplicitlyConvertToInt64(IntPtr value) => Delegates.PyExplicitlyConvertToInt64(value); + + internal static ulong PyLong_AsUnsignedLongLong(IntPtr value) => Delegates.PyLong_AsUnsignedLongLong(value); internal static bool PyFloat_Check(IntPtr ob) { @@ -1303,199 +1306,193 @@ internal static bool PyFloat_Check(IntPtr ob) /// Return value: New reference. /// Create a Python integer from the pointer p. The pointer value can be retrieved from the resulting value using PyLong_AsVoidPtr(). /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyLong_FromVoidPtr(IntPtr p); + internal static NewReference PyLong_FromVoidPtr(IntPtr p) => Delegates.PyLong_FromVoidPtr(p); /// /// Convert a Python integer pylong to a C void pointer. If pylong cannot be converted, an OverflowError will be raised. This is only assured to produce a usable void pointer for values created with PyLong_FromVoidPtr(). /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyLong_AsVoidPtr(IntPtr ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyFloat_FromDouble(double value); + internal static IntPtr PyLong_AsVoidPtr(BorrowedReference ob) => Delegates.PyLong_AsVoidPtr(ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyFloat_FromString(IntPtr value, IntPtr junk); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern double PyFloat_AsDouble(IntPtr ob); + internal static IntPtr PyFloat_FromDouble(double value) => Delegates.PyFloat_FromDouble(value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Add(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Subtract(IntPtr o1, IntPtr o2); + internal static NewReference PyFloat_FromString(BorrowedReference value) => Delegates.PyFloat_FromString(value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Multiply(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_TrueDivide(IntPtr o1, IntPtr o2); + internal static double PyFloat_AsDouble(IntPtr ob) => Delegates.PyFloat_AsDouble(ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_And(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Xor(IntPtr o1, IntPtr o2); + internal static IntPtr PyNumber_Add(IntPtr o1, IntPtr o2) => Delegates.PyNumber_Add(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Or(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Lshift(IntPtr o1, IntPtr o2); + internal static IntPtr PyNumber_Subtract(IntPtr o1, IntPtr o2) => Delegates.PyNumber_Subtract(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Rshift(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Power(IntPtr o1, IntPtr o2); + internal static IntPtr PyNumber_Multiply(IntPtr o1, IntPtr o2) => Delegates.PyNumber_Multiply(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Remainder(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceAdd(IntPtr o1, IntPtr o2); + internal static IntPtr PyNumber_TrueDivide(IntPtr o1, IntPtr o2) => Delegates.PyNumber_TrueDivide(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceSubtract(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceMultiply(IntPtr o1, IntPtr o2); + internal static IntPtr PyNumber_And(IntPtr o1, IntPtr o2) => Delegates.PyNumber_And(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceTrueDivide(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceAnd(IntPtr o1, IntPtr o2); + internal static IntPtr PyNumber_Xor(IntPtr o1, IntPtr o2) => Delegates.PyNumber_Xor(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceXor(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceOr(IntPtr o1, IntPtr o2); + internal static IntPtr PyNumber_Or(IntPtr o1, IntPtr o2) => Delegates.PyNumber_Or(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceLshift(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceRshift(IntPtr o1, IntPtr o2); + internal static IntPtr PyNumber_Lshift(IntPtr o1, IntPtr o2) => Delegates.PyNumber_Lshift(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlacePower(IntPtr o1, IntPtr o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_InPlaceRemainder(IntPtr o1, IntPtr o2); + internal static IntPtr PyNumber_Rshift(IntPtr o1, IntPtr o2) => Delegates.PyNumber_Rshift(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Negative(IntPtr o1); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Positive(IntPtr o1); + internal static IntPtr PyNumber_Power(IntPtr o1, IntPtr o2) => Delegates.PyNumber_Power(o1, o2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyNumber_Invert(IntPtr o1); + + internal static IntPtr PyNumber_Remainder(IntPtr o1, IntPtr o2) => Delegates.PyNumber_Remainder(o1, o2); + + + internal static IntPtr PyNumber_InPlaceAdd(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceAdd(o1, o2); + + + internal static IntPtr PyNumber_InPlaceSubtract(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceSubtract(o1, o2); + + + internal static IntPtr PyNumber_InPlaceMultiply(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceMultiply(o1, o2); + + + internal static IntPtr PyNumber_InPlaceTrueDivide(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceTrueDivide(o1, o2); + + + internal static IntPtr PyNumber_InPlaceAnd(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceAnd(o1, o2); + + + internal static IntPtr PyNumber_InPlaceXor(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceXor(o1, o2); + + + internal static IntPtr PyNumber_InPlaceOr(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceOr(o1, o2); + + + internal static IntPtr PyNumber_InPlaceLshift(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceLshift(o1, o2); + + + internal static IntPtr PyNumber_InPlaceRshift(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceRshift(o1, o2); + + + internal static IntPtr PyNumber_InPlacePower(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlacePower(o1, o2); + + + internal static IntPtr PyNumber_InPlaceRemainder(IntPtr o1, IntPtr o2) => Delegates.PyNumber_InPlaceRemainder(o1, o2); + + + internal static IntPtr PyNumber_Negative(IntPtr o1) => Delegates.PyNumber_Negative(o1); + + + internal static IntPtr PyNumber_Positive(IntPtr o1) => Delegates.PyNumber_Positive(o1); + + + internal static IntPtr PyNumber_Invert(IntPtr o1) => Delegates.PyNumber_Invert(o1); //==================================================================== // Python sequence API //==================================================================== - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern bool PySequence_Check(IntPtr pointer); - internal static IntPtr PySequence_GetItem(IntPtr pointer, long index) - { - return PySequence_GetItem(pointer, new IntPtr(index)); - } + internal static bool PySequence_Check(IntPtr pointer) => Delegates.PySequence_Check(pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr PySequence_GetItem(IntPtr pointer, IntPtr index); + internal static NewReference PySequence_GetItem(BorrowedReference pointer, nint index) => Delegates.PySequence_GetItem(pointer, 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); + + private static int PySequence_SetItem(IntPtr pointer, IntPtr index, IntPtr value) => Delegates.PySequence_SetItem(pointer, index, value); internal static int PySequence_DelItem(IntPtr pointer, long index) { return PySequence_DelItem(pointer, new IntPtr(index)); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - private static extern int PySequence_DelItem(IntPtr pointer, IntPtr index); + + private static int PySequence_DelItem(IntPtr pointer, IntPtr index) => Delegates.PySequence_DelItem(pointer, index); internal static IntPtr PySequence_GetSlice(IntPtr pointer, long i1, long i2) { return PySequence_GetSlice(pointer, new IntPtr(i1), new IntPtr(i2)); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr PySequence_GetSlice(IntPtr pointer, IntPtr i1, IntPtr i2); + + private static IntPtr PySequence_GetSlice(IntPtr pointer, IntPtr i1, IntPtr i2) => Delegates.PySequence_GetSlice(pointer, i1, 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, CallingConvention = CallingConvention.Cdecl)] - private static extern int PySequence_SetSlice(IntPtr pointer, IntPtr i1, IntPtr i2, IntPtr v); + + private static int PySequence_SetSlice(IntPtr pointer, IntPtr i1, IntPtr i2, IntPtr v) => Delegates.PySequence_SetSlice(pointer, i1, i2, v); 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); - } + private static int PySequence_DelSlice(IntPtr pointer, IntPtr i1, IntPtr i2) => Delegates.PySequence_DelSlice(pointer, i1, i2); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PySequence_Size")] - private static extern IntPtr _PySequence_Size(IntPtr pointer); + [Obsolete] + internal static nint PySequence_Size(IntPtr pointer) => PySequence_Size(new BorrowedReference(pointer)); + internal static nint PySequence_Size(BorrowedReference pointer) => Delegates.PySequence_Size(pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PySequence_Contains(IntPtr pointer, IntPtr item); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PySequence_Concat(IntPtr pointer, IntPtr other); + internal static int PySequence_Contains(IntPtr pointer, IntPtr item) => Delegates.PySequence_Contains(pointer, item); + + + internal static IntPtr PySequence_Concat(IntPtr pointer, IntPtr other) => Delegates.PySequence_Concat(pointer, other); 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, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PySequence_Index(IntPtr pointer, IntPtr item); + private static IntPtr PySequence_Repeat(IntPtr pointer, IntPtr count) => Delegates.PySequence_Repeat(pointer, count); + + + internal static int PySequence_Index(IntPtr pointer, IntPtr item) => Delegates.PySequence_Index(pointer, item); 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, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PySequence_Tuple(IntPtr pointer); + private static IntPtr _PySequence_Count(IntPtr pointer, IntPtr value) => Delegates._PySequence_Count(pointer, value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PySequence_List(IntPtr pointer); + + internal static IntPtr PySequence_Tuple(IntPtr pointer) => Delegates.PySequence_Tuple(pointer); + + + internal static IntPtr PySequence_List(IntPtr pointer) => Delegates.PySequence_List(pointer); //==================================================================== // Python string API //==================================================================== - + internal static bool IsStringType(BorrowedReference op) + { + BorrowedReference t = PyObject_TYPE(op); + return (t == new BorrowedReference(PyStringType)) + || (t == new BorrowedReference(PyUnicodeType)); + } internal static bool IsStringType(IntPtr op) { IntPtr t = PyObject_TYPE(op); @@ -1509,95 +1506,102 @@ internal static bool PyString_Check(IntPtr ob) internal static IntPtr PyString_FromString(string value) { - return PyUnicode_FromKindAndData(_UCS, value, value.Length); + fixed(char* ptr = value) + return PyUnicode_FromKindAndData(2, (IntPtr)ptr, value.Length); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyBytes_FromString(string op); + + internal static IntPtr EmptyPyBytes() + { + byte* bytes = stackalloc byte[1]; + bytes[0] = 0; + return Delegates.PyBytes_FromString((IntPtr)bytes); + } 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); + + private static IntPtr _PyBytes_Size(IntPtr op) => Delegates._PyBytes_Size(op); internal static IntPtr PyBytes_AS_STRING(IntPtr ob) { return ob + BytesOffset.ob_sval; } - 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, - IntPtr 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); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyUnicode_AsUTF8(IntPtr unicode); + private static IntPtr PyUnicode_FromStringAndSize(IntPtr value, IntPtr size) => Delegates.PyUnicode_FromStringAndSize(value, size); + + + internal static IntPtr PyUnicode_AsUTF8(IntPtr unicode) => Delegates.PyUnicode_AsUTF8(unicode); internal static bool PyUnicode_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyUnicodeType; } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyUnicode_FromObject(IntPtr ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err); + internal static IntPtr PyUnicode_FromObject(IntPtr ob) => Delegates.PyUnicode_FromObject(ob); + + + internal static IntPtr PyUnicode_FromEncodedObject(IntPtr ob, IntPtr enc, IntPtr err) => Delegates.PyUnicode_FromEncodedObject(ob, enc, err); - internal static IntPtr PyUnicode_FromKindAndData(int kind, string s, long size) + internal static IntPtr PyUnicode_FromKindAndData(int kind, IntPtr 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, - IntPtr size - ); + + private static IntPtr PyUnicode_FromKindAndData(int kind, IntPtr s, IntPtr size) + => Delegates.PyUnicode_FromKindAndData(kind, s, size); internal static IntPtr PyUnicode_FromUnicode(string s, long size) { - return PyUnicode_FromKindAndData(_UCS, s, size); + fixed(char* ptr = s) + return PyUnicode_FromKindAndData(2, (IntPtr)ptr, size); } + + internal static int PyUnicode_GetMax() => Delegates.PyUnicode_GetMax(); + internal static long PyUnicode_GetSize(IntPtr ob) { return (long)_PyUnicode_GetSize(ob); } - [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); + private static IntPtr _PyUnicode_GetSize(IntPtr ob) => Delegates._PyUnicode_GetSize(ob); + + + internal static IntPtr PyUnicode_AsUnicode(IntPtr ob) => Delegates.PyUnicode_AsUnicode(ob); + internal static NewReference PyUnicode_AsUTF16String(BorrowedReference ob) => Delegates.PyUnicode_AsUTF16String(ob); + - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyUnicode_FromOrdinal(int c); + + internal static IntPtr PyUnicode_FromOrdinal(int c) => Delegates.PyUnicode_FromOrdinal(c); internal static IntPtr PyUnicode_FromString(string s) { return PyUnicode_FromUnicode(s, s.Length); } + + internal static IntPtr PyUnicode_InternFromString(string s) + { + using var ptr = new StrPtr(s, Encoding.UTF8); + return Delegates.PyUnicode_InternFromString(ptr); + } + + internal static int PyUnicode_Compare(IntPtr left, IntPtr right) => Delegates.PyUnicode_Compare(left, right); + internal static string GetManagedString(in BorrowedReference borrowedReference) => GetManagedString(borrowedReference.DangerousGetAddress()); /// @@ -1619,13 +1623,12 @@ internal static string GetManagedString(IntPtr op) if (type == PyUnicodeType) { - IntPtr p = PyUnicode_AsUnicode(op); + using var p = PyUnicode_AsUTF16String(new BorrowedReference(op)); int length = (int)PyUnicode_GetSize(op); - - int size = length * _UCS; - var buffer = new byte[size]; - Marshal.Copy(p, buffer, 0, size); - return PyEncoding.GetString(buffer, 0, size); + char* codePoints = (char*)PyBytes_AS_STRING(p.DangerousGetAddress()); + return new string(codePoints, + startIndex: 1, // skip BOM + length: length); } return null; @@ -1641,90 +1644,116 @@ internal static bool PyDict_Check(IntPtr ob) return PyObject_TYPE(ob) == PyDictType; } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyDict_New(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyDict_Next(IntPtr p, out IntPtr ppos, out IntPtr pkey, out IntPtr pvalue); + internal static IntPtr PyDict_New() => Delegates.PyDict_New(); + + + internal static int PyDict_Next(IntPtr p, out IntPtr ppos, out IntPtr pkey, out IntPtr pvalue) => Delegates.PyDict_Next(p, out ppos, out pkey, out pvalue); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyDictProxy_New(IntPtr dict); + + internal static IntPtr PyDictProxy_New(IntPtr dict) => Delegates.PyDictProxy_New(dict); /// /// Return value: Borrowed reference. - /// Return NULL if the key key is not present, but without setting an exception. + /// Return NULL if the key is not present, but without setting an exception. /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyDict_GetItem(IntPtr pointer, IntPtr key); - + internal static IntPtr PyDict_GetItem(IntPtr pointer, IntPtr key) + => Delegates.PyDict_GetItem(new BorrowedReference(pointer), new BorrowedReference(key)) + .DangerousGetAddressOrNull(); /// - /// Return value: Borrowed reference. + /// Return NULL if the key is not present, but without setting an exception. /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyDict_GetItemString(IntPtr pointer, string key); + internal static BorrowedReference PyDict_GetItem(BorrowedReference pointer, BorrowedReference key) => Delegates.PyDict_GetItem(pointer, key); + + internal static BorrowedReference PyDict_GetItemString(BorrowedReference pointer, string key) + { + using var keyStr = new StrPtr(key, Encoding.UTF8); + return Delegates.PyDict_GetItemString(pointer, keyStr); + } + + internal static BorrowedReference PyDict_GetItemWithError(BorrowedReference pointer, BorrowedReference key) => Delegates.PyDict_GetItemWithError(pointer, key); /// /// Return 0 on success or -1 on failure. /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyDict_SetItem(IntPtr pointer, IntPtr key, IntPtr value); + [Obsolete] + internal static int PyDict_SetItem(IntPtr dict, IntPtr key, IntPtr value) => Delegates.PyDict_SetItem(new BorrowedReference(dict), new BorrowedReference(key), new BorrowedReference(value)); + /// + /// Return 0 on success or -1 on failure. + /// + internal static int PyDict_SetItem(BorrowedReference dict, IntPtr key, BorrowedReference value) => Delegates.PyDict_SetItem(dict, new BorrowedReference(key), value); + /// + /// Return 0 on success or -1 on failure. + /// + internal static int PyDict_SetItem(BorrowedReference dict, BorrowedReference key, BorrowedReference value) => Delegates.PyDict_SetItem(dict, key, value); /// /// Return 0 on success or -1 on failure. /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyDict_SetItemString(IntPtr pointer, string key, IntPtr value); + internal static int PyDict_SetItemString(IntPtr dict, string key, IntPtr value) + => PyDict_SetItemString(new BorrowedReference(dict), key, new BorrowedReference(value)); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyDict_DelItem(IntPtr pointer, IntPtr key); + /// + /// Return 0 on success or -1 on failure. + /// + internal static int PyDict_SetItemString(BorrowedReference dict, string key, BorrowedReference value) + { + using var keyPtr = new StrPtr(key, Encoding.UTF8); + return Delegates.PyDict_SetItemString(dict, keyPtr, value); + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyDict_DelItemString(IntPtr pointer, string key); + internal static int PyDict_DelItem(BorrowedReference pointer, BorrowedReference key) => Delegates.PyDict_DelItem(pointer, key); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyMapping_HasKey(IntPtr pointer, IntPtr key); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyDict_Keys(IntPtr pointer); + internal static int PyDict_DelItemString(BorrowedReference pointer, string key) + { + using var keyPtr = new StrPtr(key, Encoding.UTF8); + return Delegates.PyDict_DelItemString(pointer, keyPtr); + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyDict_Values(IntPtr pointer); + internal static int PyMapping_HasKey(IntPtr pointer, IntPtr key) => Delegates.PyMapping_HasKey(pointer, key); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern NewReference PyDict_Items(IntPtr pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyDict_Copy(IntPtr pointer); + [Obsolete] + internal static IntPtr PyDict_Keys(IntPtr pointer) + => Delegates.PyDict_Keys(new BorrowedReference(pointer)) + .DangerousMoveToPointerOrNull(); + internal static NewReference PyDict_Keys(BorrowedReference pointer) => Delegates.PyDict_Keys(pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyDict_Update(IntPtr pointer, IntPtr other); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyDict_Clear(IntPtr pointer); + internal static IntPtr PyDict_Values(IntPtr pointer) => Delegates.PyDict_Values(pointer); + + + internal static NewReference PyDict_Items(BorrowedReference pointer) => Delegates.PyDict_Items(pointer); + + + internal static IntPtr PyDict_Copy(IntPtr pointer) => Delegates.PyDict_Copy(pointer); + + + internal static int PyDict_Update(BorrowedReference pointer, BorrowedReference other) => Delegates.PyDict_Update(pointer, other); + + + internal static void PyDict_Clear(IntPtr pointer) => Delegates.PyDict_Clear(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); + internal static IntPtr _PyDict_Size(IntPtr pointer) => Delegates._PyDict_Size(pointer); - /// - /// Return value: New reference. - /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PySet_New(IntPtr iterable); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PySet_Add(IntPtr set, IntPtr key); + internal static NewReference PySet_New(BorrowedReference iterable) => Delegates.PySet_New(iterable); + + + internal static int PySet_Add(BorrowedReference set, BorrowedReference key) => Delegates.PySet_Add(set, key); /// - /// Return 1 if found, 0 if not found, and -1 if an error is encountered. + /// Return 1 if found, 0 if not found, and -1 if an error is encountered. /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PySet_Contains(IntPtr anyset, IntPtr key); + + internal static int PySet_Contains(BorrowedReference anyset, BorrowedReference key) => Delegates.PySet_Contains(anyset, key); //==================================================================== // Python list API @@ -1740,73 +1769,72 @@ 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, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyList_AsTuple(IntPtr pointer); + private static IntPtr PyList_New(IntPtr size) => Delegates.PyList_New(size); + + + internal static IntPtr PyList_AsTuple(IntPtr pointer) => Delegates.PyList_AsTuple(pointer); internal static BorrowedReference PyList_GetItem(BorrowedReference pointer, long index) { return PyList_GetItem(pointer, new IntPtr(index)); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - private static extern BorrowedReference PyList_GetItem(BorrowedReference pointer, IntPtr index); + + private static BorrowedReference PyList_GetItem(BorrowedReference pointer, IntPtr index) => Delegates.PyList_GetItem(pointer, 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); + + private static int PyList_SetItem(IntPtr pointer, IntPtr index, IntPtr value) => Delegates.PyList_SetItem(pointer, index, value); internal static int PyList_Insert(BorrowedReference pointer, long index, IntPtr value) { return PyList_Insert(pointer, new IntPtr(index), value); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - private static extern int PyList_Insert(BorrowedReference pointer, IntPtr index, IntPtr value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyList_Append(BorrowedReference pointer, IntPtr value); + private static int PyList_Insert(BorrowedReference pointer, IntPtr index, IntPtr value) => Delegates.PyList_Insert(pointer, index, value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyList_Reverse(BorrowedReference pointer); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyList_Sort(BorrowedReference pointer); + internal static int PyList_Append(BorrowedReference pointer, IntPtr value) => Delegates.PyList_Append(pointer, value); + + + internal static int PyList_Reverse(BorrowedReference pointer) => Delegates.PyList_Reverse(pointer); + + + internal static int PyList_Sort(BorrowedReference pointer) => Delegates.PyList_Sort(pointer); internal static IntPtr PyList_GetSlice(IntPtr pointer, long start, long end) { return PyList_GetSlice(pointer, new IntPtr(start), new IntPtr(end)); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr PyList_GetSlice(IntPtr pointer, IntPtr start, IntPtr end); + + private static IntPtr PyList_GetSlice(IntPtr pointer, IntPtr start, IntPtr end) => Delegates.PyList_GetSlice(pointer, start, end); 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(BorrowedReference pointer) - { - return (long)_PyList_Size(pointer); - } + private static int PyList_SetSlice(IntPtr pointer, IntPtr start, IntPtr end, IntPtr value) => Delegates.PyList_SetSlice(pointer, start, end, value); + - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyList_Size")] - private static extern IntPtr _PyList_Size(BorrowedReference pointer); + internal static nint PyList_Size(BorrowedReference pointer) => Delegates.PyList_Size(pointer); //==================================================================== // Python tuple API //==================================================================== + internal static bool PyTuple_Check(BorrowedReference ob) + { + return PyObject_TYPE(ob) == new BorrowedReference(PyTupleType); + } internal static bool PyTuple_Check(IntPtr ob) { return PyObject_TYPE(ob) == PyTupleType; @@ -1817,40 +1845,39 @@ 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); + private static IntPtr PyTuple_New(IntPtr size) => Delegates.PyTuple_New(size); + + internal static BorrowedReference PyTuple_GetItem(BorrowedReference pointer, long index) + => PyTuple_GetItem(pointer, new IntPtr(index)); internal static IntPtr PyTuple_GetItem(IntPtr pointer, long index) { - return PyTuple_GetItem(pointer, new IntPtr(index)); + return PyTuple_GetItem(new BorrowedReference(pointer), new IntPtr(index)) + .DangerousGetAddressOrNull(); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr PyTuple_GetItem(IntPtr pointer, IntPtr index); + + private static BorrowedReference PyTuple_GetItem(BorrowedReference pointer, IntPtr index) => Delegates.PyTuple_GetItem(pointer, index); internal static int PyTuple_SetItem(IntPtr pointer, long index, IntPtr value) { return PyTuple_SetItem(pointer, new IntPtr(index), value); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - private static extern int PyTuple_SetItem(IntPtr pointer, IntPtr index, IntPtr value); + + private static int PyTuple_SetItem(IntPtr pointer, IntPtr index, IntPtr value) => Delegates.PyTuple_SetItem(pointer, index, value); 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); - } + private static IntPtr PyTuple_GetSlice(IntPtr pointer, IntPtr start, IntPtr end) => Delegates.PyTuple_GetSlice(pointer, start, end); + - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyTuple_Size")] - private static extern IntPtr _PyTuple_Size(IntPtr pointer); + internal static nint PyTuple_Size(IntPtr pointer) => PyTuple_Size(new BorrowedReference(pointer)); + internal static nint PyTuple_Size(BorrowedReference pointer) => Delegates.PyTuple_Size(pointer); //==================================================================== @@ -1864,135 +1891,167 @@ internal static bool PyIter_Check(IntPtr pointer) return tp_iternext != IntPtr.Zero && tp_iternext != _PyObject_NextNotImplemented; } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyIter_Next(IntPtr pointer); + + internal static IntPtr PyIter_Next(IntPtr pointer) + => Delegates.PyIter_Next(new BorrowedReference(pointer)).DangerousMoveToPointerOrNull(); + internal static NewReference PyIter_Next(BorrowedReference pointer) => Delegates.PyIter_Next(pointer); //==================================================================== // Python module API //==================================================================== - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyModule_New(string name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern string PyModule_GetName(IntPtr module); + internal static NewReference PyModule_New(string name) + { + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PyModule_New(namePtr); + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyModule_GetDict(IntPtr module); + internal static string PyModule_GetName(IntPtr module) + => Delegates.PyModule_GetName(module).ToString(Encoding.UTF8); + + internal static BorrowedReference PyModule_GetDict(BorrowedReference module) => Delegates.PyModule_GetDict(module); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern string PyModule_GetFilename(IntPtr module); + + internal static string PyModule_GetFilename(IntPtr module) + => Delegates.PyModule_GetFilename(module).ToString(Encoding.UTF8); #if PYTHON_WITH_PYDEBUG [DllImport(_PythonDll, EntryPoint = "PyModule_Create2TraceRefs", CallingConvention = CallingConvention.Cdecl)] #else - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + #endif - internal static extern IntPtr PyModule_Create2(IntPtr module, int apiver); + internal static IntPtr PyModule_Create2(IntPtr module, int apiver) => Delegates.PyModule_Create2(module, apiver); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyImport_Import(IntPtr name); + + internal static IntPtr PyImport_Import(IntPtr name) => Delegates.PyImport_Import(name); /// /// Return value: New reference. /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyImport_ImportModule(string name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyImport_ReloadModule(IntPtr module); + internal static IntPtr PyImport_ImportModule(string name) + { + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PyImport_ImportModule(namePtr); + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyImport_AddModule(string name); + internal static IntPtr PyImport_ReloadModule(IntPtr module) => Delegates.PyImport_ReloadModule(module); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyImport_GetModuleDict(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PySys_SetArgvEx( - int argc, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(StrArrayMarshaler))] string[] argv, - int updatepath - ); + internal static BorrowedReference PyImport_AddModule(string name) + { + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PyImport_AddModule(namePtr); + } + + internal static BorrowedReference PyImport_GetModuleDict() => Delegates.PyImport_GetModuleDict(); + + + internal static void PySys_SetArgvEx(int argc, string[] argv, int updatepath) + { + var marshaler = StrArrayMarshaler.GetInstance(null); + var argvPtr = marshaler.MarshalManagedToNative(argv); + try + { + Delegates.PySys_SetArgvEx(argc, argvPtr, updatepath); + } + finally + { + marshaler.CleanUpNativeData(argvPtr); + } + } /// /// Return value: Borrowed reference. /// Return the object name from the sys module or NULL if it does not exist, without setting an exception. /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern BorrowedReference PySys_GetObject(string name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PySys_SetObject(string name, IntPtr ob); + internal static BorrowedReference PySys_GetObject(string name) + { + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PySys_GetObject(namePtr); + } + + internal static int PySys_SetObject(string name, BorrowedReference ob) + { + using var namePtr = new StrPtr(name, Encoding.UTF8); + return Delegates.PySys_SetObject(namePtr, ob); + } //==================================================================== // Python type object API //==================================================================== - internal static bool PyType_Check(IntPtr ob) { return PyObject_TypeCheck(ob, PyTypeType); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyType_Modified(IntPtr type); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern bool PyType_IsSubtype(IntPtr t1, IntPtr t2); + internal static void PyType_Modified(IntPtr type) => Delegates.PyType_Modified(type); + internal static bool PyType_IsSubtype(BorrowedReference t1, IntPtr ofType) + => PyType_IsSubtype(t1, new BorrowedReference(ofType)); + internal static bool PyType_IsSubtype(BorrowedReference t1, BorrowedReference t2) => Delegates.PyType_IsSubtype(t1, t2); internal static bool PyObject_TypeCheck(IntPtr ob, IntPtr tp) + => PyObject_TypeCheck(new BorrowedReference(ob), new BorrowedReference(tp)); + internal static bool PyObject_TypeCheck(BorrowedReference ob, BorrowedReference tp) { - IntPtr t = PyObject_TYPE(ob); + BorrowedReference t = PyObject_TYPE(ob); return (t == tp) || PyType_IsSubtype(t, tp); } - internal static bool PyType_IsSameAsOrSubtype(IntPtr type, IntPtr ofType) + internal static bool PyType_IsSameAsOrSubtype(BorrowedReference type, IntPtr ofType) + => PyType_IsSameAsOrSubtype(type, new BorrowedReference(ofType)); + internal static bool PyType_IsSameAsOrSubtype(BorrowedReference type, BorrowedReference ofType) { return (type == ofType) || PyType_IsSubtype(type, ofType); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw); + + internal static IntPtr PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw) => Delegates.PyType_GenericNew(type, args, kw); 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); + + private static IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n) => Delegates.PyType_GenericAlloc(type, n); /// /// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a type’s base class. Return 0 on success, or return -1 and sets an exception on error. /// - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyType_Ready(IntPtr type); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr _PyType_Lookup(IntPtr type, IntPtr name); + internal static int PyType_Ready(IntPtr type) => Delegates.PyType_Ready(type); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyObject_GenericGetAttr(IntPtr obj, IntPtr name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyObject_GenericSetAttr(IntPtr obj, IntPtr name, IntPtr value); + internal static IntPtr _PyType_Lookup(IntPtr type, IntPtr name) => Delegates._PyType_Lookup(type, name); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr _PyObject_GetDictPtr(IntPtr obj); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyObject_GC_Del(IntPtr tp); + internal static IntPtr PyObject_GenericGetAttr(IntPtr obj, IntPtr name) => Delegates.PyObject_GenericGetAttr(obj, name); + internal static bool PyType_SUPPORTS_WEAKREFS(IntPtr type) + { + return Marshal.ReadIntPtr(type, TypeOffset.tp_weaklistoffset) != IntPtr.Zero; + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyObject_GC_Track(IntPtr tp); + internal static int PyObject_GenericSetAttr(IntPtr obj, IntPtr name, IntPtr value) => Delegates.PyObject_GenericSetAttr(obj, name, value); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyObject_GC_UnTrack(IntPtr tp); + internal static BorrowedReference* _PyObject_GetDictPtr(BorrowedReference obj) => Delegates._PyObject_GetDictPtr(obj); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void _PyObject_Dump(IntPtr ob); + internal static void PyObject_GC_Del(IntPtr tp) => Delegates.PyObject_GC_Del(tp); + + internal static IntPtr _PyObject_GC_Calloc(IntPtr basicsize) => Delegates._PyObject_GC_Calloc(basicsize); + + internal static void PyObject_GC_Track(IntPtr tp) => Delegates.PyObject_GC_Track(tp); + + internal static void PyObject_GC_UnTrack(IntPtr tp) => Delegates.PyObject_GC_UnTrack(tp); + + internal static void _PyObject_Dump(IntPtr ob) => Delegates._PyObject_Dump(ob); + + internal static void PyObject_ClearWeakRefs(IntPtr obj) => Delegates.PyObject_ClearWeakRefs(obj); //==================================================================== // Python memory API @@ -2003,70 +2062,79 @@ 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); + + private static IntPtr PyMem_Malloc(IntPtr size) => Delegates.PyMem_Malloc(size); internal static IntPtr PyMem_Realloc(IntPtr ptr, long size) { return PyMem_Realloc(ptr, new IntPtr(size)); } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr PyMem_Realloc(IntPtr ptr, IntPtr size); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyMem_Free(IntPtr ptr); + private static IntPtr PyMem_Realloc(IntPtr ptr, IntPtr size) => Delegates.PyMem_Realloc(ptr, size); + + + internal static void PyMem_Free(IntPtr ptr) => Delegates.PyMem_Free(ptr); //==================================================================== // Python exception API //==================================================================== - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyErr_SetString(IntPtr ob, string message); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyErr_SetObject(BorrowedReference type, BorrowedReference exceptionObject); + internal static void PyErr_SetString(IntPtr ob, string message) + { + using var msgPtr = new StrPtr(message, Encoding.UTF8); + Delegates.PyErr_SetString(ob, msgPtr); + } - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyErr_SetFromErrno(IntPtr ob); + internal static void PyErr_SetObject(BorrowedReference type, BorrowedReference exceptionObject) => Delegates.PyErr_SetObject(type, exceptionObject); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyErr_SetNone(IntPtr ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyErr_ExceptionMatches(IntPtr exception); + internal static IntPtr PyErr_SetFromErrno(IntPtr ob) => Delegates.PyErr_SetFromErrno(ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyErr_GivenExceptionMatches(IntPtr ob, IntPtr val); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyErr_NormalizeException(IntPtr ob, IntPtr val, IntPtr tb); + internal static void PyErr_SetNone(IntPtr ob) => Delegates.PyErr_SetNone(ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyErr_Occurred(); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyErr_Fetch(out IntPtr ob, out IntPtr val, out IntPtr tb); + internal static int PyErr_ExceptionMatches(IntPtr exception) => Delegates.PyErr_ExceptionMatches(exception); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyErr_Restore(IntPtr ob, IntPtr val, IntPtr tb); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyErr_Clear(); + internal static int PyErr_GivenExceptionMatches(IntPtr ob, IntPtr val) => Delegates.PyErr_GivenExceptionMatches(ob, val); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern void PyErr_Print(); + + internal static void PyErr_NormalizeException(ref IntPtr ob, ref IntPtr val, ref IntPtr tb) => Delegates.PyErr_NormalizeException(ref ob, ref val, ref tb); + + + internal static IntPtr PyErr_Occurred() => Delegates.PyErr_Occurred(); + + + internal static void PyErr_Fetch(out IntPtr ob, out IntPtr val, out IntPtr tb) => Delegates.PyErr_Fetch(out ob, out val, out tb); + + + internal static void PyErr_Restore(IntPtr ob, IntPtr val, IntPtr tb) => Delegates.PyErr_Restore(ob, val, tb); + + + internal static void PyErr_Clear() => Delegates.PyErr_Clear(); + + + internal static void PyErr_Print() => Delegates.PyErr_Print(); + + /// + /// Set the cause associated with the exception to cause. Use NULL to clear it. There is no type check to make sure that cause is either an exception instance or None. This steals a reference to cause. + /// + + internal static void PyException_SetCause(IntPtr ex, IntPtr cause) => Delegates.PyException_SetCause(ex, cause); //==================================================================== // Cell API //==================================================================== - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern NewReference PyCell_Get(BorrowedReference cell); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyCell_Set(BorrowedReference cell, IntPtr value); + internal static NewReference PyCell_Get(BorrowedReference cell) => Delegates.PyCell_Get(cell); + + + internal static int PyCell_Set(BorrowedReference cell, IntPtr value) => Delegates.PyCell_Set(cell, value); //==================================================================== // Python GC API @@ -2078,14 +2146,14 @@ internal static IntPtr PyMem_Realloc(IntPtr ptr, long size) internal const long _PyGC_REFS_TENTATIVELY_UNREACHABLE = -4; - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyGC_Collect(); - internal static IntPtr _Py_AS_GC(IntPtr ob) + internal static IntPtr PyGC_Collect() => Delegates.PyGC_Collect(); + + internal static IntPtr _Py_AS_GC(BorrowedReference ob) { // XXX: PyGC_Head has a force alignment depend on platform. // See PyGC_Head in objimpl.h for more details. - return Is32Bit ? ob - 16 : ob - 24; + return ob.DangerousGetAddress() - (Is32Bit ? 16 : 24); } internal static IntPtr _Py_FROM_GC(IntPtr gc) @@ -2107,15 +2175,13 @@ internal static IntPtr _PyGCHead_REFS(IntPtr gc) } } - internal static IntPtr _PyGC_REFS(IntPtr ob) + internal static IntPtr _PyGC_REFS(BorrowedReference ob) { return _PyGCHead_REFS(_Py_AS_GC(ob)); } - internal static bool _PyObject_GC_IS_TRACKED(IntPtr ob) - { - return (long)_PyGC_REFS(ob) != _PyGC_REFS_UNTRACKED; - } + internal static bool _PyObject_GC_IS_TRACKED(BorrowedReference ob) + => (long)_PyGC_REFS(ob) != _PyGC_REFS_UNTRACKED; internal static void Py_CLEAR(ref IntPtr ob) { @@ -2123,39 +2189,56 @@ internal static void Py_CLEAR(ref IntPtr ob) ob = IntPtr.Zero; } + internal static unsafe void Py_SETREF(IntPtr ob, int offset, IntPtr target) + { + var p = (void**)(ob + offset); + if (*p != null) + { + XDecref((IntPtr)(*p)); + } + *p = (void*)target; + } + //==================================================================== // Python Capsules API //==================================================================== - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern NewReference PyCapsule_New(IntPtr pointer, string name, IntPtr destructor); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyCapsule_GetPointer(BorrowedReference capsule, string name); + internal static NewReference PyCapsule_New(IntPtr pointer, IntPtr name, IntPtr destructor) + => Delegates.PyCapsule_New(pointer, name, destructor); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int PyCapsule_SetPointer(BorrowedReference capsule, IntPtr pointer); + internal static IntPtr PyCapsule_GetPointer(BorrowedReference capsule, IntPtr name) + { + return Delegates.PyCapsule_GetPointer(capsule, name); + } + + internal static int PyCapsule_SetPointer(BorrowedReference capsule, IntPtr pointer) => Delegates.PyCapsule_SetPointer(capsule, pointer); //==================================================================== // Miscellaneous //==================================================================== - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyMethod_Self(IntPtr ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr PyMethod_Function(IntPtr ob); + internal static IntPtr PyMethod_Self(IntPtr ob) => Delegates.PyMethod_Self(ob); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int Py_AddPendingCall(IntPtr func, IntPtr arg); - [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] - internal static extern int Py_MakePendingCalls(); + internal static IntPtr PyMethod_Function(IntPtr ob) => Delegates.PyMethod_Function(ob); + + + internal static int Py_AddPendingCall(IntPtr func, IntPtr arg) => Delegates.Py_AddPendingCall(func, arg); + + + internal static int PyThreadState_SetAsyncExcLLP64(uint id, IntPtr exc) => Delegates.PyThreadState_SetAsyncExcLLP64(id, exc); + + internal static int PyThreadState_SetAsyncExcLP64(ulong id, IntPtr exc) => Delegates.PyThreadState_SetAsyncExcLP64(id, exc); + + + internal static int Py_MakePendingCalls() => Delegates.Py_MakePendingCalls(); internal static void SetNoSiteFlag() { - var loader = LibraryLoader.Get(NativeCodePageHelper.OperatingSystem); - IntPtr dllLocal; + var loader = LibraryLoader.Instance; + IntPtr dllLocal = IntPtr.Zero; if (_PythonDll != "__Internal") { dllLocal = loader.Load(_PythonDll); @@ -2183,7 +2266,545 @@ internal static void SetNoSiteFlag() /// internal static IntPtr GetBuiltins() { - return PyImport_ImportModule("builtins"); + return PyImport_Import(PyIdentifier.builtins); + } + + private static class Delegates + { + static readonly ILibraryLoader libraryLoader = LibraryLoader.Instance; + + static Delegates() + { + PyDictProxy_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDictProxy_New), GetUnmanagedDll(_PythonDll)); + Py_IncRef = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_IncRef), GetUnmanagedDll(_PythonDll)); + Py_DecRef = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_DecRef), GetUnmanagedDll(_PythonDll)); + Py_Initialize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_Initialize), GetUnmanagedDll(_PythonDll)); + Py_InitializeEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_InitializeEx), GetUnmanagedDll(_PythonDll)); + Py_IsInitialized = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_IsInitialized), GetUnmanagedDll(_PythonDll)); + Py_Finalize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_Finalize), GetUnmanagedDll(_PythonDll)); + Py_NewInterpreter = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_NewInterpreter), GetUnmanagedDll(_PythonDll)); + Py_EndInterpreter = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_EndInterpreter), GetUnmanagedDll(_PythonDll)); + PyThreadState_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThreadState_New), GetUnmanagedDll(_PythonDll)); + PyThreadState_Get = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThreadState_Get), GetUnmanagedDll(_PythonDll)); + _PyThreadState_UncheckedGet = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyThreadState_UncheckedGet), GetUnmanagedDll(_PythonDll)); + PyThread_get_key_value = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThread_get_key_value), GetUnmanagedDll(_PythonDll)); + PyThread_get_thread_ident = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThread_get_thread_ident), GetUnmanagedDll(_PythonDll)); + PyThread_set_key_value = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThread_set_key_value), GetUnmanagedDll(_PythonDll)); + PyThreadState_Swap = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThreadState_Swap), GetUnmanagedDll(_PythonDll)); + PyGILState_Ensure = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_Ensure), GetUnmanagedDll(_PythonDll)); + PyGILState_Release = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_Release), GetUnmanagedDll(_PythonDll)); + PyGILState_GetThisThreadState = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_GetThisThreadState), GetUnmanagedDll(_PythonDll)); + Py_Main = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_Main), GetUnmanagedDll(_PythonDll)); + PyEval_InitThreads = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_InitThreads), GetUnmanagedDll(_PythonDll)); + PyEval_ThreadsInitialized = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_ThreadsInitialized), GetUnmanagedDll(_PythonDll)); + PyEval_AcquireLock = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_AcquireLock), GetUnmanagedDll(_PythonDll)); + PyEval_ReleaseLock = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_ReleaseLock), GetUnmanagedDll(_PythonDll)); + PyEval_AcquireThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_AcquireThread), GetUnmanagedDll(_PythonDll)); + PyEval_ReleaseThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_ReleaseThread), GetUnmanagedDll(_PythonDll)); + PyEval_SaveThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_SaveThread), GetUnmanagedDll(_PythonDll)); + PyEval_RestoreThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_RestoreThread), GetUnmanagedDll(_PythonDll)); + PyEval_GetBuiltins = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_GetBuiltins), GetUnmanagedDll(_PythonDll)); + PyEval_GetGlobals = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_GetGlobals), GetUnmanagedDll(_PythonDll)); + PyEval_GetLocals = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_GetLocals), GetUnmanagedDll(_PythonDll)); + Py_GetProgramName = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetProgramName), GetUnmanagedDll(_PythonDll)); + Py_SetProgramName = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_SetProgramName), GetUnmanagedDll(_PythonDll)); + Py_GetPythonHome = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetPythonHome), GetUnmanagedDll(_PythonDll)); + Py_SetPythonHome = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_SetPythonHome), GetUnmanagedDll(_PythonDll)); + Py_GetPath = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetPath), GetUnmanagedDll(_PythonDll)); + Py_SetPath = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_SetPath), GetUnmanagedDll(_PythonDll)); + Py_GetVersion = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetVersion), GetUnmanagedDll(_PythonDll)); + Py_GetPlatform = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetPlatform), GetUnmanagedDll(_PythonDll)); + Py_GetCopyright = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetCopyright), GetUnmanagedDll(_PythonDll)); + Py_GetCompiler = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetCompiler), GetUnmanagedDll(_PythonDll)); + Py_GetBuildInfo = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetBuildInfo), GetUnmanagedDll(_PythonDll)); + PyRun_SimpleStringFlags = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyRun_SimpleStringFlags), GetUnmanagedDll(_PythonDll)); + PyRun_StringFlags = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyRun_StringFlags), GetUnmanagedDll(_PythonDll)); + PyEval_EvalCode = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_EvalCode), GetUnmanagedDll(_PythonDll)); + Py_CompileStringObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_CompileStringObject), GetUnmanagedDll(_PythonDll)); + PyImport_ExecCodeModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ExecCodeModule), GetUnmanagedDll(_PythonDll)); + PyCFunction_NewEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCFunction_NewEx), GetUnmanagedDll(_PythonDll)); + PyCFunction_Call = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCFunction_Call), GetUnmanagedDll(_PythonDll)); + PyMethod_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMethod_New), GetUnmanagedDll(_PythonDll)); + PyObject_HasAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_HasAttrString), GetUnmanagedDll(_PythonDll)); + PyObject_GetAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetAttrString), GetUnmanagedDll(_PythonDll)); + PyObject_SetAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetAttrString), GetUnmanagedDll(_PythonDll)); + PyObject_HasAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_HasAttr), GetUnmanagedDll(_PythonDll)); + PyObject_GetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetAttr), GetUnmanagedDll(_PythonDll)); + PyObject_SetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetAttr), GetUnmanagedDll(_PythonDll)); + PyObject_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetItem), GetUnmanagedDll(_PythonDll)); + PyObject_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetItem), GetUnmanagedDll(_PythonDll)); + PyObject_DelItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_DelItem), GetUnmanagedDll(_PythonDll)); + PyObject_GetIter = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetIter), GetUnmanagedDll(_PythonDll)); + PyObject_Call = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Call), GetUnmanagedDll(_PythonDll)); + PyObject_CallObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_CallObject), GetUnmanagedDll(_PythonDll)); + PyObject_RichCompareBool = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_RichCompareBool), GetUnmanagedDll(_PythonDll)); + PyObject_IsInstance = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsInstance), GetUnmanagedDll(_PythonDll)); + PyObject_IsSubclass = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsSubclass), GetUnmanagedDll(_PythonDll)); + PyCallable_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCallable_Check), GetUnmanagedDll(_PythonDll)); + PyObject_IsTrue = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsTrue), GetUnmanagedDll(_PythonDll)); + PyObject_Not = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Not), GetUnmanagedDll(_PythonDll)); + _PyObject_Size = (delegate* unmanaged[Cdecl])GetFunctionByName("PyObject_Size", GetUnmanagedDll(_PythonDll)); + PyObject_Hash = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Hash), GetUnmanagedDll(_PythonDll)); + PyObject_Repr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Repr), GetUnmanagedDll(_PythonDll)); + PyObject_Str = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Str), GetUnmanagedDll(_PythonDll)); + PyObject_Unicode = (delegate* unmanaged[Cdecl])GetFunctionByName("PyObject_Str", GetUnmanagedDll(_PythonDll)); + PyObject_Dir = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Dir), GetUnmanagedDll(_PythonDll)); + PyObject_GetBuffer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetBuffer), GetUnmanagedDll(_PythonDll)); + PyBuffer_Release = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_Release), GetUnmanagedDll(_PythonDll)); + try + { + PyBuffer_SizeFromFormat = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_SizeFromFormat), GetUnmanagedDll(_PythonDll)); + } + catch (MissingMethodException) + { + // only in 3.9+ + } + PyBuffer_IsContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_IsContiguous), GetUnmanagedDll(_PythonDll)); + PyBuffer_GetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_GetPointer), GetUnmanagedDll(_PythonDll)); + PyBuffer_FromContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FromContiguous), GetUnmanagedDll(_PythonDll)); + PyBuffer_ToContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_ToContiguous), GetUnmanagedDll(_PythonDll)); + PyBuffer_FillContiguousStrides = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FillContiguousStrides), GetUnmanagedDll(_PythonDll)); + PyBuffer_FillInfo = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FillInfo), GetUnmanagedDll(_PythonDll)); + PyNumber_Int = (delegate* unmanaged[Cdecl])GetFunctionByName("PyNumber_Long", GetUnmanagedDll(_PythonDll)); + PyNumber_Long = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Long), GetUnmanagedDll(_PythonDll)); + PyNumber_Float = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Float), GetUnmanagedDll(_PythonDll)); + PyNumber_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Check), GetUnmanagedDll(_PythonDll)); + PyInt_FromLong = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_FromLong", GetUnmanagedDll(_PythonDll)); + PyInt_AsLong = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsLong", GetUnmanagedDll(_PythonDll)); + PyLong_FromLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromLong), GetUnmanagedDll(_PythonDll)); + PyLong_FromUnsignedLong32 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_FromUnsignedLong", GetUnmanagedDll(_PythonDll)); + PyLong_FromUnsignedLong64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_FromUnsignedLong", GetUnmanagedDll(_PythonDll)); + PyLong_FromDouble = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromDouble), GetUnmanagedDll(_PythonDll)); + PyLong_FromLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromLongLong), GetUnmanagedDll(_PythonDll)); + PyLong_FromUnsignedLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromUnsignedLongLong), GetUnmanagedDll(_PythonDll)); + PyLong_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromString), GetUnmanagedDll(_PythonDll)); + PyLong_AsLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsLong), GetUnmanagedDll(_PythonDll)); + PyLong_AsUnsignedLong32 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsUnsignedLong", GetUnmanagedDll(_PythonDll)); + PyLong_AsUnsignedLong64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsUnsignedLong", GetUnmanagedDll(_PythonDll)); + PyLong_AsLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsLongLong), GetUnmanagedDll(_PythonDll)); + PyLong_AsUnsignedLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsUnsignedLongLong), GetUnmanagedDll(_PythonDll)); + PyLong_FromVoidPtr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromVoidPtr), GetUnmanagedDll(_PythonDll)); + PyLong_AsVoidPtr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsVoidPtr), GetUnmanagedDll(_PythonDll)); + PyFloat_FromDouble = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyFloat_FromDouble), GetUnmanagedDll(_PythonDll)); + PyFloat_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyFloat_FromString), GetUnmanagedDll(_PythonDll)); + PyFloat_AsDouble = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyFloat_AsDouble), GetUnmanagedDll(_PythonDll)); + PyNumber_Add = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Add), GetUnmanagedDll(_PythonDll)); + PyNumber_Subtract = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Subtract), GetUnmanagedDll(_PythonDll)); + PyNumber_Multiply = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Multiply), GetUnmanagedDll(_PythonDll)); + PyNumber_TrueDivide = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_TrueDivide), GetUnmanagedDll(_PythonDll)); + PyNumber_And = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_And), GetUnmanagedDll(_PythonDll)); + PyNumber_Xor = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Xor), GetUnmanagedDll(_PythonDll)); + PyNumber_Or = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Or), GetUnmanagedDll(_PythonDll)); + PyNumber_Lshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Lshift), GetUnmanagedDll(_PythonDll)); + PyNumber_Rshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Rshift), GetUnmanagedDll(_PythonDll)); + PyNumber_Power = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Power), GetUnmanagedDll(_PythonDll)); + PyNumber_Remainder = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Remainder), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceAdd = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceAdd), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceSubtract = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceSubtract), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceMultiply = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceMultiply), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceTrueDivide = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceTrueDivide), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceAnd = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceAnd), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceXor = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceXor), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceOr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceOr), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceLshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceLshift), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceRshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceRshift), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlacePower = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlacePower), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceRemainder = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceRemainder), GetUnmanagedDll(_PythonDll)); + PyNumber_Negative = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Negative), GetUnmanagedDll(_PythonDll)); + PyNumber_Positive = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Positive), GetUnmanagedDll(_PythonDll)); + PyNumber_Invert = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Invert), GetUnmanagedDll(_PythonDll)); + PySequence_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Check), GetUnmanagedDll(_PythonDll)); + PySequence_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_GetItem), GetUnmanagedDll(_PythonDll)); + PySequence_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_SetItem), GetUnmanagedDll(_PythonDll)); + PySequence_DelItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_DelItem), GetUnmanagedDll(_PythonDll)); + PySequence_GetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_GetSlice), GetUnmanagedDll(_PythonDll)); + PySequence_SetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_SetSlice), GetUnmanagedDll(_PythonDll)); + PySequence_DelSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_DelSlice), GetUnmanagedDll(_PythonDll)); + PySequence_Size = (delegate* unmanaged[Cdecl])GetFunctionByName("PySequence_Size", GetUnmanagedDll(_PythonDll)); + PySequence_Contains = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Contains), GetUnmanagedDll(_PythonDll)); + PySequence_Concat = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Concat), GetUnmanagedDll(_PythonDll)); + PySequence_Repeat = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Repeat), GetUnmanagedDll(_PythonDll)); + PySequence_Index = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Index), GetUnmanagedDll(_PythonDll)); + _PySequence_Count = (delegate* unmanaged[Cdecl])GetFunctionByName("PySequence_Count", GetUnmanagedDll(_PythonDll)); + PySequence_Tuple = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Tuple), GetUnmanagedDll(_PythonDll)); + PySequence_List = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_List), GetUnmanagedDll(_PythonDll)); + PyBytes_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_FromString), GetUnmanagedDll(_PythonDll)); + _PyBytes_Size = (delegate* unmanaged[Cdecl])GetFunctionByName("PyBytes_Size", GetUnmanagedDll(_PythonDll)); + PyUnicode_FromStringAndSize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_FromStringAndSize), GetUnmanagedDll(_PythonDll)); + PyUnicode_AsUTF8 = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUTF8), GetUnmanagedDll(_PythonDll)); + PyUnicode_FromObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_FromObject), GetUnmanagedDll(_PythonDll)); + PyUnicode_FromEncodedObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_FromEncodedObject), GetUnmanagedDll(_PythonDll)); + PyUnicode_FromKindAndData = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_FromKindAndData), GetUnmanagedDll(_PythonDll)); + PyUnicode_GetMax = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_GetMax), GetUnmanagedDll(_PythonDll)); + _PyUnicode_GetSize = (delegate* unmanaged[Cdecl])GetFunctionByName("PyUnicode_GetSize", GetUnmanagedDll(_PythonDll)); + PyUnicode_AsUnicode = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUnicode), GetUnmanagedDll(_PythonDll)); + PyUnicode_AsUTF16String = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUTF16String), GetUnmanagedDll(_PythonDll)); + PyUnicode_FromOrdinal = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_FromOrdinal), GetUnmanagedDll(_PythonDll)); + PyUnicode_InternFromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_InternFromString), GetUnmanagedDll(_PythonDll)); + PyUnicode_Compare = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_Compare), GetUnmanagedDll(_PythonDll)); + PyDict_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_New), GetUnmanagedDll(_PythonDll)); + PyDict_Next = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Next), GetUnmanagedDll(_PythonDll)); + PyDict_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_GetItem), GetUnmanagedDll(_PythonDll)); + PyDict_GetItemString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_GetItemString), GetUnmanagedDll(_PythonDll)); + PyDict_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_SetItem), GetUnmanagedDll(_PythonDll)); + PyDict_SetItemString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_SetItemString), GetUnmanagedDll(_PythonDll)); + PyDict_DelItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_DelItem), GetUnmanagedDll(_PythonDll)); + PyDict_DelItemString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_DelItemString), GetUnmanagedDll(_PythonDll)); + PyMapping_HasKey = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMapping_HasKey), GetUnmanagedDll(_PythonDll)); + PyDict_Keys = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Keys), GetUnmanagedDll(_PythonDll)); + PyDict_Values = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Values), GetUnmanagedDll(_PythonDll)); + PyDict_Items = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Items), GetUnmanagedDll(_PythonDll)); + PyDict_Copy = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Copy), GetUnmanagedDll(_PythonDll)); + PyDict_Update = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Update), GetUnmanagedDll(_PythonDll)); + PyDict_Clear = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Clear), GetUnmanagedDll(_PythonDll)); + _PyDict_Size = (delegate* unmanaged[Cdecl])GetFunctionByName("PyDict_Size", GetUnmanagedDll(_PythonDll)); + PySet_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySet_New), GetUnmanagedDll(_PythonDll)); + PySet_Add = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySet_Add), GetUnmanagedDll(_PythonDll)); + PySet_Contains = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySet_Contains), GetUnmanagedDll(_PythonDll)); + PyList_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_New), GetUnmanagedDll(_PythonDll)); + PyList_AsTuple = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_AsTuple), GetUnmanagedDll(_PythonDll)); + PyList_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_GetItem), GetUnmanagedDll(_PythonDll)); + PyList_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_SetItem), GetUnmanagedDll(_PythonDll)); + PyList_Insert = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Insert), GetUnmanagedDll(_PythonDll)); + PyList_Append = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Append), GetUnmanagedDll(_PythonDll)); + PyList_Reverse = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Reverse), GetUnmanagedDll(_PythonDll)); + PyList_Sort = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Sort), GetUnmanagedDll(_PythonDll)); + PyList_GetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_GetSlice), GetUnmanagedDll(_PythonDll)); + PyList_SetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_SetSlice), GetUnmanagedDll(_PythonDll)); + PyList_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Size), GetUnmanagedDll(_PythonDll)); + PyTuple_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_New), GetUnmanagedDll(_PythonDll)); + PyTuple_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_GetItem), GetUnmanagedDll(_PythonDll)); + PyTuple_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_SetItem), GetUnmanagedDll(_PythonDll)); + PyTuple_GetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_GetSlice), GetUnmanagedDll(_PythonDll)); + PyTuple_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_Size), GetUnmanagedDll(_PythonDll)); + PyIter_Next = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyIter_Next), GetUnmanagedDll(_PythonDll)); + PyModule_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_New), GetUnmanagedDll(_PythonDll)); + PyModule_GetName = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_GetName), GetUnmanagedDll(_PythonDll)); + PyModule_GetDict = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_GetDict), GetUnmanagedDll(_PythonDll)); + PyModule_GetFilename = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_GetFilename), GetUnmanagedDll(_PythonDll)); + PyModule_Create2 = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_Create2), GetUnmanagedDll(_PythonDll)); + PyImport_Import = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_Import), GetUnmanagedDll(_PythonDll)); + PyImport_ImportModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ImportModule), GetUnmanagedDll(_PythonDll)); + PyImport_ReloadModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ReloadModule), GetUnmanagedDll(_PythonDll)); + PyImport_AddModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_AddModule), GetUnmanagedDll(_PythonDll)); + PyImport_GetModuleDict = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_GetModuleDict), GetUnmanagedDll(_PythonDll)); + PySys_SetArgvEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_SetArgvEx), GetUnmanagedDll(_PythonDll)); + PySys_GetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_GetObject), GetUnmanagedDll(_PythonDll)); + PySys_SetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_SetObject), GetUnmanagedDll(_PythonDll)); + PyType_Modified = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_Modified), GetUnmanagedDll(_PythonDll)); + PyType_IsSubtype = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_IsSubtype), GetUnmanagedDll(_PythonDll)); + PyType_GenericNew = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GenericNew), GetUnmanagedDll(_PythonDll)); + PyType_GenericAlloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GenericAlloc), GetUnmanagedDll(_PythonDll)); + PyType_Ready = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_Ready), GetUnmanagedDll(_PythonDll)); + _PyType_Lookup = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyType_Lookup), GetUnmanagedDll(_PythonDll)); + PyObject_GenericGetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GenericGetAttr), GetUnmanagedDll(_PythonDll)); + PyObject_GenericSetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GenericSetAttr), GetUnmanagedDll(_PythonDll)); + _PyObject_GetDictPtr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyObject_GetDictPtr), GetUnmanagedDll(_PythonDll)); + PyObject_GC_Del = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_Del), GetUnmanagedDll(_PythonDll)); + _PyObject_GC_Calloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyObject_GC_Calloc), GetUnmanagedDll(_PythonDll)); + PyObject_GC_Track = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_Track), GetUnmanagedDll(_PythonDll)); + PyObject_GC_UnTrack = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_UnTrack), GetUnmanagedDll(_PythonDll)); + _PyObject_Dump = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyObject_Dump), GetUnmanagedDll(_PythonDll)); + PyObject_ClearWeakRefs = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_ClearWeakRefs), GetUnmanagedDll(_PythonDll)); + PyMem_Malloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMem_Malloc), GetUnmanagedDll(_PythonDll)); + PyMem_Realloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMem_Realloc), GetUnmanagedDll(_PythonDll)); + PyMem_Free = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMem_Free), GetUnmanagedDll(_PythonDll)); + PyErr_SetString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_SetString), GetUnmanagedDll(_PythonDll)); + PyErr_SetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_SetObject), GetUnmanagedDll(_PythonDll)); + PyErr_SetFromErrno = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_SetFromErrno), GetUnmanagedDll(_PythonDll)); + PyErr_SetNone = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_SetNone), GetUnmanagedDll(_PythonDll)); + PyErr_ExceptionMatches = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_ExceptionMatches), GetUnmanagedDll(_PythonDll)); + PyErr_GivenExceptionMatches = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_GivenExceptionMatches), GetUnmanagedDll(_PythonDll)); + PyErr_NormalizeException = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_NormalizeException), GetUnmanagedDll(_PythonDll)); + PyErr_Occurred = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Occurred), GetUnmanagedDll(_PythonDll)); + PyErr_Fetch = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Fetch), GetUnmanagedDll(_PythonDll)); + PyErr_Restore = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Restore), GetUnmanagedDll(_PythonDll)); + PyErr_Clear = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Clear), GetUnmanagedDll(_PythonDll)); + PyErr_Print = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Print), GetUnmanagedDll(_PythonDll)); + PyCell_Get = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCell_Get), GetUnmanagedDll(_PythonDll)); + PyCell_Set = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCell_Set), GetUnmanagedDll(_PythonDll)); + PyGC_Collect = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGC_Collect), GetUnmanagedDll(_PythonDll)); + PyCapsule_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_New), GetUnmanagedDll(_PythonDll)); + PyCapsule_GetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_GetPointer), GetUnmanagedDll(_PythonDll)); + PyCapsule_SetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_SetPointer), GetUnmanagedDll(_PythonDll)); + PyMethod_Self = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMethod_Self), GetUnmanagedDll(_PythonDll)); + PyMethod_Function = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMethod_Function), GetUnmanagedDll(_PythonDll)); + Py_AddPendingCall = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_AddPendingCall), GetUnmanagedDll(_PythonDll)); + Py_MakePendingCalls = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_MakePendingCalls), GetUnmanagedDll(_PythonDll)); + PyLong_AsUnsignedSize_t = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsSize_t", GetUnmanagedDll(_PythonDll)); + PyLong_AsSignedSize_t = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsSsize_t", GetUnmanagedDll(_PythonDll)); + PyExplicitlyConvertToInt64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsLongLong", GetUnmanagedDll(_PythonDll)); + PyDict_GetItemWithError = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_GetItemWithError), GetUnmanagedDll(_PythonDll)); + PyException_SetCause = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_SetCause), GetUnmanagedDll(_PythonDll)); + PyThreadState_SetAsyncExcLLP64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll)); + PyThreadState_SetAsyncExcLP64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll)); + } + + static global::System.IntPtr GetUnmanagedDll(string libraryName) + { + if (libraryName is null) return IntPtr.Zero; + return libraryLoader.Load(libraryName); + } + + static global::System.IntPtr GetFunctionByName(string functionName, global::System.IntPtr libraryHandle) + => libraryLoader.GetFunction(libraryHandle, functionName); + + internal static delegate* unmanaged[Cdecl] PyDictProxy_New { get; } + internal static delegate* unmanaged[Cdecl] Py_IncRef { get; } + internal static delegate* unmanaged[Cdecl] Py_DecRef { get; } + internal static delegate* unmanaged[Cdecl] Py_Initialize { get; } + internal static delegate* unmanaged[Cdecl] Py_InitializeEx { get; } + internal static delegate* unmanaged[Cdecl] Py_IsInitialized { get; } + internal static delegate* unmanaged[Cdecl] Py_Finalize { get; } + internal static delegate* unmanaged[Cdecl] Py_NewInterpreter { get; } + internal static delegate* unmanaged[Cdecl] Py_EndInterpreter { get; } + internal static delegate* unmanaged[Cdecl] PyThreadState_New { get; } + internal static delegate* unmanaged[Cdecl] PyThreadState_Get { get; } + internal static delegate* unmanaged[Cdecl] _PyThreadState_UncheckedGet { get; } + internal static delegate* unmanaged[Cdecl] PyThread_get_key_value { get; } + internal static delegate* unmanaged[Cdecl] PyThread_get_thread_ident { get; } + internal static delegate* unmanaged[Cdecl] PyThread_set_key_value { get; } + internal static delegate* unmanaged[Cdecl] PyThreadState_Swap { get; } + internal static delegate* unmanaged[Cdecl] PyGILState_Ensure { get; } + internal static delegate* unmanaged[Cdecl] PyGILState_Release { get; } + internal static delegate* unmanaged[Cdecl] PyGILState_GetThisThreadState { get; } + internal static delegate* unmanaged[Cdecl] Py_Main { get; } + internal static delegate* unmanaged[Cdecl] PyEval_InitThreads { get; } + internal static delegate* unmanaged[Cdecl] PyEval_ThreadsInitialized { get; } + internal static delegate* unmanaged[Cdecl] PyEval_AcquireLock { get; } + internal static delegate* unmanaged[Cdecl] PyEval_ReleaseLock { get; } + internal static delegate* unmanaged[Cdecl] PyEval_AcquireThread { get; } + internal static delegate* unmanaged[Cdecl] PyEval_ReleaseThread { get; } + internal static delegate* unmanaged[Cdecl] PyEval_SaveThread { get; } + internal static delegate* unmanaged[Cdecl] PyEval_RestoreThread { get; } + internal static delegate* unmanaged[Cdecl] PyEval_GetBuiltins { get; } + internal static delegate* unmanaged[Cdecl] PyEval_GetGlobals { get; } + internal static delegate* unmanaged[Cdecl] PyEval_GetLocals { get; } + internal static delegate* unmanaged[Cdecl] Py_GetProgramName { get; } + internal static delegate* unmanaged[Cdecl] Py_SetProgramName { get; } + internal static delegate* unmanaged[Cdecl] Py_GetPythonHome { get; } + internal static delegate* unmanaged[Cdecl] Py_SetPythonHome { get; } + internal static delegate* unmanaged[Cdecl] Py_GetPath { get; } + internal static delegate* unmanaged[Cdecl] Py_SetPath { get; } + internal static delegate* unmanaged[Cdecl] Py_GetVersion { get; } + internal static delegate* unmanaged[Cdecl] Py_GetPlatform { get; } + internal static delegate* unmanaged[Cdecl] Py_GetCopyright { get; } + internal static delegate* unmanaged[Cdecl] Py_GetCompiler { get; } + internal static delegate* unmanaged[Cdecl] Py_GetBuildInfo { get; } + internal static delegate* unmanaged[Cdecl] PyRun_SimpleStringFlags { get; } + internal static delegate* unmanaged[Cdecl] PyRun_StringFlags { get; } + internal static delegate* unmanaged[Cdecl] PyEval_EvalCode { get; } + internal static delegate* unmanaged[Cdecl] Py_CompileStringObject { get; } + internal static delegate* unmanaged[Cdecl] PyImport_ExecCodeModule { get; } + internal static delegate* unmanaged[Cdecl] PyCFunction_NewEx { get; } + internal static delegate* unmanaged[Cdecl] PyCFunction_Call { get; } + internal static delegate* unmanaged[Cdecl] PyMethod_New { get; } + internal static delegate* unmanaged[Cdecl] PyObject_HasAttrString { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetAttrString { get; } + internal static delegate* unmanaged[Cdecl] PyObject_SetAttrString { get; } + internal static delegate* unmanaged[Cdecl] PyObject_HasAttr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetAttr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_SetAttr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PyObject_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PyObject_DelItem { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetIter { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Call { get; } + internal static delegate* unmanaged[Cdecl] PyObject_CallObject { get; } + internal static delegate* unmanaged[Cdecl] PyObject_RichCompareBool { get; } + internal static delegate* unmanaged[Cdecl] PyObject_IsInstance { get; } + internal static delegate* unmanaged[Cdecl] PyObject_IsSubclass { get; } + internal static delegate* unmanaged[Cdecl] PyCallable_Check { get; } + internal static delegate* unmanaged[Cdecl] PyObject_IsTrue { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Not { get; } + internal static delegate* unmanaged[Cdecl] _PyObject_Size { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Hash { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Repr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Str { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Unicode { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Dir { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetBuffer { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_Release { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_SizeFromFormat { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_IsContiguous { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_GetPointer { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_FromContiguous { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_ToContiguous { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_FillContiguousStrides { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_FillInfo { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Int { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Long { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Float { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Check { get; } + internal static delegate* unmanaged[Cdecl] PyInt_FromLong { get; } + internal static delegate* unmanaged[Cdecl] PyInt_AsLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromUnsignedLong32 { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromUnsignedLong64 { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromDouble { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromLongLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromUnsignedLongLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromString { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsUnsignedLong32 { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsUnsignedLong64 { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsLongLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsUnsignedLongLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromVoidPtr { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsVoidPtr { get; } + internal static delegate* unmanaged[Cdecl] PyFloat_FromDouble { get; } + internal static delegate* unmanaged[Cdecl] PyFloat_FromString { get; } + internal static delegate* unmanaged[Cdecl] PyFloat_AsDouble { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Add { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Subtract { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Multiply { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_TrueDivide { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_And { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Xor { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Or { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Lshift { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Rshift { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Power { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Remainder { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceAdd { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceSubtract { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceMultiply { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceTrueDivide { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceAnd { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceXor { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceOr { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceLshift { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceRshift { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlacePower { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceRemainder { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Negative { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Positive { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Invert { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Check { get; } + internal static delegate* unmanaged[Cdecl] PySequence_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PySequence_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PySequence_DelItem { get; } + internal static delegate* unmanaged[Cdecl] PySequence_GetSlice { get; } + internal static delegate* unmanaged[Cdecl] PySequence_SetSlice { get; } + internal static delegate* unmanaged[Cdecl] PySequence_DelSlice { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Size { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Contains { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Concat { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Repeat { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Index { get; } + internal static delegate* unmanaged[Cdecl] _PySequence_Count { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Tuple { get; } + internal static delegate* unmanaged[Cdecl] PySequence_List { get; } + internal static delegate* unmanaged[Cdecl] PyBytes_FromString { get; } + internal static delegate* unmanaged[Cdecl] _PyBytes_Size { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_FromStringAndSize { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_AsUTF8 { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_FromObject { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_FromEncodedObject { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_FromKindAndData { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_GetMax { get; } + internal static delegate* unmanaged[Cdecl] _PyUnicode_GetSize { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_AsUnicode { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_AsUTF16String { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_FromOrdinal { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_InternFromString { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_Compare { get; } + internal static delegate* unmanaged[Cdecl] PyDict_New { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Next { get; } + internal static delegate* unmanaged[Cdecl] PyDict_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PyDict_GetItemString { get; } + internal static delegate* unmanaged[Cdecl] PyDict_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PyDict_SetItemString { get; } + internal static delegate* unmanaged[Cdecl] PyDict_DelItem { get; } + internal static delegate* unmanaged[Cdecl] PyDict_DelItemString { get; } + internal static delegate* unmanaged[Cdecl] PyMapping_HasKey { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Keys { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Values { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Items { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Copy { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Update { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Clear { get; } + internal static delegate* unmanaged[Cdecl] _PyDict_Size { get; } + internal static delegate* unmanaged[Cdecl] PySet_New { get; } + internal static delegate* unmanaged[Cdecl] PySet_Add { get; } + internal static delegate* unmanaged[Cdecl] PySet_Contains { get; } + internal static delegate* unmanaged[Cdecl] PyList_New { get; } + internal static delegate* unmanaged[Cdecl] PyList_AsTuple { get; } + internal static delegate* unmanaged[Cdecl] PyList_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PyList_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PyList_Insert { get; } + internal static delegate* unmanaged[Cdecl] PyList_Append { get; } + internal static delegate* unmanaged[Cdecl] PyList_Reverse { get; } + internal static delegate* unmanaged[Cdecl] PyList_Sort { get; } + internal static delegate* unmanaged[Cdecl] PyList_GetSlice { get; } + internal static delegate* unmanaged[Cdecl] PyList_SetSlice { get; } + internal static delegate* unmanaged[Cdecl] PyList_Size { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_New { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_GetSlice { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_Size { get; } + internal static delegate* unmanaged[Cdecl] PyIter_Next { get; } + internal static delegate* unmanaged[Cdecl] PyModule_New { get; } + internal static delegate* unmanaged[Cdecl] PyModule_GetName { get; } + internal static delegate* unmanaged[Cdecl] PyModule_GetDict { get; } + internal static delegate* unmanaged[Cdecl] PyModule_GetFilename { get; } + internal static delegate* unmanaged[Cdecl] PyModule_Create2 { get; } + internal static delegate* unmanaged[Cdecl] PyImport_Import { get; } + internal static delegate* unmanaged[Cdecl] PyImport_ImportModule { get; } + internal static delegate* unmanaged[Cdecl] PyImport_ReloadModule { get; } + internal static delegate* unmanaged[Cdecl] PyImport_AddModule { get; } + internal static delegate* unmanaged[Cdecl] PyImport_GetModuleDict { get; } + internal static delegate* unmanaged[Cdecl] PySys_SetArgvEx { get; } + internal static delegate* unmanaged[Cdecl] PySys_GetObject { get; } + internal static delegate* unmanaged[Cdecl] PySys_SetObject { get; } + internal static delegate* unmanaged[Cdecl] PyType_Modified { get; } + internal static delegate* unmanaged[Cdecl] PyType_IsSubtype { get; } + internal static delegate* unmanaged[Cdecl] PyType_GenericNew { get; } + internal static delegate* unmanaged[Cdecl] PyType_GenericAlloc { get; } + internal static delegate* unmanaged[Cdecl] PyType_Ready { get; } + internal static delegate* unmanaged[Cdecl] _PyType_Lookup { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GenericGetAttr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GenericSetAttr { get; } + internal static delegate* unmanaged[Cdecl] _PyObject_GetDictPtr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GC_Del { get; } + internal static delegate* unmanaged[Cdecl] _PyObject_GC_Calloc { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GC_Track { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GC_UnTrack { get; } + internal static delegate* unmanaged[Cdecl] _PyObject_Dump { get; } + internal static delegate* unmanaged[Cdecl] PyObject_ClearWeakRefs { get; } + internal static delegate* unmanaged[Cdecl] PyMem_Malloc { get; } + internal static delegate* unmanaged[Cdecl] PyMem_Realloc { get; } + internal static delegate* unmanaged[Cdecl] PyMem_Free { get; } + internal static delegate* unmanaged[Cdecl] PyErr_SetString { get; } + internal static delegate* unmanaged[Cdecl] PyErr_SetObject { get; } + internal static delegate* unmanaged[Cdecl] PyErr_SetFromErrno { get; } + internal static delegate* unmanaged[Cdecl] PyErr_SetNone { get; } + internal static delegate* unmanaged[Cdecl] PyErr_ExceptionMatches { get; } + internal static delegate* unmanaged[Cdecl] PyErr_GivenExceptionMatches { get; } + internal static delegate* unmanaged[Cdecl] PyErr_NormalizeException { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Occurred { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Fetch { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Restore { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Clear { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Print { get; } + internal static delegate* unmanaged[Cdecl] PyCell_Get { get; } + internal static delegate* unmanaged[Cdecl] PyCell_Set { get; } + internal static delegate* unmanaged[Cdecl] PyGC_Collect { get; } + internal static delegate* unmanaged[Cdecl] PyCapsule_New { get; } + internal static delegate* unmanaged[Cdecl] PyCapsule_GetPointer { get; } + internal static delegate* unmanaged[Cdecl] PyCapsule_SetPointer { get; } + internal static delegate* unmanaged[Cdecl] PyMethod_Self { get; } + internal static delegate* unmanaged[Cdecl] PyMethod_Function { get; } + internal static delegate* unmanaged[Cdecl] Py_AddPendingCall { get; } + internal static delegate* unmanaged[Cdecl] Py_MakePendingCalls { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsUnsignedSize_t { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsSignedSize_t { get; } + internal static delegate* unmanaged[Cdecl] PyExplicitlyConvertToInt64 { get; } + internal static delegate* unmanaged[Cdecl] PyDict_GetItemWithError { get; } + internal static delegate* unmanaged[Cdecl] PyException_SetCause { get; } + internal static delegate* unmanaged[Cdecl] PyThreadState_SetAsyncExcLLP64 { get; } + internal static delegate* unmanaged[Cdecl] PyThreadState_SetAsyncExcLP64 { get; } } } @@ -2194,6 +2815,7 @@ public enum ShutdownMode Normal, Soft, Reload, + Extension, } diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs index 060573db4..0b3bf3017 100644 --- a/src/runtime/runtime_data.cs +++ b/src/runtime/runtime_data.cs @@ -39,7 +39,7 @@ static void ClearCLRData () BorrowedReference capsule = PySys_GetObject("clr_data"); if (!capsule.IsNull) { - IntPtr oldData = PyCapsule_GetPointer(capsule, null); + IntPtr oldData = PyCapsule_GetPointer(capsule, IntPtr.Zero); PyMem_Free(oldData); PyCapsule_SetPointer(capsule, IntPtr.Zero); } @@ -85,8 +85,9 @@ internal static void Stash() Marshal.Copy(data, 0, mem + IntPtr.Size, (int)ms.Length); ClearCLRData(); - NewReference capsule = PyCapsule_New(mem, null, IntPtr.Zero); - PySys_SetObject("clr_data", capsule.DangerousGetAddress()); + + NewReference capsule = PyCapsule_New(mem, IntPtr.Zero, IntPtr.Zero); + PySys_SetObject("clr_data", capsule); // Let the dictionary own the reference capsule.Dispose(); } @@ -110,7 +111,7 @@ private static void RestoreRuntimeDataImpl() { return; } - IntPtr mem = PyCapsule_GetPointer(capsule, null); + IntPtr mem = PyCapsule_GetPointer(capsule, IntPtr.Zero); int length = (int)Marshal.ReadIntPtr(mem); byte[] data = new byte[length]; Marshal.Copy(mem + IntPtr.Size, data, 0, length); @@ -120,8 +121,8 @@ private static void RestoreRuntimeDataImpl() var objs = RestoreRuntimeDataObjects(storage.GetStorage("objs")); RestoreRuntimeDataModules(storage.GetStorage("modules")); - var clsObjs = ClassManager.RestoreRuntimeData(storage.GetStorage("classes")); TypeManager.RestoreRuntimeData(storage.GetStorage("types")); + var clsObjs = ClassManager.RestoreRuntimeData(storage.GetStorage("classes")); ImportHook.RestoreRuntimeData(storage.GetStorage("import")); PyCLRMetaType = MetaType.RestoreRuntimeData(storage.GetStorage("meta")); @@ -143,7 +144,7 @@ public static bool HasStashData() public static void ClearStash() { - PySys_SetObject("clr_data", IntPtr.Zero); + PySys_SetObject("clr_data", default); } static bool CheckSerializable (object o) @@ -295,7 +296,9 @@ private static void RestoreRuntimeDataModules(RuntimeDataStorage storage) var pyMoudles = PyImport_GetModuleDict(); foreach (var item in modules) { - int res = PyDict_SetItem(pyMoudles, item.Key, item.Value); + var moduleName = new BorrowedReference(item.Key); + var module = new BorrowedReference(item.Value); + int res = PyDict_SetItem(pyMoudles, moduleName, module); PythonException.ThrowIfIsNotZero(res); XDecref(item.Key); XDecref(item.Value); diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs index 69acbcd31..295219675 100644 --- a/src/runtime/runtime_state.cs +++ b/src/runtime/runtime_state.cs @@ -19,20 +19,20 @@ public static void Save() throw new Exception("Runtime State set already"); } - IntPtr objs = IntPtr.Zero; + NewReference objs = default; if (ShouldRestoreObjects) { - objs = PySet_New(IntPtr.Zero); - foreach (var obj in PyGCGetObjects()) + objs = PySet_New(default); + foreach (var objRaw in PyGCGetObjects()) { - AddObjPtrToSet(objs, obj); + AddObjPtrToSet(objs, new BorrowedReference(objRaw)); } } - var modules = PySet_New(IntPtr.Zero); + var modules = PySet_New(default); foreach (var name in GetModuleNames()) { - int res = PySet_Add(modules, name); + int res = PySet_Add(modules, new BorrowedReference(name)); PythonException.ThrowIfIsNotZero(res); } @@ -46,10 +46,9 @@ public static void Save() head->gc.gc_refs = IntPtr.Zero; } { - var pyDummyGC = PyLong_FromVoidPtr(dummyGCHead); + using var pyDummyGC = PyLong_FromVoidPtr(dummyGCHead); int res = PySys_SetObject("dummy_gc", pyDummyGC); PythonException.ThrowIfIsNotZero(res); - XDecref(pyDummyGC); try { @@ -58,7 +57,7 @@ public static void Save() } finally { - XDecref(modules); + modules.Dispose(); } if (ShouldRestoreObjects) @@ -71,7 +70,7 @@ public static void Save() } finally { - XDecref(objs); + objs.Dispose(); } } } @@ -79,8 +78,8 @@ public static void Save() public static void Restore() { - var dummyGCAddr = PySys_GetObject("dummy_gc").DangerousGetAddress(); - if (dummyGCAddr == IntPtr.Zero) + var dummyGCAddr = PySys_GetObject("dummy_gc"); + if (dummyGCAddr.IsNull) { throw new InvalidOperationException("Runtime state have not set"); } @@ -97,9 +96,10 @@ private static void ResotreModules(IntPtr dummyGC) var intialModules = PySys_GetObject("initial_modules"); Debug.Assert(!intialModules.IsNull); var modules = PyImport_GetModuleDict(); - foreach (var name in GetModuleNames()) + foreach (var nameRaw in GetModuleNames()) { - if (PySet_Contains(intialModules.DangerousGetAddress(), name) == 1) + var name = new BorrowedReference(nameRaw); + if (PySet_Contains(intialModules, name) == 1) { continue; } @@ -122,21 +122,15 @@ private static void RestoreObjects(IntPtr dummyGC) { throw new Exception("To prevent crash by _PyObject_GC_UNTRACK in Python internal, UseDummyGC should be enabled when using ResotreObjects"); } - IntPtr intialObjs = PySys_GetObject("initial_objs").DangerousGetAddress(); - Debug.Assert(intialObjs != IntPtr.Zero); - foreach (var obj in PyGCGetObjects()) + BorrowedReference intialObjs = PySys_GetObject("initial_objs"); + Debug.Assert(@intialObjs.IsNull); + foreach (var objRaw in PyGCGetObjects()) { - var p = PyLong_FromVoidPtr(obj); - try - { - if (PySet_Contains(intialObjs, p) == 1) - { - continue; - } - } - finally + using var p = PyLong_FromVoidPtr(objRaw); + var obj = new BorrowedReference(objRaw); + if (PySet_Contains(intialObjs, p) == 1) { - XDecref(p); + continue; } Debug.Assert(_PyObject_GC_IS_TRACKED(obj), "A GC object must be tracked"); ExchangeGCChain(obj, dummyGC); @@ -162,34 +156,28 @@ public static IEnumerable PyGCGetObjects() public static IEnumerable GetModuleNames() { var modules = PyImport_GetModuleDict(); - var names = PyDict_Keys(modules); - var length = PyList_Size(new BorrowedReference(names)); + using var names = PyDict_Keys(modules); + var length = PyList_Size(names); + var result = new IntPtr[length]; for (int i = 0; i < length; i++) { - var name = PyList_GetItem(new BorrowedReference(names), i); - yield return name.DangerousGetAddress(); + result[i] = PyList_GetItem(names, i).DangerousGetAddress(); } - XDecref(names); + return result; } - private static void AddObjPtrToSet(IntPtr set, IntPtr obj) + private static void AddObjPtrToSet(BorrowedReference set, BorrowedReference obj) { - var p = PyLong_FromVoidPtr(obj); - XIncref(obj); - try - { - int res = PySet_Add(set, p); - PythonException.ThrowIfIsNotZero(res); - } - finally - { - XDecref(p); - } + IntPtr objRaw = obj.DangerousGetAddress(); + using var p = PyLong_FromVoidPtr(objRaw); + XIncref(objRaw); + int res = PySet_Add(set, p); + PythonException.ThrowIfIsNotZero(res); } /// /// Exchange gc to a dummy gc prevent nullptr error in _PyObject_GC_UnTrack macro. /// - private static void ExchangeGCChain(IntPtr obj, IntPtr gc) + private static void ExchangeGCChain(BorrowedReference obj, IntPtr gc) { var head = _Py_AS_GC(obj); if ((long)_PyGCHead_REFS(head) == _PyGC_REFS_UNTRACKED) diff --git a/src/runtime/slots/mp_length.cs b/src/runtime/slots/mp_length.cs index 42448a2e9..a13c7b6f8 100644 --- a/src/runtime/slots/mp_length.cs +++ b/src/runtime/slots/mp_length.cs @@ -26,7 +26,7 @@ public static MethodInfo Method } } - public static bool CanAssgin(Type clrType) + public static bool CanAssign(Type clrType) { if (typeof(ICollection).IsAssignableFrom(clrType)) { @@ -36,6 +36,10 @@ public static bool CanAssgin(Type clrType) { return true; } + if (clrType.IsInterface && clrType.IsGenericType && clrType.GetGenericTypeDefinition() == typeof(ICollection<>)) + { + return true; + } return false; } diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index c00247ca4..1975c2421 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using System.Diagnostics; using Python.Runtime.Slots; +using static Python.Runtime.PythonException; namespace Python.Runtime { @@ -20,9 +21,10 @@ internal class TypeManager internal static IntPtr subtype_clear; private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static; - private static Dictionary cache = new Dictionary(); + private static Dictionary cache = new Dictionary(); + private static readonly Dictionary _slotsHolders = new Dictionary(); - private static Dictionary _slotsImpls = new Dictionary(); + private static Dictionary _slotsImpls = new Dictionary(); // Slots which must be set private static readonly string[] _requiredSlots = new string[] @@ -43,10 +45,10 @@ internal static void Initialize() internal static void RemoveTypes() { - foreach (var tpHandle in cache.Values) + foreach (var entry in _slotsHolders) { - SlotsHolder holder; - if (_slotsHolders.TryGetValue(tpHandle, out holder)) + IntPtr tpHandle = entry.Key; + SlotsHolder holder = entry.Value; { // If refcount > 1, it needs to reset the managed slot, // otherwise it can dealloc without any trick. @@ -76,11 +78,17 @@ internal static void RestoreRuntimeData(RuntimeDataStorage storage) { Debug.Assert(cache == null || cache.Count == 0); storage.GetValue("slots", out _slotsImpls); - storage.GetValue("cache", out cache); - foreach (var entry in cache) + storage.GetValue>("cache", out var _cache); + foreach (var entry in _cache) { - Type type = entry.Key; + if (!entry.Key.Valid) + { + Runtime.XDecref(entry.Value); + continue; + } + Type type = entry.Key.Value;; IntPtr handle = entry.Value; + cache[type] = handle; SlotsHolder holder = CreateSolotsHolder(handle); InitializeSlots(handle, _slotsImpls[type], holder); // FIXME: mp_length_slot.CanAssgin(clrType) @@ -94,6 +102,7 @@ internal static void RestoreRuntimeData(RuntimeDataStorage storage) /// object. These Python type instances are used to implement internal /// descriptor and utility types like ModuleObject, PropertyObject, etc. /// + [Obsolete] internal static IntPtr GetTypeHandle(Type type) { // Note that these types are cached with a refcount of 1, so they @@ -109,6 +118,14 @@ internal static IntPtr GetTypeHandle(Type type) _slotsImpls.Add(type, type); return handle; } + /// + /// Given a managed Type derived from ExtensionType, get the handle to + /// a Python type object that delegates its implementation to the Type + /// object. These Python type instances are used to implement internal + /// descriptor and utility types like ModuleObject, PropertyObject, etc. + /// + internal static BorrowedReference GetTypeReference(Type type) + => new BorrowedReference(GetTypeHandle(type)); /// @@ -142,14 +159,14 @@ internal static IntPtr GetTypeHandle(ManagedType obj, Type type) /// internal static IntPtr CreateType(Type impl) { - IntPtr type = AllocateTypeObject(impl.Name); - int ob_size = ObjectOffset.Size(type); + IntPtr type = AllocateTypeObject(impl.Name, metatype: Runtime.PyTypeType); + int ob_size = ObjectOffset.Size; // Set tp_basicsize to the size of our managed instance objects. Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size); - var offset = (IntPtr)ObjectOffset.TypeDictOffset(type); - Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); + var offset = OriginalObjectOffsets.Size + ManagedDataOffsets.ob_dict; + Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)offset); SlotsHolder slotsHolder = CreateSolotsHolder(type); InitializeSlots(type, impl, slotsHolder); @@ -163,12 +180,21 @@ internal static IntPtr CreateType(Type impl) throw new PythonException(); } - IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); - IntPtr mod = Runtime.PyString_FromString("CLR"); - Runtime.PyDict_SetItemString(dict, "__module__", mod); - Runtime.XDecref(mod); + var dict = new BorrowedReference(Marshal.ReadIntPtr(type, TypeOffset.tp_dict)); + var mod = NewReference.DangerousFromPointer(Runtime.PyString_FromString("CLR")); + Runtime.PyDict_SetItem(dict, PyIdentifier.__module__, mod); + mod.Dispose(); InitMethods(type, impl); + unsafe + { + var typeEx = ClrMetaTypeEx.FromType(type); + typeEx->ClrHandleOffset = (IntPtr)OriginalObjectOffsets.Size + ManagedDataOffsets.ob_data; + } + + // The type has been modified after PyType_Ready has been called + // Refresh the type + Runtime.PyType_Modified(type); return type; } @@ -176,7 +202,7 @@ internal static IntPtr CreateType(Type impl) internal static IntPtr CreateType(ManagedType impl, Type clrType) { // Cleanup the type name to get rid of funny nested type names. - string name = "CLR." + clrType.FullName; + string name = $"clr.{clrType.FullName}"; int i = name.LastIndexOf('+'); if (i > -1) { @@ -189,17 +215,25 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) } IntPtr base_ = IntPtr.Zero; - int ob_size = ObjectOffset.Size(Runtime.PyTypeType); - + int baseOffset = OriginalObjectOffsets.Size; + int ob_size, tp_dictoffset, tp_weaklistoffset, magicOffset; // XXX Hack, use a different base class for System.Exception // Python 2.5+ allows new style class exceptions but they *must* // subclass BaseException (or better Exception). if (typeof(Exception).IsAssignableFrom(clrType)) { - ob_size = ObjectOffset.Size(Exceptions.Exception); + tp_dictoffset = ManagedExceptionOffset.ob_dict; + tp_weaklistoffset = 0; + ob_size = ManagedExceptionOffset.Size; + magicOffset = ManagedExceptionOffset.ob_data; + } + else + { + tp_dictoffset = baseOffset + ManagedDataOffsets.ob_dict; + tp_weaklistoffset = baseOffset + ManagedDataOffsets.ob_weaklist; + ob_size = baseOffset + ManagedDataOffsets.Size; + magicOffset = baseOffset + ManagedDataOffsets.ob_data; } - - int tp_dictoffset = ob_size + ManagedDataOffsets.ob_dict; if (clrType == typeof(Exception)) { @@ -211,7 +245,7 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) base_ = bc.pyHandle; } - IntPtr type = AllocateTypeObject(name); + IntPtr type = AllocateTypeObject(name, Runtime.PyCLRMetaType); Marshal.WriteIntPtr(type, TypeOffset.ob_type, Runtime.PyCLRMetaType); Runtime.XIncref(Runtime.PyCLRMetaType); @@ -219,13 +253,14 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size); Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset); + Marshal.WriteIntPtr(type, TypeOffset.tp_weaklistoffset, (IntPtr)tp_weaklistoffset); // we want to do this after the slot stuff above in case the class itself implements a slot method SlotsHolder slotsHolder = CreateSolotsHolder(type); InitializeSlots(type, impl.GetType(), slotsHolder); if (Marshal.ReadIntPtr(type, TypeOffset.mp_length) == IntPtr.Zero - && mp_length_slot.CanAssgin(clrType)) + && mp_length_slot.CanAssign(clrType)) { InitializeSlot(type, TypeOffset.mp_length, mp_length_slot.Method, slotsHolder); } @@ -272,6 +307,7 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) | TypeFlags.HaveGC; Util.WriteCLong(type, TypeOffset.tp_flags, flags); + OperatorMethod.FixupSlots(type, clrType); // Leverage followup initialization from the Python runtime. Note // that the type of the new type must PyType_Type at the time we // call this, else PyType_Ready will skip some slot initialization. @@ -281,18 +317,24 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) throw new PythonException(); } - IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); + var dict = new BorrowedReference(Marshal.ReadIntPtr(type, TypeOffset.tp_dict)); string mn = clrType.Namespace ?? ""; - IntPtr mod = Runtime.PyString_FromString(mn); - Runtime.PyDict_SetItemString(dict, "__module__", mod); - Runtime.XDecref(mod); + var mod = NewReference.DangerousFromPointer(Runtime.PyString_FromString(mn)); + Runtime.PyDict_SetItem(dict, PyIdentifier.__module__, mod); + mod.Dispose(); // Hide the gchandle of the implementation in a magic type slot. GCHandle gc = impl.AllocGCHandle(); - Marshal.WriteIntPtr(type, TypeOffset.magic(), (IntPtr)gc); + unsafe + { + var typePtr = ClrMetaTypeEx.FromType(type); + typePtr->ClrHandle = (IntPtr)gc; + typePtr->ClrHandleOffset = (IntPtr)magicOffset; + } // Set the handle attributes on the implementing instance. - impl.tpHandle = type; + impl.tpHandle = Runtime.PyCLRMetaType; + Runtime.XIncref(type); impl.pyHandle = type; //DebugUtil.DumpType(type); @@ -302,6 +344,7 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict) { + var dictRef = new BorrowedReference(py_dict); // Utility to create a subtype of a managed type with the ability for the // a python subtype able to override the managed implementation string name = Runtime.GetManagedString(py_name); @@ -311,40 +354,29 @@ internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr object assembly = null; object namespaceStr = null; - var disposeList = new List(); - try + using (var assemblyKey = new PyString("__assembly__")) { - var assemblyKey = new PyObject(Converter.ToPython("__assembly__", typeof(string))); - disposeList.Add(assemblyKey); - if (0 != Runtime.PyMapping_HasKey(py_dict, assemblyKey.Handle)) + var assemblyPtr = Runtime.PyDict_GetItemWithError(dictRef, assemblyKey.Reference); + if (assemblyPtr.IsNull) { - 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)) - { - throw new InvalidCastException("Couldn't convert __assembly__ value to string"); - } + if (Exceptions.ErrorOccurred()) return IntPtr.Zero; + } + else if (!Converter.ToManagedValue(assemblyPtr, typeof(string), out assembly, true)) + { + return Exceptions.RaiseTypeError("Couldn't convert __assembly__ value to string"); } - var namespaceKey = new PyObject(Converter.ToPythonImplicit("__namespace__")); - disposeList.Add(namespaceKey); - if (0 != Runtime.PyMapping_HasKey(py_dict, namespaceKey.Handle)) + using (var namespaceKey = new PyString("__namespace__")) { - 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)) + var pyNamespace = Runtime.PyDict_GetItemWithError(dictRef, namespaceKey.Reference); + if (pyNamespace.IsNull) { - throw new InvalidCastException("Couldn't convert __namespace__ value to string"); + if (Exceptions.ErrorOccurred()) return IntPtr.Zero; + } + else if (!Converter.ToManagedValue(pyNamespace, typeof(string), out namespaceStr, true)) + { + return Exceptions.RaiseTypeError("Couldn't convert __namespace__ value to string"); } - } - } - finally - { - foreach (PyObject o in disposeList) - { - o.Dispose(); } } @@ -358,7 +390,7 @@ internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr try { Type subType = ClassDerivedObject.CreateDerivedType(name, - baseClass.type, + baseClass.type.Value, py_dict, (string)namespaceStr, (string)assembly); @@ -369,15 +401,15 @@ internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr // by default the class dict will have all the C# methods in it, but as this is a // derived class we want the python overrides in there instead if they exist. - IntPtr cls_dict = Marshal.ReadIntPtr(py_type, TypeOffset.tp_dict); - Runtime.PyDict_Update(cls_dict, py_dict); + var cls_dict = new BorrowedReference(Marshal.ReadIntPtr(py_type, TypeOffset.tp_dict)); + ThrowIfIsNotZero(Runtime.PyDict_Update(cls_dict, new BorrowedReference(py_dict))); Runtime.XIncref(py_type); // Update the __classcell__ if it exists - var cell = new BorrowedReference(Runtime.PyDict_GetItemString(cls_dict, "__classcell__")); + BorrowedReference cell = Runtime.PyDict_GetItemString(cls_dict, "__classcell__"); if (!cell.IsNull) { - Runtime.PyCell_Set(cell, py_type); - Runtime.PyDict_DelItemString(cls_dict, "__classcell__"); + ThrowIfIsNotZero(Runtime.PyCell_Set(cell, py_type)); + ThrowIfIsNotZero(Runtime.PyDict_DelItemString(cls_dict, "__classcell__")); } return py_type; @@ -436,11 +468,19 @@ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder) // the standard type slots, and has to subclass PyType_Type for // certain functions in the C runtime to work correctly with it. - IntPtr type = AllocateTypeObject("CLR Metatype"); IntPtr py_type = Runtime.PyTypeType; + var heapTypeSize = (int)Marshal.ReadIntPtr(py_type, TypeOffset.tp_basicsize); + Debug.Assert(heapTypeSize == TypeOffset.Size); + int metaSize = heapTypeSize + Marshal.SizeOf(typeof(ClrMetaTypeEx)); - Marshal.WriteIntPtr(type, TypeOffset.tp_base, py_type); + IntPtr type = Runtime._PyObject_GC_Calloc(new IntPtr(metaSize)); Runtime.XIncref(py_type); + Marshal.WriteIntPtr(type, TypeOffset.tp_base, py_type); + Marshal.WriteIntPtr(type, TypeOffset.ob_refcnt, (IntPtr)1); + Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)metaSize); + Marshal.WriteIntPtr(type, heapTypeSize, (IntPtr)(heapTypeSize + IntPtr.Size)); + + SetupHeapType(type, "CLR Metatype"); const int flags = TypeFlags.Default | TypeFlags.Managed @@ -464,6 +504,9 @@ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder) IntPtr mod = Runtime.PyString_FromString("CLR"); Runtime.PyDict_SetItemString(dict, "__module__", mod); + // The type has been modified after PyType_Ready has been called + // Refresh the type + Runtime.PyType_Modified(type); //DebugUtil.DumpType(type); return type; @@ -513,7 +556,7 @@ private static IntPtr AddCustomMetaMethod(string name, IntPtr type, IntPtr mdef, IntPtr mdefAddr = mdef; slotsHolder.AddDealloctor(() => { - IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); + var tp_dict = new BorrowedReference(Marshal.ReadIntPtr(type, TypeOffset.tp_dict)); if (Runtime.PyDict_DelItemString(tp_dict, name) != 0) { Runtime.PyErr_Print(); @@ -531,7 +574,7 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl) // Utility to create a subtype of a std Python type, but with // a managed type able to override implementation - IntPtr type = AllocateTypeObject(name); + IntPtr type = AllocateTypeObject(name, metatype: Runtime.PyTypeType); //Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)obSize); //Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); @@ -564,44 +607,22 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl) IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); IntPtr mod = Runtime.PyString_FromString("CLR"); - Runtime.PyDict_SetItemString(tp_dict, "__module__", mod); + Runtime.PyDict_SetItem(tp_dict, PyIdentifier.__module__, mod); + + // The type has been modified after PyType_Ready has been called + // Refresh the type + Runtime.PyType_Modified(type); return type; } - /// /// Utility method to allocate a type object & do basic initialization. /// - internal static IntPtr AllocateTypeObject(string name) + internal static IntPtr AllocateTypeObject(string name, IntPtr metatype) { - IntPtr type = Runtime.PyType_GenericAlloc(Runtime.PyTypeType, 0); - // Clr type would not use __slots__, - // and the PyMemberDef after PyHeapTypeObject will have other uses(e.g. type handle), - // thus set the ob_size to 0 for avoiding slots iterations. - Marshal.WriteIntPtr(type, TypeOffset.ob_size, IntPtr.Zero); - - // Cheat a little: we'll set tp_name to the internal char * of - // the Python version of the type name - otherwise we'd have to - // allocate the tp_name and would have no way to free it. - IntPtr temp = Runtime.PyUnicode_FromString(name); - IntPtr raw = Runtime.PyUnicode_AsUTF8(temp); - Marshal.WriteIntPtr(type, TypeOffset.tp_name, raw); - Marshal.WriteIntPtr(type, TypeOffset.name, temp); - - Runtime.XIncref(temp); - Marshal.WriteIntPtr(type, TypeOffset.qualname, temp); - temp = type + TypeOffset.nb_add; - Marshal.WriteIntPtr(type, TypeOffset.tp_as_number, temp); - - temp = type + TypeOffset.sq_length; - Marshal.WriteIntPtr(type, TypeOffset.tp_as_sequence, temp); - - temp = type + TypeOffset.mp_length; - Marshal.WriteIntPtr(type, TypeOffset.tp_as_mapping, temp); - - temp = type + TypeOffset.bf_getbuffer; - Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, temp); + IntPtr type = Runtime.PyType_GenericAlloc(Runtime.PyCLRMetaType, 0); + SetupHeapType(type, name); return type; } @@ -623,8 +644,9 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo foreach (MethodInfo method in methods) { string name = method.Name; - if (!name.StartsWith("tp_") && !SlotTypes.IsSlotName(name)) + if (!name.StartsWith("tp_") && !TypeOffset.IsSupportedSlotName(name)) { + Debug.Assert(!name.Contains("_") || name.StartsWith("_") || method.IsSpecialName); continue; } @@ -675,9 +697,7 @@ static void InitializeSlot(IntPtr type, IntPtr slot, string name, bool canOverri static void InitializeSlot(IntPtr type, ThunkInfo thunk, string name, SlotsHolder slotsHolder = null, bool canOverride = true) { - Type typeOffset = typeof(TypeOffset); - FieldInfo fi = typeOffset.GetField(name); - var offset = (int)fi.GetValue(typeOffset); + int offset = ManagedDataOffsets.GetSlotOffset(name); if (!canOverride && Marshal.ReadIntPtr(type, offset) != IntPtr.Zero) { @@ -759,6 +779,36 @@ private static SlotsHolder CreateSolotsHolder(IntPtr type) _slotsHolders.Add(type, holder); return holder; } + + private static void SetupHeapType(IntPtr type, string name) + { + // Clr type would not use __slots__, + // and the PyMemberDef after PyHeapTypeObject will have other uses(e.g. type handle), + // thus set the ob_size to 0 for avoiding slots iterations. + Marshal.WriteIntPtr(type, TypeOffset.ob_size, IntPtr.Zero); + + // Cheat a little: we'll set tp_name to the internal char * of + // the Python version of the type name - otherwise we'd have to + // allocate the tp_name and would have no way to free it. + IntPtr temp = Runtime.PyUnicode_FromString(name); + IntPtr raw = Runtime.PyUnicode_AsUTF8(temp); + Marshal.WriteIntPtr(type, TypeOffset.tp_name, raw); + Marshal.WriteIntPtr(type, TypeOffset.name, temp); + + Runtime.XIncref(temp); + Marshal.WriteIntPtr(type, TypeOffset.qualname, temp); + temp = type + TypeOffset.nb_add; + Marshal.WriteIntPtr(type, TypeOffset.tp_as_number, temp); + + temp = type + TypeOffset.sq_length; + Marshal.WriteIntPtr(type, TypeOffset.tp_as_sequence, temp); + + temp = type + TypeOffset.mp_length; + Marshal.WriteIntPtr(type, TypeOffset.tp_as_mapping, temp); + + temp = type + TypeOffset.bf_getbuffer; + Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, temp); + } } @@ -767,10 +817,10 @@ class SlotsHolder public delegate void Resetor(IntPtr type, int offset); private readonly IntPtr _type; - private Dictionary _slots = new Dictionary(); - private List _keepalive = new List(); - private Dictionary _customResetors = new Dictionary(); - private List _deallocators = new List(); + private Dictionary _slots; + private List _keepalive; + private Dictionary _customResetors; + private List _deallocators; private bool _alreadyReset = false; /// @@ -784,21 +834,25 @@ public SlotsHolder(IntPtr type) public void Set(int offset, ThunkInfo thunk) { + if (_slots == null) _slots = new Dictionary(); _slots[offset] = thunk; } public void Set(int offset, Resetor resetor) { + if (_customResetors == null) _customResetors = new Dictionary(); _customResetors[offset] = resetor; } public void AddDealloctor(Action deallocate) { + if (_deallocators == null) _deallocators = new List(); _deallocators.Add(deallocate); } public void KeeapAlive(ThunkInfo thunk) { + if (_keepalive == null) _keepalive = new List(); _keepalive.Add(thunk); } @@ -809,47 +863,15 @@ public void ResetSlots() return; } _alreadyReset = true; -#if DEBUG - IntPtr tp_name = Marshal.ReadIntPtr(_type, TypeOffset.tp_name); - string typeName = Marshal.PtrToStringAnsi(tp_name); -#endif - foreach (var offset in _slots.Keys) - { - IntPtr ptr = GetDefaultSlot(offset); -#if DEBUG - //DebugUtil.Print($"Set slot<{TypeOffsetHelper.GetSlotNameByOffset(offset)}> to 0x{ptr.ToString("X")} at {typeName}<0x{_type}>"); -#endif - Marshal.WriteIntPtr(_type, offset, ptr); - } + ResetDefaultSlots(); + InvokeDeallocActions(); + InvokeCustomResetors(); - foreach (var action in _deallocators) - { - action(); - } - - foreach (var pair in _customResetors) + if (_keepalive != null) { - int offset = pair.Key; - var resetor = pair.Value; - resetor?.Invoke(_type, offset); - } - - _customResetors.Clear(); - _slots.Clear(); - _keepalive.Clear(); - _deallocators.Clear(); - - // Custom reset - IntPtr handlePtr = Marshal.ReadIntPtr(_type, TypeOffset.magic()); - if (handlePtr != IntPtr.Zero) - { - GCHandle handle = GCHandle.FromIntPtr(handlePtr); - if (handle.IsAllocated) - { - handle.Free(); - } - Marshal.WriteIntPtr(_type, TypeOffset.magic(), IntPtr.Zero); + _keepalive.Clear(); } + ReleaseGCHandle(); } public static IntPtr GetDefaultSlot(int offset) @@ -894,62 +916,89 @@ public static IntPtr GetDefaultSlot(int offset) return Marshal.ReadIntPtr(Runtime.PyTypeType, offset); } - } - - static class SlotHelper - { - public static IntPtr CreateObjectType() + private void InvokeCustomResetors() { - IntPtr globals = Runtime.PyDict_New(); - if (Runtime.PyDict_SetItemString(globals, "__builtins__", Runtime.PyEval_GetBuiltins()) != 0) + if (_customResetors == null) return; + foreach (var pair in _customResetors) { - Runtime.XDecref(globals); - throw new PythonException(); + int offset = pair.Key; + var resetor = pair.Value; + resetor?.Invoke(_type, offset); } - const string code = "class A(object): pass"; - var resRef = Runtime.PyRun_String(code, RunFlagType.File, globals, globals); - IntPtr res = resRef.DangerousGetAddress(); - if (res == IntPtr.Zero) + _customResetors.Clear(); + } + + private void InvokeDeallocActions() + { + if (_deallocators == null) return; + foreach (var action in _deallocators) { - try - { - throw new PythonException(); - } - finally - { - Runtime.XDecref(globals); - } + action(); } - resRef.Dispose(); - IntPtr A = Runtime.PyDict_GetItemString(globals, "A"); - Debug.Assert(A != IntPtr.Zero); - Runtime.XIncref(A); - Runtime.XDecref(globals); - return A; + _deallocators.Clear(); } - } - - static partial class SlotTypes - { - private static Dictionary _nameMap = new Dictionary(); + private void ResetDefaultSlots() + { + if (_slots == null) return; +#if DEBUG + IntPtr tp_name = Marshal.ReadIntPtr(_type, TypeOffset.tp_name); + string typeName = Marshal.PtrToStringAnsi(tp_name); +#endif + foreach (var offset in _slots.Keys) + { + IntPtr ptr = GetDefaultSlot(offset); +#if DEBUG + //DebugUtil.Print($"Set slot<{TypeOffsetHelper.GetSlotNameByOffset(offset)}> to 0x{ptr.ToString("X")} at {typeName}<0x{_type}>"); +#endif + Marshal.WriteIntPtr(_type, offset, ptr); + } + _slots.Clear(); + } - static SlotTypes() + private void ReleaseGCHandle() { - foreach (var type in Types) + if (!ManagedType.IsManagedType(_type)) + { + return; + } + int offset = ObjectOffset.magic(Runtime.PyObject_TYPE(_type)); + IntPtr handlePtr = Marshal.ReadIntPtr(_type, offset); + if (handlePtr != IntPtr.Zero) { - FieldInfo[] fields = type.GetFields(); - foreach (var fi in fields) + GCHandle handle = GCHandle.FromIntPtr(handlePtr); + if (handle.IsAllocated) { - _nameMap[fi.Name] = type; + handle.Free(); } + Marshal.WriteIntPtr(_type, offset, IntPtr.Zero); } } + } + - public static bool IsSlotName(string name) + static class SlotHelper + { + public static IntPtr CreateObjectType() { - return _nameMap.ContainsKey(name); + using var globals = NewReference.DangerousFromPointer(Runtime.PyDict_New()); + if (Runtime.PyDict_SetItemString(globals, "__builtins__", Runtime.PyEval_GetBuiltins()) != 0) + { + globals.Dispose(); + throw new PythonException(); + } + const string code = "class A(object): pass"; + using var resRef = Runtime.PyRun_String(code, RunFlagType.File, globals, globals); + if (resRef.IsNull()) + { + globals.Dispose(); + throw new PythonException(); + } + resRef.Dispose(); + BorrowedReference A = Runtime.PyDict_GetItemString(globals, "A"); + Debug.Assert(!A.IsNull); + return new NewReference(A).DangerousMoveToPointer(); } } } diff --git a/src/testing/CodecTest.cs b/src/testing/CodecTest.cs new file mode 100644 index 000000000..74f77531b --- /dev/null +++ b/src/testing/CodecTest.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Python.Test +{ + public class ListMember + { + public ListMember(int value, string name) + { + Value = value; + Name = name; + } + + public int Value { get; set; } + public string Name { get; set; } + } + + public class ListConversionTester + { + public int GetLength(IEnumerable o) + { + return o.Count(); + } + public int GetLength(ICollection o) + { + return o.Count; + } + public int GetLength(IList o) + { + return o.Count; + } + public int GetLength2(IEnumerable o) + { + return o.Count(); + } + public int GetLength2(ICollection o) + { + return o.Count; + } + public int GetLength2(IList o) + { + return o.Count; + } + } +} diff --git a/src/testing/Python.Test.15.csproj b/src/testing/Python.Test.15.csproj deleted file mode 100644 index 0e19adf91..000000000 --- a/src/testing/Python.Test.15.csproj +++ /dev/null @@ -1,86 +0,0 @@ - - - - net40;netstandard2.0 - x64;x86 - DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3 - Python.Test - Python.Test - Python.Test - 2.5.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 63526c060..f7bc10bb4 100644 --- a/src/testing/Python.Test.csproj +++ b/src/testing/Python.Test.csproj @@ -1,117 +1,10 @@ - - + - Debug - AnyCPU - {6F401A34-273B-450F-9A4C-13550BE0767B} - Library - Python.Test - Python.Test - bin\Python.Test.xml - bin\ - v4.0 - - 1591,0067 - ..\..\ - $(SolutionDir)\bin\ - 6 - false - ..\pythonnet.snk - prompt + netstandard2.0;net5.0 + true + true - - x86 - - - x64 - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - true - DEBUG;TRACE - full - - - - - true - pdbonly - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - {097B4AC0-74E9-4C58-BCF8-C69746EC8271} - Python.Runtime - - - - - $(TargetPath) - $(TargetDir)$(TargetName).pdb - - - - - diff --git a/src/testing/arraytest.cs b/src/testing/arraytest.cs index 946684962..a3c94e019 100644 --- a/src/testing/arraytest.cs +++ b/src/testing/arraytest.cs @@ -314,4 +314,16 @@ public static Spam[][] EchoRangeAA(Spam[][] items) return items; } } + + public struct Point + { + public Point(float x, float y) + { + X = x; + Y = y; + } + + public float X { get; set; } + public float Y { get; set; } + } } diff --git a/src/testing/constructortests.cs b/src/testing/constructortests.cs index 8800c94a1..4dc7f04d8 100644 --- a/src/testing/constructortests.cs +++ b/src/testing/constructortests.cs @@ -48,4 +48,22 @@ public SubclassConstructorTest(Exception v) value = v; } } + + public class MultipleConstructorsTest + { + public string value; + public Type[] type; + + public MultipleConstructorsTest() + { + value = ""; + type = new Type[1] { null }; + } + + public MultipleConstructorsTest(string s, params Type[] tp) + { + value = s; + type = tp; + } + } } diff --git a/src/testing/delegatetest.cs b/src/testing/delegatetest.cs index e2df9475f..ee66bdad7 100644 --- a/src/testing/delegatetest.cs +++ b/src/testing/delegatetest.cs @@ -13,6 +13,12 @@ namespace Python.Test public delegate bool BoolDelegate(); + public delegate void OutStringDelegate(out string value); + public delegate void RefStringDelegate(ref string value); + public delegate void OutIntDelegate(out int value); + public delegate void RefIntDelegate(ref int value); + public delegate void RefIntRefStringDelegate(ref int intValue, ref string stringValue); + public delegate int IntRefIntRefStringDelegate(ref int intValue, ref string stringValue); public class DelegateTest { @@ -27,6 +33,8 @@ public class DelegateTest public StringDelegate stringDelegate; public ObjectDelegate objectDelegate; public BoolDelegate boolDelegate; + public OutStringDelegate outStringDelegate; + public RefStringDelegate refStringDelegate; public DelegateTest() { @@ -42,6 +50,11 @@ public static string StaticSayHello() return "hello"; } + public void OutHello(out string value) + { + value = "hello"; + } + public string CallStringDelegate(StringDelegate d) { return d(); @@ -56,5 +69,35 @@ public bool CallBoolDelegate(BoolDelegate d) { return d(); } + + public void CallOutIntDelegate(OutIntDelegate d, out int value) + { + d(out value); + } + + public void CallRefIntDelegate(RefIntDelegate d, ref int value) + { + d(ref value); + } + + public void CallOutStringDelegate(OutStringDelegate d, out string value) + { + d(out value); + } + + public void CallRefStringDelegate(RefStringDelegate d, ref string value) + { + d(ref value); + } + + public void CallRefIntRefStringDelegate(RefIntRefStringDelegate d, ref int intValue, ref string stringValue) + { + d(ref intValue, ref stringValue); + } + + public int CallIntRefIntRefStringDelegate(IntRefIntRefStringDelegate d, ref int intValue, ref string stringValue) + { + return d(ref intValue, ref stringValue); + } } } diff --git a/src/testing/eventtest.cs b/src/testing/eventtest.cs index 4c701d488..c9573f71a 100644 --- a/src/testing/eventtest.cs +++ b/src/testing/eventtest.cs @@ -7,7 +7,7 @@ namespace Python.Test /// public delegate void EventHandlerTest(object sender, EventArgsTest e); - + #pragma warning disable 67 // Unused events, these are only accessed from Python public class EventTest { public static event EventHandlerTest PublicStaticEvent; @@ -26,6 +26,10 @@ public class EventTest private event EventHandlerTest PrivateEvent; + public event OutStringDelegate OutStringEvent; + public event OutIntDelegate OutIntEvent; + public event RefStringDelegate RefStringEvent; + public event RefIntDelegate RefIntEvent; public static int s_value; public int value; @@ -76,6 +80,27 @@ protected static void OnProtectedStaticEvent(EventArgsTest e) } } + public void OnRefStringEvent(ref string data) + { + RefStringEvent?.Invoke(ref data); + } + + public void OnRefIntEvent(ref int data) + { + RefIntEvent?.Invoke(ref data); + } + + public void OnOutStringEvent(out string data) + { + data = default; + OutStringEvent?.Invoke(out data); + } + + public void OnOutIntEvent(out int data) + { + data = default; + OutIntEvent?.Invoke(out data); + } public void GenericHandler(object sender, EventArgsTest e) { @@ -87,6 +112,26 @@ public static void StaticHandler(object sender, EventArgsTest e) s_value = e.value; } + public void OutStringHandler(out string data) + { + data = value.ToString(); + } + + public void OutIntHandler(out int data) + { + data = value; + } + + public void RefStringHandler(ref string data) + { + data += "!"; + } + + public void RefIntHandler(ref int data) + { + data++; + } + public static void ShutUpCompiler() { // Quiet compiler warnings. @@ -100,6 +145,7 @@ public static void ShutUpCompiler() e.PrivateEvent += f; } } + #pragma warning restore 67 public class EventArgsTest : EventArgs diff --git a/src/testing/generictest.cs b/src/testing/generictest.cs index 1e9c71ac6..238435811 100644 --- a/src/testing/generictest.cs +++ b/src/testing/generictest.cs @@ -35,6 +35,9 @@ public DerivedFromOpenGeneric(int arg1, V arg2, W arg3) : base(arg1, arg2) } } + public class GenericTypeWithConstraint + where T: struct + { } public class GenericNameTest1 { @@ -125,4 +128,12 @@ public static string Overloaded(int arg1, int arg2, string arg3) return arg3; } } + + public class GenericArrayConversionTest + { + public static T[] EchoRange(T[] items) + { + return items; + } + } } diff --git a/src/testing/indexertest.cs b/src/testing/indexertest.cs index 78bb99a7c..08e6ad053 100644 --- a/src/testing/indexertest.cs +++ b/src/testing/indexertest.cs @@ -427,7 +427,8 @@ public interface IIndexer public interface IInheritedIndexer : IIndexer { } - public class InterfaceInheritedIndexerTest :IndexerBase, IInheritedIndexer { + public class InterfaceInheritedIndexerTest : IndexerBase, IInheritedIndexer + { private System.Collections.Generic.IDictionary d = new System.Collections.Generic.Dictionary(); public string this[int index] @@ -436,4 +437,13 @@ public string this[int index] set { t[index] = value; } } } + + public class PublicInheritedOverloadedIndexer : PublicIndexerTest + { + public string this[string index] + { + get { return GetValue(index); } + set { t[index] = value; } + } + } } diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index 30e42ac9f..abdc5f4d6 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -85,7 +85,7 @@ public Type[] TestNullArrayConversion(Type[] v) public static string[] TestStringParamsArg(params string[] args) { - return args.Concat(new []{"tail"}).ToArray(); + return args.Concat(new[] { "tail" }).ToArray(); } public static object[] TestObjectParamsArg(params object[] args) @@ -654,37 +654,76 @@ public static string Casesensitive() return "Casesensitive"; } - public static string DefaultParams(int a=0, int b=0, int c=0, int d=0) + public static string DefaultParams(int a = 0, int b = 0, int c = 0, int d = 0) { return string.Format("{0}{1}{2}{3}", a, b, c, d); } - public static string OptionalParams([Optional]int a, [Optional]int b, [Optional]int c, [Optional] int d) + public static string OptionalParams([Optional] int a, [Optional] int b, [Optional] int c, [Optional] int d) { return string.Format("{0}{1}{2}{3}", a, b, c, d); } - public static bool OptionalParams_TestMissing([Optional]object a) + public static bool OptionalParams_TestMissing([Optional] object a) { return a == Type.Missing; } - public static bool OptionalParams_TestReferenceType([Optional]string a) + public static bool OptionalParams_TestReferenceType([Optional] string a) { return a == null; } - public static string OptionalAndDefaultParams([Optional]int a, [Optional]int b, int c=0, int d=0) + public static string OptionalAndDefaultParams([Optional] int a, [Optional] int b, int c = 0, int d = 0) { return string.Format("{0}{1}{2}{3}", a, b, c, d); } - public static string OptionalAndDefaultParams2([Optional]int a, [Optional]int b, [Optional, DefaultParameterValue(1)]int c, int d = 2) + public static string OptionalAndDefaultParams2([Optional] int a, [Optional] int b, [Optional, DefaultParameterValue(1)] int c, int d = 2) { return string.Format("{0}{1}{2}{3}", a, b, c, d); } - + public static string DefaultParamsWithOverloading(int a = 2, int b = 1) + { + return $"{a}{b}"; + } + + public static string DefaultParamsWithOverloading(string a = "a", string b = "b") + { + return $"{a}{b}X"; + } + + public static string DefaultParamsWithOverloading(int a = 0, int b = 1, int c = 2) + { + return $"{a}{b}{c}XX"; + } + + public static string DefaultParamsWithOverloading(int a = 5, int b = 6, int c = 7, int d = 8) + { + return $"{a}{b}{c}{d}XXX"; + } + + public static string ParamsArrayOverloaded(int i = 1) + { + return "without params-array"; + } + + public static string ParamsArrayOverloaded(int i, params int[] paramsArray) + { + return "with params-array"; + } + + public static void EncodingTestÅngström() + { + } + + // This method can never be invoked from Python, but we want to test that any attempt fails gracefully instead of crashing. + unsafe + public static void PointerArray(int*[] array) + { + + } } diff --git a/src/testing/threadtest.cs b/src/testing/threadtest.cs index 2825c3fef..9c76929b2 100644 --- a/src/testing/threadtest.cs +++ b/src/testing/threadtest.cs @@ -11,8 +11,8 @@ public class ThreadTest private static PyObject module; private static string testmod = - "import CLR\n" + - "from CLR.Python.Test import ThreadTest\n" + + "import clr\n" + + "from Python.Test import ThreadTest\n" + "\n" + "def echostring(value):\n" + " return value\n" + diff --git a/src/tests/conftest.py b/src/tests/conftest.py deleted file mode 100644 index a93326c51..000000000 --- a/src/tests/conftest.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# TODO: move tests one out of src to project root. -# TODO: travis has numpy on their workers. Maybe add tests? - -"""Helpers for testing.""" - -import ctypes -import os -import sys -import sysconfig - -import pytest -import clr - -# Add path for `Python.Test` -cwd = os.path.dirname(__file__) -fixtures_path = os.path.join(cwd, "fixtures") -sys.path.append(fixtures_path) - -# Add References for tests -clr.AddReference("Python.Test") -clr.AddReference("System.Collections") -clr.AddReference("System.Data") - - -def pytest_report_header(config): - """Generate extra report headers""" - # FIXME: https://github.com/pytest-dev/pytest/issues/2257 - is_64bits = sys.maxsize > 2**32 - arch = "x64" if is_64bits else "x86" - ucs = ctypes.sizeof(ctypes.c_wchar) - libdir = sysconfig.get_config_var("LIBDIR") - shared = bool(sysconfig.get_config_var("Py_ENABLE_SHARED")) - - header = ("Arch: {arch}, UCS: {ucs}, LIBDIR: {libdir}, " - "Py_ENABLE_SHARED: {shared}".format(**locals())) - return header - - -@pytest.fixture() -def filepath(): - """Returns full filepath for file in `fixtures` directory.""" - - def make_filepath(filename): - # http://stackoverflow.com/questions/18011902/parameter-to-a-fixture - return os.path.join(fixtures_path, filename) - - return make_filepath diff --git a/src/tests/fixtures/netstandard2.0/.gitkeep b/src/tests/fixtures/netstandard2.0/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/tests/test_codec.py b/src/tests/test_codec.py new file mode 100644 index 000000000..9fdbfbbf5 --- /dev/null +++ b/src/tests/test_codec.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +"""Test conversions using codecs from client python code""" +import clr +import System +import pytest +import Python.Runtime +from Python.Test import ListConversionTester, ListMember + +class int_iterable(): + def __init__(self): + self.counter = 0 + def __iter__(self): + return self + def __next__(self): + if self.counter == 3: + raise StopIteration + self.counter = self.counter + 1 + return self.counter + +class obj_iterable(): + def __init__(self): + self.counter = 0 + def __iter__(self): + return self + def __next__(self): + if self.counter == 3: + raise StopIteration + self.counter = self.counter + 1 + return ListMember(self.counter, "Number " + str(self.counter)) + +def test_iterable(): + """Test that a python iterable can be passed into a function that takes an IEnumerable""" + + #Python.Runtime.Codecs.ListDecoder.Register() + #Python.Runtime.Codecs.SequenceDecoder.Register() + Python.Runtime.Codecs.IterableDecoder.Register() + ob = ListConversionTester() + + iterable = int_iterable() + assert 3 == ob.GetLength(iterable) + + iterable2 = obj_iterable() + assert 3 == ob.GetLength2(iterable2) + + Python.Runtime.PyObjectConversions.Reset() + +def test_sequence(): + Python.Runtime.Codecs.SequenceDecoder.Register() + ob = ListConversionTester() + + tup = (1,2,3) + assert 3 == ob.GetLength(tup) + + tup2 = (ListMember(1, "one"), ListMember(2, "two"), ListMember(3, "three")) + assert 3 == ob.GetLength(tup2) + + Python.Runtime.PyObjectConversions.Reset() + +def test_list(): + Python.Runtime.Codecs.SequenceDecoder.Register() + ob = ListConversionTester() + + l = [1,2,3] + assert 3 == ob.GetLength(l) + + l2 = [ListMember(1, "one"), ListMember(2, "two"), ListMember(3, "three")] + assert 3 == ob.GetLength(l2) + + Python.Runtime.PyObjectConversions.Reset() diff --git a/src/tests/test_compat.py b/src/tests/test_compat.py deleted file mode 100644 index 1c9f80e65..000000000 --- a/src/tests/test_compat.py +++ /dev/null @@ -1,230 +0,0 @@ -# -*- coding: utf-8 -*- -# TODO: Complete removal of methods below. Similar to test_module - -"""Backward-compatibility tests for deprecated features.""" - -import types - -import pytest - -from .utils import is_clr_class, is_clr_module, is_clr_root_module - - -# Tests for old-style CLR-prefixed module naming. -def test_simple_import(): - """Test simple import.""" - import CLR - assert is_clr_root_module(CLR) - assert CLR.__name__ == 'clr' - - import sys - assert isinstance(sys, types.ModuleType) - assert sys.__name__ == 'sys' - - import http.client - assert isinstance(http.client, types.ModuleType) - assert http.client.__name__ == 'http.client' - - -def test_simple_import_with_alias(): - """Test simple import with aliasing.""" - import CLR as myCLR - assert is_clr_root_module(myCLR) - assert myCLR.__name__ == 'clr' - - import sys as mySys - assert isinstance(mySys, types.ModuleType) - assert mySys.__name__ == 'sys' - - import http.client as myHttplib - assert isinstance(myHttplib, types.ModuleType) - assert myHttplib.__name__ == 'http.client' - - -def test_dotted_name_import(): - """Test dotted-name import.""" - import CLR.System - assert is_clr_module(CLR.System) - assert CLR.System.__name__ == 'System' - - import System - assert is_clr_module(System) - assert System.__name__ == 'System' - - assert System is CLR.System - - import xml.dom - assert isinstance(xml.dom, types.ModuleType) - assert xml.dom.__name__ == 'xml.dom' - - -def test_dotted_name_import_with_alias(): - """Test dotted-name import with aliasing.""" - import CLR.System as myCLRSystem - assert is_clr_module(myCLRSystem) - assert myCLRSystem.__name__ == 'System' - - import System as mySystem - assert is_clr_module(mySystem) - assert mySystem.__name__ == 'System' - - assert mySystem is myCLRSystem - - import xml.dom as myDom - assert isinstance(myDom, types.ModuleType) - assert myDom.__name__ == 'xml.dom' - - -def test_simple_import_from(): - """Test simple 'import from'.""" - from CLR import System - assert is_clr_module(System) - assert System.__name__ == 'System' - - from xml import dom - assert isinstance(dom, types.ModuleType) - assert dom.__name__ == 'xml.dom' - - -def test_simple_import_from_with_alias(): - """Test simple 'import from' with aliasing.""" - from CLR import System as mySystem - assert is_clr_module(mySystem) - assert mySystem.__name__ == 'System' - - from xml import dom as myDom - assert isinstance(myDom, types.ModuleType) - assert myDom.__name__ == 'xml.dom' - - -def test_dotted_name_import_from(): - """Test dotted-name 'import from'.""" - from CLR.System import Xml - assert is_clr_module(Xml) - assert Xml.__name__ == 'System.Xml' - - from CLR.System.Xml import XmlDocument - assert is_clr_class(XmlDocument) - assert XmlDocument.__name__ == 'XmlDocument' - - from xml.dom import pulldom - assert isinstance(pulldom, types.ModuleType) - assert pulldom.__name__ == 'xml.dom.pulldom' - - from xml.dom.pulldom import PullDOM - assert isinstance(PullDOM, type) - assert PullDOM.__name__ == 'PullDOM' - - -def test_dotted_name_import_from_with_alias(): - """Test dotted-name 'import from' with aliasing.""" - from CLR.System import Xml as myXml - assert is_clr_module(myXml) - assert myXml.__name__ == 'System.Xml' - - from CLR.System.Xml import XmlDocument as myXmlDocument - assert is_clr_class(myXmlDocument) - assert myXmlDocument.__name__ == 'XmlDocument' - - from xml.dom import pulldom as myPulldom - assert isinstance(myPulldom, types.ModuleType) - assert myPulldom.__name__ == 'xml.dom.pulldom' - - from xml.dom.pulldom import PullDOM as myPullDOM - assert isinstance(myPullDOM, type) - assert myPullDOM.__name__ == 'PullDOM' - - -def test_from_module_import_star(): - """Test from module import * behavior.""" - count = len(locals().keys()) - m = __import__('CLR.System.Management', globals(), locals(), ['*']) - assert m.__name__ == 'System.Management' - assert is_clr_module(m) - assert len(locals().keys()) > count + 1 - - m2 = __import__('System.Management', globals(), locals(), ['*']) - assert m2.__name__ == 'System.Management' - assert is_clr_module(m2) - assert len(locals().keys()) > count + 1 - - assert m is m2 - - -def test_explicit_assembly_load(): - """Test explicit assembly loading using standard CLR tools.""" - from CLR.System.Reflection import Assembly - from CLR import System - import sys - - assembly = Assembly.LoadWithPartialName('System.Data') - assert assembly is not None - - import CLR.System.Data - assert 'System.Data' in sys.modules - - assembly = Assembly.LoadWithPartialName('SpamSpamSpamSpamEggsAndSpam') - assert assembly is None - - -def test_implicit_load_already_valid_namespace(): - """Test implicit assembly load over an already valid namespace.""" - # In this case, the mscorlib assembly (loaded by default) defines - # a number of types in the System namespace. There is also a System - # assembly, which is _not_ loaded by default, which also contains - # types in the System namespace. The desired behavior is for the - # Python runtime to "do the right thing", allowing types from both - # assemblies to be found in the CLR.System module implicitly. - import CLR.System - assert is_clr_class(CLR.System.UriBuilder) - - -def test_import_non_existant_module(): - """Test import failure for a non-existent module.""" - with pytest.raises(ImportError): - import System.SpamSpamSpam - - with pytest.raises(ImportError): - import CLR.System.SpamSpamSpam - - -def test_lookup_no_namespace_type(): - """Test lookup of types without a qualified namespace.""" - import CLR.Python.Test - import CLR - assert is_clr_class(CLR.NoNamespaceType) - - -def test_module_lookup_recursion(): - """Test for recursive lookup handling.""" - with pytest.raises(ImportError): - from CLR import CLR - - with pytest.raises(AttributeError): - import CLR - _ = CLR.CLR - - -def test_module_get_attr(): - """Test module getattr behavior.""" - import CLR.System as System - - int_type = System.Int32 - assert is_clr_class(int_type) - - module = System.Xml - assert is_clr_module(module) - - with pytest.raises(AttributeError): - _ = System.Spam - - with pytest.raises(TypeError): - _ = getattr(System, 1) - - -def test_multiple_imports(): - # import CLR did raise a Seg Fault once - # test if the Exceptions.warn() method still causes it - for _ in range(100): - import CLR - _ = CLR diff --git a/src/tests/__init__.py b/tests/__init__.py similarity index 100% rename from src/tests/__init__.py rename to tests/__init__.py diff --git a/src/tests/_missing_import.py b/tests/_missing_import.py similarity index 100% rename from src/tests/_missing_import.py rename to tests/_missing_import.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..cf3341f01 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# TODO: move tests one out of src to project root. +# TODO: travis has numpy on their workers. Maybe add tests? + +"""Helpers for testing.""" + +import ctypes +import os +import sys +import sysconfig +from subprocess import check_call +from tempfile import mkdtemp +import shutil + +import pytest + +from pythonnet import set_runtime + +# Add path for `Python.Test` +cwd = os.path.dirname(__file__) +fixtures_path = os.path.join(cwd, "fixtures") +sys.path.append(fixtures_path) + +def pytest_addoption(parser): + parser.addoption( + "--runtime", + action="store", + default="default", + help="Must be one of default, netcore, netfx and mono" + ) + +def pytest_configure(config): + global bin_path + if "clr" in sys.modules: + # Already loaded (e.g. by the C# test runner), skip build + import clr + clr.AddReference("Python.Test") + return + + runtime_opt = config.getoption("runtime") + + test_proj_path = os.path.join(cwd, "..", "src", "testing") + + if runtime_opt not in ["netcore", "netfx", "mono", "default"]: + raise RuntimeError(f"Invalid runtime: {runtime_opt}") + + bin_path = mkdtemp() + + # tmpdir_factory.mktemp(f"pythonnet-{runtime_opt}") + + fw = "net5.0" if runtime_opt == "netcore" else "netstandard2.0" + + check_call(["dotnet", "publish", "-f", fw, "-o", bin_path, test_proj_path]) + + sys.path.append(bin_path) + + if runtime_opt == "default": + pass + elif runtime_opt == "netfx": + from clr_loader import get_netfx + runtime = get_netfx() + set_runtime(runtime) + elif runtime_opt == "mono": + from clr_loader import get_mono + runtime = get_mono() + set_runtime(runtime) + elif runtime_opt == "netcore": + from clr_loader import get_coreclr + rt_config_path = os.path.join(bin_path, "Python.Test.runtimeconfig.json") + runtime = get_coreclr(rt_config_path) + set_runtime(runtime) + + import clr + clr.AddReference("Python.Test") + + +def pytest_unconfigure(config): + global bin_path + try: + shutil.rmtree(bin_path) + except Exception: + pass + +def pytest_report_header(config): + """Generate extra report headers""" + # FIXME: https://github.com/pytest-dev/pytest/issues/2257 + is_64bits = sys.maxsize > 2**32 + arch = "x64" if is_64bits else "x86" + ucs = ctypes.sizeof(ctypes.c_wchar) + libdir = sysconfig.get_config_var("LIBDIR") + shared = bool(sysconfig.get_config_var("Py_ENABLE_SHARED")) + + header = ("Arch: {arch}, UCS: {ucs}, LIBDIR: {libdir}, " + "Py_ENABLE_SHARED: {shared}".format(**locals())) + return header + + +@pytest.fixture() +def filepath(): + """Returns full filepath for file in `fixtures` directory.""" + + def make_filepath(filename): + # http://stackoverflow.com/questions/18011902/parameter-to-a-fixture + return os.path.join(fixtures_path, filename) + + return make_filepath diff --git a/src/tests/fixtures/argv-fixture.py b/tests/fixtures/argv-fixture.py similarity index 100% rename from src/tests/fixtures/argv-fixture.py rename to tests/fixtures/argv-fixture.py diff --git a/src/tests/importtest.py b/tests/importtest.py similarity index 100% rename from src/tests/importtest.py rename to tests/importtest.py diff --git a/src/tests/leaktest.py b/tests/leaktest.py similarity index 100% rename from src/tests/leaktest.py rename to tests/leaktest.py diff --git a/src/tests/profile.py b/tests/profile.py similarity index 100% rename from src/tests/profile.py rename to tests/profile.py diff --git a/src/tests/runtests.py b/tests/runtests.py similarity index 100% rename from src/tests/runtests.py rename to tests/runtests.py diff --git a/src/tests/stress.py b/tests/stress.py similarity index 100% rename from src/tests/stress.py rename to tests/stress.py diff --git a/src/tests/stresstest.py b/tests/stresstest.py similarity index 100% rename from src/tests/stresstest.py rename to tests/stresstest.py diff --git a/src/tests/test_array.py b/tests/test_array.py similarity index 96% rename from src/tests/test_array.py rename to tests/test_array.py index 015b5bdde..2b1a289ad 100644 --- a/src/tests/test_array.py +++ b/tests/test_array.py @@ -2,6 +2,7 @@ """Test support for managed arrays.""" +import clr import Python.Test as Test import System import pytest @@ -835,8 +836,15 @@ def test_typed_array(): with pytest.raises(TypeError): ob = Test.TypedArrayTest() - ob.items["wrong"] = "wrong" + ob.items["wrong"] = Spam("0") + with pytest.raises(TypeError): + ob = Test.TypedArrayTest() + _ = ob.items[0.5] + + with pytest.raises(TypeError): + ob = Test.TypedArrayTest() + ob.items[0.5] = Spam("0") def test_multi_dimensional_array(): """Test multi-dimensional arrays.""" @@ -900,8 +908,23 @@ def test_multi_dimensional_array(): with pytest.raises(TypeError): ob = Test.MultiDimensionalArrayTest() - ob[0, 0] = "wrong" + ob.items[0, 0] = "wrong" + + with pytest.raises(TypeError): + ob = Test.MultiDimensionalArrayTest() + ob["0", 0] = 0 + + with pytest.raises(TypeError): + ob = Test.MultiDimensionalArrayTest() + ob.items["0", 0] = 0 + + with pytest.raises(TypeError): + ob = Test.MultiDimensionalArrayTest() + _ = ob.items[0.5, 0] + with pytest.raises(TypeError): + ob = Test.MultiDimensionalArrayTest() + ob.items[0.5, 0] = 0 def test_array_iteration(): """Test array iteration.""" @@ -1143,8 +1166,8 @@ def test_boxed_value_type_mutation_result(): # to accidentally write code like the following which is not really # mutating value types in-place but changing boxed copies. - from System.Drawing import Point from System import Array + from Python.Test import Point items = Array.CreateInstance(Point, 5) @@ -1171,6 +1194,29 @@ def test_boxed_value_type_mutation_result(): assert items[i].X == i + 1 assert items[i].Y == i + 1 +def test_create_array_from_shape(): + from System import Array + + value = Array[int](3) + assert value[1] == 0 + assert value.Length == 3 + + value = Array[int](3, 4) + assert value[1, 1] == 0 + assert value.GetLength(0) == 3 + assert value.GetLength(1) == 4 + + with pytest.raises(ValueError): + Array[int](-1) + + with pytest.raises(TypeError): + Array[int]('1') + + with pytest.raises(ValueError): + Array[int](-1, -1) + + with pytest.raises(TypeError): + Array[int]('1', '1') def test_special_array_creation(): """Test using the Array[] syntax for creating arrays.""" diff --git a/src/tests/test_callback.py b/tests/test_callback.py similarity index 100% rename from src/tests/test_callback.py rename to tests/test_callback.py diff --git a/src/tests/test_class.py b/tests/test_class.py similarity index 99% rename from src/tests/test_class.py rename to tests/test_class.py index c24d788df..f961b3975 100644 --- a/src/tests/test_class.py +++ b/tests/test_class.py @@ -3,6 +3,7 @@ """Test CLR class support.""" +import clr import Python.Test as Test import System import pytest @@ -124,7 +125,8 @@ def __init__(self, v): def test_struct_construction(): """Test construction of structs.""" - from System.Drawing import Point + + from Python.Test import Point p = Point() assert p.X == 0 diff --git a/src/tests/test_clrmethod.py b/tests/test_clrmethod.py similarity index 99% rename from src/tests/test_clrmethod.py rename to tests/test_clrmethod.py index a6078bece..0c63f9c59 100644 --- a/src/tests/test_clrmethod.py +++ b/tests/test_clrmethod.py @@ -14,7 +14,7 @@ def __init__(self): @clr.clrmethod(int, [int]) def test(self, x): return x*2 - + def get_X(self): return self._x def set_X(self, value): diff --git a/src/tests/test_constructors.py b/tests/test_constructors.py similarity index 85% rename from src/tests/test_constructors.py rename to tests/test_constructors.py index 5e5489630..c305377f3 100644 --- a/src/tests/test_constructors.py +++ b/tests/test_constructors.py @@ -44,3 +44,12 @@ class Sub(System.Exception): instance = Sub() ob = SubclassConstructorTest(instance) assert isinstance(ob.value, System.Exception) + + +def test_multiple_constructor(): + from Python.Test import MultipleConstructorsTest + import System + + # Test parameterless + ob = MultipleConstructorsTest() + assert ob.value == "" diff --git a/src/tests/test_conversion.py b/tests/test_conversion.py similarity index 98% rename from src/tests/test_conversion.py rename to tests/test_conversion.py index 313274647..aea95e164 100644 --- a/src/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -343,7 +343,7 @@ def test_uint32_conversion(): ob.UInt32Field = System.UInt32(0) assert ob.UInt32Field == 0 - with pytest.raises(ValueError): + with pytest.raises(TypeError): ConversionTest().UInt32Field = "spam" with pytest.raises(TypeError): @@ -382,7 +382,10 @@ def test_uint64_conversion(): ob.UInt64Field = System.UInt64(0) assert ob.UInt64Field == 0 - with pytest.raises(ValueError): + with pytest.raises(TypeError): + ConversionTest().UInt64Field = 0.5 + + with pytest.raises(TypeError): ConversionTest().UInt64Field = "spam" with pytest.raises(TypeError): @@ -403,8 +406,8 @@ def test_uint64_conversion(): def test_single_conversion(): """Test single conversion.""" - assert System.Single.MaxValue == 3.402823e38 - assert System.Single.MinValue == -3.402823e38 + assert System.Single.MaxValue == pytest.approx(3.402823e38) + assert System.Single.MinValue == pytest.approx(-3.402823e38) ob = ConversionTest() assert ob.SingleField == 0.0 diff --git a/src/tests/test_delegate.py b/tests/test_delegate.py similarity index 57% rename from src/tests/test_delegate.py rename to tests/test_delegate.py index 1bfc4e903..52ac8226d 100644 --- a/src/tests/test_delegate.py +++ b/tests/test_delegate.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# TODO: Add test for ObjectDelegate """Test CLR delegate support.""" @@ -257,6 +256,186 @@ def always_so_negative(): assert not d() assert not ob.CallBoolDelegate(d) +def test_object_delegate(): + """Test object delegate.""" + from Python.Test import ObjectDelegate + + def create_object(): + return DelegateTest() + + d = ObjectDelegate(create_object) + ob = DelegateTest() + ob.CallObjectDelegate(d) + +def test_invalid_object_delegate(): + """Test invalid object delegate with mismatched return type.""" + from Python.Test import ObjectDelegate + + d = ObjectDelegate(hello_func) + ob = DelegateTest() + with pytest.raises(TypeError): + ob.CallObjectDelegate(d) + +def test_out_int_delegate(): + """Test delegate with an out int parameter.""" + from Python.Test import OutIntDelegate + value = 7 + + def out_hello_func(ignored): + return 5 + + d = OutIntDelegate(out_hello_func) + result = d(value) + assert result == 5 + + ob = DelegateTest() + result = ob.CallOutIntDelegate(d, value) + assert result == 5 + + def invalid_handler(ignored): + return '5' + + d = OutIntDelegate(invalid_handler) + with pytest.raises(TypeError): + result = d(value) + +def test_out_string_delegate(): + """Test delegate with an out string parameter.""" + from Python.Test import OutStringDelegate + value = 'goodbye' + + def out_hello_func(ignored): + return 'hello' + + d = OutStringDelegate(out_hello_func) + result = d(value) + assert result == 'hello' + + ob = DelegateTest() + result = ob.CallOutStringDelegate(d, value) + assert result == 'hello' + +def test_ref_int_delegate(): + """Test delegate with a ref string parameter.""" + from Python.Test import RefIntDelegate + value = 7 + + def ref_hello_func(data): + assert data == value + return data + 1 + + d = RefIntDelegate(ref_hello_func) + result = d(value) + assert result == value + 1 + + ob = DelegateTest() + result = ob.CallRefIntDelegate(d, value) + assert result == value + 1 + +def test_ref_string_delegate(): + """Test delegate with a ref string parameter.""" + from Python.Test import RefStringDelegate + value = 'goodbye' + + def ref_hello_func(data): + assert data == value + return 'hello' + + d = RefStringDelegate(ref_hello_func) + result = d(value) + assert result == 'hello' + + ob = DelegateTest() + result = ob.CallRefStringDelegate(d, value) + assert result == 'hello' + +def test_ref_int_ref_string_delegate(): + """Test delegate with a ref int and ref string parameter.""" + from Python.Test import RefIntRefStringDelegate + intData = 7 + stringData = 'goodbye' + + def ref_hello_func(intValue, stringValue): + assert intData == intValue + assert stringData == stringValue + return (intValue + 1, stringValue + '!') + + d = RefIntRefStringDelegate(ref_hello_func) + result = d(intData, stringData) + assert result == (intData + 1, stringData + '!') + + ob = DelegateTest() + result = ob.CallRefIntRefStringDelegate(d, intData, stringData) + assert result == (intData + 1, stringData + '!') + + def not_a_tuple(intValue, stringValue): + return 'a' + + d = RefIntRefStringDelegate(not_a_tuple) + with pytest.raises(TypeError): + result = d(intData, stringData) + + def short_tuple(intValue, stringValue): + return (5,) + + d = RefIntRefStringDelegate(short_tuple) + with pytest.raises(TypeError): + result = d(intData, stringData) + + def long_tuple(intValue, stringValue): + return (5, 'a', 'b') + + d = RefIntRefStringDelegate(long_tuple) + with pytest.raises(TypeError): + result = d(intData, stringData) + + def wrong_tuple_item(intValue, stringValue): + return ('a', 'b') + + d = RefIntRefStringDelegate(wrong_tuple_item) + with pytest.raises(TypeError): + result = d(intData, stringData) + +def test_int_ref_int_ref_string_delegate(): + """Test delegate with a ref int and ref string parameter.""" + from Python.Test import IntRefIntRefStringDelegate + intData = 7 + stringData = 'goodbye' + + def ref_hello_func(intValue, stringValue): + assert intData == intValue + assert stringData == stringValue + return (intValue + len(stringValue), intValue + 1, stringValue + '!') + + d = IntRefIntRefStringDelegate(ref_hello_func) + result = d(intData, stringData) + assert result == (intData + len(stringData), intData + 1, stringData + '!') + + ob = DelegateTest() + result = ob.CallIntRefIntRefStringDelegate(d, intData, stringData) + assert result == (intData + len(stringData), intData + 1, stringData + '!') + + def not_a_tuple(intValue, stringValue): + return 'a' + + d = IntRefIntRefStringDelegate(not_a_tuple) + with pytest.raises(TypeError): + result = d(intData, stringData) + + def short_tuple(intValue, stringValue): + return (5,) + + d = IntRefIntRefStringDelegate(short_tuple) + with pytest.raises(TypeError): + result = d(intData, stringData) + + def wrong_return_type(intValue, stringValue): + return ('a', 7, 'b') + + d = IntRefIntRefStringDelegate(wrong_return_type) + with pytest.raises(TypeError): + result = d(intData, stringData) + # test async delegates # test multicast delegates diff --git a/src/tests/test_docstring.py b/tests/test_docstring.py similarity index 100% rename from src/tests/test_docstring.py rename to tests/test_docstring.py diff --git a/src/tests/test_engine.py b/tests/test_engine.py similarity index 100% rename from src/tests/test_engine.py rename to tests/test_engine.py diff --git a/src/tests/test_enum.py b/tests/test_enum.py similarity index 100% rename from src/tests/test_enum.py rename to tests/test_enum.py diff --git a/src/tests/test_event.py b/tests/test_event.py similarity index 93% rename from src/tests/test_event.py rename to tests/test_event.py index e9c0ffd8a..885589032 100644 --- a/src/tests/test_event.py +++ b/tests/test_event.py @@ -295,6 +295,41 @@ def handler(sender, args, dict_=dict_): ob.OnPublicEvent(EventArgsTest(20)) assert dict_['value'] == 10 +def test_out_function_handler(): + """Test function handlers with Out arguments.""" + ob = EventTest() + + value = 10 + def handler(ignored): + return value + + ob.OutIntEvent += handler + result = ob.OnOutIntEvent(55) + assert result == value + + ob.OutStringEvent += handler + value = 'This is the event data' + result = ob.OnOutStringEvent('Hello') + assert result == value + +def test_ref_function_handler(): + """Test function handlers with Ref arguments.""" + ob = EventTest() + + value = 10 + def handler(data): + return value + data + + ob.RefIntEvent += ob.RefIntHandler + ob.RefIntEvent += handler + result = ob.OnRefIntEvent(5) + assert result == value + 5 + 1 + + ob.RefStringEvent += ob.RefStringHandler + ob.RefStringEvent += handler + value = 'This is the event data' + result = ob.OnRefStringEvent('!') + assert result == value + '!!' def test_add_non_callable_handler(): """Test handling of attempts to add non-callable handlers.""" diff --git a/src/tests/test_exceptions.py b/tests/test_exceptions.py similarity index 99% rename from src/tests/test_exceptions.py rename to tests/test_exceptions.py index 02d3005aa..7fafeebcb 100644 --- a/src/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -291,7 +291,7 @@ def test_exception_is_instance_of_system_object(): # classes, we wrap managed exceptions in a general-purpose old-style # class that delegates to the wrapped object. This makes _almost_ # everything work as expected, except that an isinstance check against - # CLR.System.Object will fail for a managed exception (because a new + # System.Object will fail for a managed exception (because a new # style class cannot appear in the __bases__ of an old-style class # without causing a crash in the CPython interpreter). This test is # here mainly to remind me to update the caveat in the documentation diff --git a/src/tests/test_field.py b/tests/test_field.py similarity index 100% rename from src/tests/test_field.py rename to tests/test_field.py diff --git a/src/tests/test_generic.py b/tests/test_generic.py similarity index 95% rename from src/tests/test_generic.py rename to tests/test_generic.py index c7e5a8d4d..248303179 100644 --- a/src/tests/test_generic.py +++ b/tests/test_generic.py @@ -298,9 +298,8 @@ def test_generic_method_type_handling(): from Python.Test import InterfaceTest, ISayHello1, ShortEnum import System - # FIXME: The value doesn't fit into Int64 and PythonNet doesn't - # recognize it as UInt64 for unknown reasons. - # assert_generic_method_by_type(System.UInt64, 18446744073709551615L) + # FIXME: Fails because Converter.GetTypeByAlias returns int32 for any integer, including ulong + # assert_generic_method_by_type(System.UInt64, 18446744073709551615) assert_generic_method_by_type(System.Boolean, True) assert_generic_method_by_type(bool, True) assert_generic_method_by_type(System.Byte, 255) @@ -745,3 +744,37 @@ def test_nested_generic_class(): """Check nested generic classes.""" # TODO NotImplemented pass + +def test_missing_generic_type(): + from System.Collections import IList + with pytest.raises(TypeError): + IList[bool] + +def test_invalid_generic_type_parameter(): + from Python.Test import GenericTypeWithConstraint + with pytest.raises(TypeError): + GenericTypeWithConstraint[System.Object] + +def test_invalid_generic_method_type_parameter(): + from Python.Test import ConversionTest + with pytest.raises(TypeError): + ConversionTest.Echo[System.Object] + +def test_generic_list_array_conversion(): + """Test conversion of lists to generic array arguments.""" + from Python.Test import GenericArrayConversionTest + from Python.Test import Spam + + items = [] + for i in range(10): + items.append(Spam(str(i))) + + result = GenericArrayConversionTest.EchoRange[Spam](items) + assert result[0].__class__ == Spam + assert len(result) == 10 + + # Currently raises an exception because the correct generic type parameter is not inferred + with pytest.raises(TypeError): + result = GenericArrayConversionTest.EchoRange(items) + assert result[0].__class__ == Spam + assert len(result) == 10 diff --git a/src/tests/test_import.py b/tests/test_import.py similarity index 100% rename from src/tests/test_import.py rename to tests/test_import.py diff --git a/src/tests/test_indexer.py b/tests/test_indexer.py similarity index 97% rename from src/tests/test_indexer.py rename to tests/test_indexer.py index b8a33fb46..7992f76b0 100644 --- a/src/tests/test_indexer.py +++ b/tests/test_indexer.py @@ -646,3 +646,21 @@ def test_inherited_indexer_interface(): ifc = IInheritedIndexer(impl) ifc[0] = "zero" assert ifc[0] == "zero" + +def test_public_inherited_overloaded_indexer(): + """Test public indexers.""" + ob = Test.PublicInheritedOverloadedIndexer() + + ob[0] = "zero" + assert ob[0] == "zero" + + ob[1] = "one" + assert ob[1] == "one" + + assert ob[10] is None + + ob["spam"] = "spam" + assert ob["spam"] == "spam" + + with pytest.raises(TypeError): + ob[[]] diff --git a/src/tests/test_interface.py b/tests/test_interface.py similarity index 80% rename from src/tests/test_interface.py rename to tests/test_interface.py index e6c6ba64b..130bd71c1 100644 --- a/src/tests/test_interface.py +++ b/tests/test_interface.py @@ -120,3 +120,30 @@ def test_implementation_access(): assert 100 == i.__implementation__ assert clrVal == i.__raw_implementation__ assert i.__implementation__ != i.__raw_implementation__ + + +def test_interface_collection_iteration(): + """Test interface type is used when iterating over interface collection""" + import System + from System.Collections.Generic import List + elem = System.IComparable(System.Int32(100)) + typed_list = List[System.IComparable]() + typed_list.Add(elem) + for e in typed_list: + assert type(e).__name__ == "IComparable" + + untyped_list = System.Collections.ArrayList() + untyped_list.Add(elem) + for e in untyped_list: + assert type(e).__name__ == "int" + + +def test_methods_of_Object_are_available(): + """Test calling methods inherited from Object""" + import System + clrVal = System.Int32(100) + i = System.IComparable(clrVal) + assert i.Equals(clrVal) + assert clrVal.GetHashCode() == i.GetHashCode() + assert clrVal.GetType() == i.GetType() + assert clrVal.ToString() == i.ToString() diff --git a/src/tests/test_method.py b/tests/test_method.py similarity index 93% rename from src/tests/test_method.py rename to tests/test_method.py index 15327cb3d..9bdb571c0 100644 --- a/src/tests/test_method.py +++ b/tests/test_method.py @@ -761,7 +761,7 @@ def test_we_can_bind_to_encoding_get_string(): read = 1 while read > 0: - read, _ = stream.Read(buff, 0, buff.Length) + read = stream.Read(buff, 0, buff.Length) temp = Encoding.UTF8.GetString(buff, 0, read) data.append(temp) @@ -807,6 +807,12 @@ def test_no_object_in_param(): with pytest.raises(TypeError): MethodTest.TestOverloadedNoObject("test") + with pytest.raises(TypeError): + MethodTest.TestOverloadedNoObject(5.5) + + # Ensure that the top-level error is TypeError even if the inner error is an OverflowError + with pytest.raises(TypeError): + MethodTest.TestOverloadedNoObject(2147483648) def test_object_in_param(): """Test regression introduced by #151 in which Object method overloads @@ -886,9 +892,13 @@ def test_object_in_multiparam(): def test_object_in_multiparam_exception(): """Test method with object multiparams behaves""" - with pytest.raises(TypeError): + with pytest.raises(TypeError) as excinfo: MethodTest.TestOverloadedObjectThree("foo", "bar") + e = excinfo.value + c = e.__cause__ + assert c.GetType().FullName == 'System.AggregateException' + assert len(c.InnerExceptions) == 2 def test_case_sensitive(): """Test that case-sensitivity is respected. GH#81""" @@ -928,7 +938,7 @@ def test_getting_generic_method_binding_does_not_leak_memory(): PlainOldClass().GenericMethod[str] gc.collect() - clr.System.GC.Collect() + System.GC.Collect() processBytesAfterCall = process.memory_info().rss print("Memory consumption (bytes) at end of test: " + str(processBytesAfterCall)) @@ -969,7 +979,7 @@ def test_getting_overloaded_method_binding_does_not_leak_memory(): PlainOldClass().OverloadedMethod.Overloads[int] gc.collect() - clr.System.GC.Collect() + System.GC.Collect() processBytesAfterCall = process.memory_info().rss print("Memory consumption (bytes) at end of test: " + str(processBytesAfterCall)) @@ -1010,7 +1020,7 @@ def test_getting_method_overloads_binding_does_not_leak_memory(): PlainOldClass().OverloadedMethod.Overloads gc.collect() - clr.System.GC.Collect() + System.GC.Collect() processBytesAfterCall = process.memory_info().rss print("Memory consumption (bytes) at end of test: " + str(processBytesAfterCall)) @@ -1153,9 +1163,77 @@ def test_optional_and_default_params(): res = MethodTest.OptionalAndDefaultParams2(b=2, c=3) assert res == "0232" +def test_default_params_overloads(): + res = MethodTest.DefaultParamsWithOverloading(1, 2) + assert res == "12" + + res = MethodTest.DefaultParamsWithOverloading(b=5) + assert res == "25" + + res = MethodTest.DefaultParamsWithOverloading("d") + assert res == "dbX" + + res = MethodTest.DefaultParamsWithOverloading(b="c") + assert res == "acX" + + res = MethodTest.DefaultParamsWithOverloading(c=3) + assert res == "013XX" + + res = MethodTest.DefaultParamsWithOverloading(5, c=2) + assert res == "512XX" + + res = MethodTest.DefaultParamsWithOverloading(c=0, d=1) + assert res == "5601XXX" + + res = MethodTest.DefaultParamsWithOverloading(1, d=1) + assert res == "1671XXX" + +def test_default_params_overloads_ambiguous_call(): + with pytest.raises(TypeError): + MethodTest.DefaultParamsWithOverloading() + def test_keyword_arg_method_resolution(): from Python.Test import MethodArityTest ob = MethodArityTest() assert ob.Foo(1, b=2) == "Arity 2" +def test_params_array_overload(): + res = MethodTest.ParamsArrayOverloaded() + assert res == "without params-array" + + res = MethodTest.ParamsArrayOverloaded(1) + assert res == "without params-array" + + res = MethodTest.ParamsArrayOverloaded(i=1) + assert res == "without params-array" + + res = MethodTest.ParamsArrayOverloaded(1, 2) + assert res == "with params-array" + + res = MethodTest.ParamsArrayOverloaded(1, 2, 3) + assert res == "with params-array" + + res = MethodTest.ParamsArrayOverloaded(1, paramsArray=[]) + assert res == "with params-array" + + res = MethodTest.ParamsArrayOverloaded(1, i=1) + assert res == "with params-array" + + res = MethodTest.ParamsArrayOverloaded(1, 2, 3, i=1) + assert res == "with params-array" + +@pytest.mark.skip(reason="FIXME: incorrectly failing") +def test_params_array_overloaded_failing(): + res = MethodTest.ParamsArrayOverloaded(1, 2, i=1) + assert res == "with params-array" + + res = MethodTest.ParamsArrayOverloaded(paramsArray=[], i=1) + assert res == "with params-array" + +def test_method_encoding(): + MethodTest.EncodingTestÅngström() + +def test_method_with_pointer_array_argument(): + with pytest.raises(TypeError): + MethodTest.PointerArray([0]) diff --git a/src/tests/test_module.py b/tests/test_module.py similarity index 90% rename from src/tests/test_module.py rename to tests/test_module.py index 2b1a9e4ec..d0378e91e 100644 --- a/src/tests/test_module.py +++ b/tests/test_module.py @@ -30,7 +30,7 @@ def test_import_clr(): def test_version_clr(): import clr - assert clr.__version__ >= "2.2.0" + assert clr.__version__ >= "3.0.0" def test_preload_var(): @@ -111,12 +111,13 @@ def test_dotted_name_import(): def test_multiple_dotted_name_import(): """Test an import bug with multiple dotted imports.""" - import System.Data - assert is_clr_module(System.Data) - assert System.Data.__name__ == 'System.Data' - import System.Data - assert is_clr_module(System.Data) - assert System.Data.__name__ == 'System.Data' + + import System.Reflection + assert is_clr_module(System.Reflection) + assert System.Reflection.__name__ == 'System.Reflection' + import System.Reflection + assert is_clr_module(System.Reflection) + assert System.Reflection.__name__ == 'System.Reflection' def test_dotted_name_import_with_alias(): @@ -192,27 +193,31 @@ def test_dotted_name_import_from_with_alias(): def test_from_module_import_star(): """Test from module import * behavior.""" + clr.AddReference("System") + count = len(locals().keys()) - m = __import__('System.Xml', globals(), locals(), ['*']) - assert m.__name__ == 'System.Xml' + m = __import__('System', globals(), locals(), ['*']) + assert m.__name__ == 'System' assert is_clr_module(m) assert len(locals().keys()) > count + 1 def test_implicit_assembly_load(): """Test implicit assembly loading via import.""" - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") + with pytest.raises(ImportError): + # MS.Build should not have been added as a reference yet + # (and should exist for mono) - # should trigger a DeprecationWarning as Microsoft.Build hasn't - # been added as a reference yet (and should exist for mono) + # The implicit behavior has been disabled in 3.0 + # therefore this should fail import Microsoft.Build - assert len(w) == 1 - assert isinstance(w[0].message, DeprecationWarning) - with warnings.catch_warnings(record=True) as w: - clr.AddReference("System.Windows.Forms") + try: + clr.AddReference("System.Windows.Forms") + except Exception: + pytest.skip() + import System.Windows.Forms as Forms assert is_clr_module(Forms) assert Forms.__name__ == 'System.Windows.Forms' @@ -227,11 +232,11 @@ def test_explicit_assembly_load(): from System.Reflection import Assembly import System, sys - assembly = Assembly.LoadWithPartialName('System.Data') + assembly = Assembly.LoadWithPartialName('System.Runtime') assert assembly is not None - import System.Data - assert 'System.Data' in sys.modules + import System.Runtime + assert 'System.Runtime' in sys.modules assembly = Assembly.LoadWithPartialName('SpamSpamSpamSpamEggsAndSpam') assert assembly is None @@ -275,12 +280,14 @@ def test_module_lookup_recursion(): def test_module_get_attr(): """Test module getattr behavior.""" + import System + import System.Runtime int_type = System.Int32 assert is_clr_class(int_type) - module = System.Xml + module = System.Runtime assert is_clr_module(module) with pytest.raises(AttributeError): @@ -324,7 +331,6 @@ def test_clr_list_assemblies(): from clr import ListAssemblies verbose = list(ListAssemblies(True)) short = list(ListAssemblies(False)) - assert u'mscorlib' in short assert u'System' in short assert u'Culture=' in verbose[0] assert u'Version=' in verbose[0] @@ -377,8 +383,11 @@ def test_assembly_load_thread_safety(): _ = Dictionary[Guid, DateTime]() ModuleTest.JoinThreads() +@pytest.mark.skipif() def test_assembly_load_recursion_bug(): """Test fix for recursion bug documented in #627""" - from System.Configuration import ConfigurationManager - content = dir(ConfigurationManager) + sys_config = pytest.importorskip( + "System.Configuration", reason="System.Configuration can't be imported" + ) + content = dir(sys_config.ConfigurationManager) assert len(content) > 5, content diff --git a/src/tests/test_mp_length.py b/tests/test_mp_length.py similarity index 74% rename from src/tests/test_mp_length.py rename to tests/test_mp_length.py index c96ac77d1..e86fff288 100644 --- a/src/tests/test_mp_length.py +++ b/tests/test_mp_length.py @@ -47,3 +47,17 @@ def test_custom_generic_collection_explicit___len__(): s.Add(1) s.Add(10) assert len(s) == 2 + +def test_len_through_interface_generic(): + """Test __len__ for ICollection""" + import System.Collections.Generic + l = System.Collections.Generic.List[int]() + coll = System.Collections.Generic.ICollection[int](l) + assert len(coll) == 0 + +def test_len_through_interface(): + """Test __len__ for ICollection""" + import System.Collections + l = System.Collections.ArrayList() + coll = System.Collections.ICollection(l) + assert len(coll) == 0 diff --git a/src/tests/test_property.py b/tests/test_property.py similarity index 100% rename from src/tests/test_property.py rename to tests/test_property.py diff --git a/src/tests/test_recursive_types.py b/tests/test_recursive_types.py similarity index 100% rename from src/tests/test_recursive_types.py rename to tests/test_recursive_types.py diff --git a/src/tests/test_repr.py b/tests/test_repr.py similarity index 100% rename from src/tests/test_repr.py rename to tests/test_repr.py diff --git a/src/tests/test_subclass.py b/tests/test_subclass.py similarity index 94% rename from src/tests/test_subclass.py rename to tests/test_subclass.py index 968f6a788..4f3180480 100644 --- a/src/tests/test_subclass.py +++ b/tests/test_subclass.py @@ -57,6 +57,14 @@ def return_list(self): return DerivedClass +def broken_derived_class_fixture(subnamespace): + """Delay creation of class until test starts.""" + + class DerivedClass(SubClassTest): + """class that derives from a class deriving from IInterfaceTest""" + __namespace__ = 3 + + return DerivedClass def derived_event_test_class_fixture(subnamespace): """Delay creation of class until test starts.""" @@ -127,6 +135,11 @@ def test_derived_class(): x = FunctionsTest.pass_through(ob) assert id(x) == id(ob) +def test_broken_derived_class(): + """Test python class derived from managed type with invalid namespace""" + with pytest.raises(TypeError): + DerivedClass = broken_derived_class_fixture(test_derived_class.__name__) + ob = DerivedClass() def test_derived_traceback(): """Test python exception traceback in class derived from managed base""" diff --git a/src/tests/test_sysargv.py b/tests/test_sysargv.py similarity index 77% rename from src/tests/test_sysargv.py rename to tests/test_sysargv.py index dd62bc632..d856ec902 100644 --- a/src/tests/test_sysargv.py +++ b/tests/test_sysargv.py @@ -2,6 +2,7 @@ import sys from subprocess import check_output +from ast import literal_eval def test_sys_argv_state(filepath): @@ -11,5 +12,5 @@ def test_sys_argv_state(filepath): script = filepath("argv-fixture.py") out = check_output([sys.executable, script, "foo", "bar"]) - assert b"foo" in out - assert b"bar" in out + out = literal_eval(out.decode("ascii")) + assert out[-2:] == ["foo", "bar"] diff --git a/src/tests/test_thread.py b/tests/test_thread.py similarity index 100% rename from src/tests/test_thread.py rename to tests/test_thread.py diff --git a/src/tests/tests.pyproj b/tests/tests.pyproj similarity index 83% rename from src/tests/tests.pyproj rename to tests/tests.pyproj index 4bdbc6b14..fc1053f7b 100644 --- a/src/tests/tests.pyproj +++ b/tests/tests.pyproj @@ -14,14 +14,8 @@ - - - - - - - - + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets @@ -58,6 +52,7 @@ + diff --git a/src/tests/utils.py b/tests/utils.py similarity index 100% rename from src/tests/utils.py rename to tests/utils.py diff --git a/tools/geninterop/geninterop.py b/tools/geninterop/geninterop.py index aacc4af65..0c80c1904 100644 --- a/tools/geninterop/geninterop.py +++ b/tools/geninterop/geninterop.py @@ -53,25 +53,25 @@ class AstParser(object): """Walk an AST and determine the members of all structs""" def __init__(self): - self.__typedefs = {} - self.__typedecls = {} - self.__structs = {} - self.__struct_stack = [] - self.__struct_members_stack = [] - self.__ptr_decl_depth = 0 - self.__struct_members = {} - self.__decl_names = {} + self._typedefs = {} + self._typedecls = {} + self._structs = {} + self._struct_stack = [] + self._struct_members_stack = [] + self._ptr_decl_depth = 0 + self._struct_members = {} + self._decl_names = {} def get_struct_members(self, name): """return a list of (name, type) of struct members""" - defs = self.__typedefs.get(name) + defs = self._typedefs.get(name) if defs is None: return None - node = self.__get_leaf_node(defs) + node = self._get_leaf_node(defs) name = node.name if name is None: name = defs.declname - return self.__struct_members.get(name) + return self._struct_members.get(name) def visit(self, node): if isinstance(node, c_ast.FileAST): @@ -96,27 +96,27 @@ def visit_ast(self, ast): self.visit(node) def visit_typedef(self, typedef): - self.__typedefs[typedef.name] = typedef.type + self._typedefs[typedef.name] = typedef.type self.visit(typedef.type) def visit_typedecl(self, typedecl): - self.__decl_names[typedecl.type] = typedecl.declname + self._decl_names[typedecl.type] = typedecl.declname self.visit(typedecl.type) def visit_struct(self, struct): - self.__structs[self.__get_struct_name(struct)] = struct if struct.decls: + self._structs[self._get_struct_name(struct)] = struct # recurse into the struct - self.__struct_stack.insert(0, struct) + self._struct_stack.insert(0, struct) for decl in struct.decls: - self.__struct_members_stack.insert(0, decl.name) + self._struct_members_stack.insert(0, decl.name) self.visit(decl) - self.__struct_members_stack.pop(0) - self.__struct_stack.pop(0) - elif self.__ptr_decl_depth: + self._struct_members_stack.pop(0) + self._struct_stack.pop(0) + elif self._ptr_decl_depth: # the struct is empty, but add it as a member to the current # struct as the current member maybe a pointer to it. - self.__add_struct_member(struct.name) + self._add_struct_member(struct.name) def visit_decl(self, decl): self.visit(decl.type) @@ -125,51 +125,57 @@ def visit_funcdecl(self, funcdecl): self.visit(funcdecl.type) def visit_ptrdecl(self, ptrdecl): - self.__ptr_decl_depth += 1 + self._ptr_decl_depth += 1 self.visit(ptrdecl.type) - self.__ptr_decl_depth -= 1 + self._ptr_decl_depth -= 1 def visit_identifier(self, identifier): type_name = " ".join(identifier.names) - self.__add_struct_member(type_name) + self._add_struct_member(type_name) - def __add_struct_member(self, type_name): - if not (self.__struct_stack and self.__struct_members_stack): + def _add_struct_member(self, type_name): + if not (self._struct_stack and self._struct_members_stack): return # add member to current struct - current_struct = self.__struct_stack[0] - member_name = self.__struct_members_stack[0] - struct_members = self.__struct_members.setdefault( - self.__get_struct_name(current_struct), []) + current_struct = self._struct_stack[0] + member_name = self._struct_members_stack[0] + struct_members = self._struct_members.setdefault( + self._get_struct_name(current_struct), []) # get the node associated with this type node = None - if type_name in self.__typedefs: - node = self.__get_leaf_node(self.__typedefs[type_name]) - elif type_name in self.__structs: - node = self.__structs[type_name] + if type_name in self._typedefs: + node = self._get_leaf_node(self._typedefs[type_name]) + # If the struct was only declared when the typedef was created, its member + # information will not have been recorded and we have to look it up in the + # structs + if isinstance(node, c_ast.Struct) and node.decls is None: + if node.name in self._structs: + node = self._structs[node.name] + elif type_name in self._structs: + node = self._structs[type_name] # If it's a struct (and not a pointer to a struct) expand # it into the current struct definition - if not self.__ptr_decl_depth and isinstance(node, c_ast.Struct): + if not self._ptr_decl_depth and isinstance(node, c_ast.Struct): for decl in node.decls or []: - self.__struct_members_stack.insert(0, decl.name) + self._struct_members_stack.insert(0, decl.name) self.visit(decl) - self.__struct_members_stack.pop(0) + self._struct_members_stack.pop(0) else: # otherwise add it as a single member struct_members.append((member_name, type_name)) - def __get_leaf_node(self, node): + def _get_leaf_node(self, node): if isinstance(node, c_ast.Typedef): - return self.__get_leaf_node(node.type) + return self._get_leaf_node(node.type) if isinstance(node, c_ast.TypeDecl): - return self.__get_leaf_node(node.type) + return self._get_leaf_node(node.type) return node - def __get_struct_name(self, node): - return node.name or self.__decl_names.get(node) or "_struct_%d" % id(node) + def _get_struct_name(self, node): + return node.name or self._decl_names.get(node) or "_struct_%d" % id(node) class Writer(object): @@ -219,8 +225,6 @@ def preprocess_python_headers(): if hasattr(sys, "abiflags"): if "d" in sys.abiflags: defines.extend(("-D", "PYTHON_WITH_PYDEBUG")) - if "m" in sys.abiflags: - defines.extend(("-D", "PYTHON_WITH_PYMALLOC")) if "u" in sys.abiflags: defines.extend(("-D", "PYTHON_WITH_WIDE_UNICODE")) @@ -238,67 +242,58 @@ def preprocess_python_headers(): def gen_interop_head(writer): - defines = [ - "PYTHON{0}{1}".format(PY_MAJOR, PY_MINOR) - ] - - if hasattr(sys, "abiflags"): - if "d" in sys.abiflags: - defines.append("PYTHON_WITH_PYDEBUG") - if "m" in sys.abiflags: - defines.append("PYTHON_WITH_PYMALLOC") - if "u" in sys.abiflags: - defines.append("PYTHON_WITH_WIDE_UNICODE") - filename = os.path.basename(__file__) - defines_str = " && ".join(defines) + abi_flags = getattr(sys, "abiflags", "").replace("m", "") + py_ver = "{0}.{1}".format(PY_MAJOR, PY_MINOR) class_definition = """ // Auto-generated by %s. // DO NOT MODIFY BY HAND. +// Python %s: ABI flags: '%s' + +// ReSharper disable InconsistentNaming +// ReSharper disable IdentifierTypo -#if %s using System; -using System.Collections; -using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using System.Reflection; -using System.Text; + +using Python.Runtime.Native; namespace Python.Runtime -{ -""" % (filename, defines_str) +{""" % (filename, py_ver, abi_flags) writer.extend(class_definition) def gen_interop_tail(writer): tail = """} -#endif """ writer.extend(tail) -def gen_heap_type_members(parser, writer): +def gen_heap_type_members(parser, writer, type_name = None): """Generate the TypeOffset C# class""" members = parser.get_struct_members("PyHeapTypeObject") + type_name = type_name or "TypeOffset{0}{1}".format(PY_MAJOR, PY_MINOR) class_definition = """ + [SuppressMessage("Style", "IDE1006:Naming Styles", + Justification = "Following CPython", + Scope = "type")] + [StructLayout(LayoutKind.Sequential)] - internal static partial class TypeOffset - { + internal class {0} : GeneratedTypeOffsets, ITypeOffsets + {{ + public {0}() {{ }} // Auto-generated from PyHeapTypeObject in Python.h -""" +""".format(type_name) # All the members are sizeof(void*) so we don't need to do any # extra work to determine the size based on the type. for name, tpy in members: name = _typeoffset_member_renames.get(name, name) - class_definition += " public static int %s = 0;\n" % name - - class_definition += """ - /* here are optional user slots, followed by the members. */ - public static int members = 0; - } + class_definition += " public int %s { get; private set; }\n" % name + class_definition += """ } """ writer.extend(class_definition) @@ -317,19 +312,6 @@ def gen_structure_code(parser, writer, type_name, indent): out() return True - -def gen_supported_slot_record(writer, types, indent): - out = writer.append - out(indent, "internal static partial class SlotTypes") - out(indent, "{") - out(indent + 1, "public static readonly Type[] Types = {") - for name in types: - out(indent + 2, "typeof(%s)," % name) - out(indent + 1, "};") - out(indent, "}") - out() - - def main(): # preprocess Python.h and build the AST python_h = preprocess_python_headers() @@ -342,23 +324,10 @@ def main(): writer = Writer() # generate the C# code + offsets_type_name = "NativeTypeOffset" if len(sys.argv) > 1 else None gen_interop_head(writer) - gen_heap_type_members(ast_parser, writer) - slots_types = [ - "PyNumberMethods", - "PySequenceMethods", - "PyMappingMethods", - "PyAsyncMethods", - "PyBufferProcs", - ] - supported_types = [] - indent = 1 - for type_name in slots_types: - if not gen_structure_code(ast_parser, writer, type_name, indent): - continue - supported_types.append(type_name) - gen_supported_slot_record(writer, supported_types, indent) + gen_heap_type_members(ast_parser, writer, type_name = offsets_type_name) gen_interop_tail(writer) diff --git a/tools/nuget/nuget.exe b/tools/nuget/nuget.exe deleted file mode 100644 index 463f8e137..000000000 Binary files a/tools/nuget/nuget.exe and /dev/null differ diff --git a/tools/vswhere/vswhere.exe b/tools/vswhere/vswhere.exe deleted file mode 100644 index 582e82868..000000000 Binary files a/tools/vswhere/vswhere.exe and /dev/null differ