├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ARM.yml │ ├── main.yml │ └── nuget-preview.yml ├── .gitignore ├── .mention-bot ├── AUTHORS.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Directory.Build.props ├── LICENSE ├── MANIFEST.in ├── README.rst ├── clr.py ├── demo ├── DynamicGrid.py ├── DynamicGrid.xaml ├── helloform.py ├── splitter.py └── wordpad.py ├── pyproject.toml ├── pythonnet.sln ├── pythonnet ├── .gitignore ├── __init__.py └── runtime │ └── .gitkeep ├── requirements.txt ├── setup.py ├── src ├── console │ ├── Console.csproj │ ├── python-clear.ico │ ├── python-clear.png │ └── pythonconsole.cs ├── embed_tests │ ├── CallableObject.cs │ ├── ClassManagerTests.cs │ ├── CodecGroups.cs │ ├── Codecs.cs │ ├── ExtensionTypes.cs │ ├── GlobalTestsSetup.cs │ ├── Inheritance.cs │ ├── Inspect.cs │ ├── Modules.cs │ ├── NumPyTests.cs │ ├── Python.EmbeddingTest.csproj │ ├── References.cs │ ├── StateSerialization │ │ └── MethodSerialization.cs │ ├── TestCallbacks.cs │ ├── TestConverter.cs │ ├── TestCustomMarshal.cs │ ├── TestDomainReload.cs │ ├── TestFinalizer.cs │ ├── TestGILState.cs │ ├── TestInstanceWrapping.cs │ ├── TestInterrupt.cs │ ├── TestNamedArguments.cs │ ├── TestNativeTypeOffset.cs │ ├── TestOperator.cs │ ├── TestPyBuffer.cs │ ├── TestPyFloat.cs │ ├── TestPyInt.cs │ ├── TestPyIter.cs │ ├── TestPyList.cs │ ├── TestPyNumber.cs │ ├── TestPyObject.cs │ ├── TestPySequence.cs │ ├── TestPyString.cs │ ├── TestPyTuple.cs │ ├── TestPyType.cs │ ├── TestPyWith.cs │ ├── TestPythonEngineProperties.cs │ ├── TestPythonException.cs │ ├── TestRuntime.cs │ ├── dynamic.cs │ ├── fixtures │ │ └── PyImportTest │ │ │ ├── __init__.py │ │ │ ├── cast_global_var.py │ │ │ ├── sysargv.py │ │ │ └── test │ │ │ ├── __init__.py │ │ │ └── one.py │ ├── pyimport.cs │ ├── pyinitialize.cs │ └── pyrunstring.cs ├── perf_tests │ ├── BaselineComparisonBenchmarkBase.cs │ ├── BaselineComparisonConfig.cs │ ├── BenchmarkTests.cs │ ├── Python.PerformanceTests.csproj │ ├── PythonCallingNetBenchmark.cs │ └── baseline │ │ └── .gitkeep ├── python_tests_runner │ ├── Python.PythonTestsRunner.csproj │ └── PythonTestRunner.cs ├── pythonnet.snk ├── runtime │ ├── AssemblyManager.cs │ ├── ClassManager.cs │ ├── Codecs │ │ ├── DecoderGroup.cs │ │ ├── EncoderGroup.cs │ │ ├── EnumPyIntCodec.cs │ │ ├── IPyObjectDecoder.cs │ │ ├── IPyObjectEncoder.cs │ │ ├── IterableDecoder.cs │ │ ├── ListDecoder.cs │ │ ├── PyObjectConversions.cs │ │ ├── RawProxyEncoder.cs │ │ ├── SequenceDecoder.cs │ │ └── TupleCodecs.cs │ ├── CollectionWrappers │ │ ├── IterableWrapper.cs │ │ ├── ListWrapper.cs │ │ └── SequenceWrapper.cs │ ├── Converter.cs │ ├── DefaultBaseTypeProvider.cs │ ├── DelegateManager.cs │ ├── Exceptions.cs │ ├── Finalizer.cs │ ├── IPythonBaseTypeProvider.cs │ ├── ImportHook.cs │ ├── Interfaces.cs │ ├── InternString.cs │ ├── Interop.cs │ ├── InteropConfiguration.cs │ ├── Loader.cs │ ├── MethodBinder.cs │ ├── Mixins │ │ ├── CollectionMixinsProvider.cs │ │ └── collections.py │ ├── Native │ │ ├── ABI.cs │ │ ├── BorrowedReference.cs │ │ ├── CustomMarshaler.cs │ │ ├── GeneratedTypeOffsets.cs │ │ ├── ITypeOffsets.cs │ │ ├── LibDL.cs │ │ ├── LibraryLoader.cs │ │ ├── NativeCall.cs │ │ ├── NativeFunc.cs │ │ ├── NativeTypeSpec.cs │ │ ├── NewReference.cs │ │ ├── PyBufferInterface.cs │ │ ├── PyCompilerFlags.cs │ │ ├── PyGILState.cs │ │ ├── PyIdentifier_.cs │ │ ├── PyIdentifier_.tt │ │ ├── PyInterpreterState.cs │ │ ├── PyMemberFlags.cs │ │ ├── PyMemberType.cs │ │ ├── PyMethodFlags.cs │ │ ├── PyThreadState.cs │ │ ├── ReferenceExtensions.cs │ │ ├── StolenReference.cs │ │ ├── StrPtr.cs │ │ ├── TypeOffset.cs │ │ ├── TypeOffset310.cs │ │ ├── TypeOffset37.cs │ │ ├── TypeOffset38.cs │ │ └── TypeOffset39.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Py.cs │ ├── PyExportAttribute.cs │ ├── Python.Runtime.csproj │ ├── PythonBaseTypeProviderGroup.cs │ ├── PythonEngine.cs │ ├── PythonException.cs │ ├── PythonTypes │ │ ├── PyBuffer.cs │ │ ├── PyDict.cs │ │ ├── PyFloat.cs │ │ ├── PyInt.cs │ │ ├── PyIter.cs │ │ ├── PyIterable.cs │ │ ├── PyList.cs │ │ ├── PyModule.cs │ │ ├── PyNumber.cs │ │ ├── PyObject.IConvertible.cs │ │ ├── PyObject.cs │ │ ├── PySequence.cs │ │ ├── PyString.cs │ │ ├── PyTuple.cs │ │ ├── PyType.cs │ │ └── TypeSpec.cs │ ├── README.md │ ├── Resources │ │ ├── clr.py │ │ └── interop.py │ ├── Runtime.Delegates.cs │ ├── Runtime.cs │ ├── RuntimeState.cs │ ├── StateSerialization │ │ ├── CLRMappedItem.cs │ │ ├── CLRWrapperCollection.cs │ │ ├── ClassManagerState.cs │ │ ├── ICLRObjectStorer.cs │ │ ├── ImportHookState.cs │ │ ├── MaybeMemberInfo.cs │ │ ├── MaybeMethodBase.cs │ │ ├── MaybeType.cs │ │ ├── MetatypeState.cs │ │ ├── PythonNetState.cs │ │ ├── RuntimeData.cs │ │ ├── SharedObjectsState.cs │ │ ├── TypeManagerState.cs │ │ └── UnloadedClass.cs │ ├── TypeManager.cs │ ├── Types │ │ ├── ArrayObject.cs │ │ ├── ClassBase.cs │ │ ├── ClassDerived.cs │ │ ├── ClassObject.cs │ │ ├── ClrObject.cs │ │ ├── DelegateObject.cs │ │ ├── EventBinding.cs │ │ ├── EventObject.cs │ │ ├── ExceptionClassObject.cs │ │ ├── ExtensionType.cs │ │ ├── FieldObject.cs │ │ ├── GenericType.cs │ │ ├── Indexer.cs │ │ ├── InterfaceObject.cs │ │ ├── Iterator.cs │ │ ├── ManagedType.cs │ │ ├── ManagedTypes.cd │ │ ├── MetaType.cs │ │ ├── MethodBinding.cs │ │ ├── MethodObject.cs │ │ ├── ModuleFunctionObject.cs │ │ ├── ModuleObject.cs │ │ ├── ModulePropertyObject.cs │ │ ├── MpLengthSlot.cs │ │ ├── OperatorMethod.cs │ │ ├── OverloadMapper.cs │ │ ├── PropertyObject.cs │ │ ├── ReflectedClrType.cs │ │ └── UnsafeReferenceWithRun.cs │ └── Util │ │ ├── CodeGenerator.cs │ │ ├── DebugUtil.cs │ │ ├── EventHandlerCollection.cs │ │ ├── GenericUtil.cs │ │ ├── InitOnly.cs │ │ ├── NonCopyableAttribute.cs │ │ ├── NullOnly.cs │ │ ├── OpsHelper.cs │ │ ├── ParameterHelper.cs │ │ ├── PythonReferenceComparer.cs │ │ ├── ReflectionPolyfills.cs │ │ ├── ReflectionUtil.cs │ │ └── Util.cs └── testing │ ├── CodecTest.cs │ ├── InheritanceTest.cs │ ├── Python.Test.csproj │ ├── ReprTest.cs │ ├── arraytest.cs │ ├── callbacktest.cs │ ├── classtest.cs │ ├── constructortests.cs │ ├── conversiontest.cs │ ├── delegatetest.cs │ ├── doctest.cs │ ├── enumtest.cs │ ├── eventtest.cs │ ├── exceptiontest.cs │ ├── fieldtest.cs │ ├── generictest.cs │ ├── globaltest.cs │ ├── indexertest.cs │ ├── interfacetest.cs │ ├── methodtest.cs │ ├── moduletest.cs │ ├── mp_lengthtest.cs │ ├── nonexportable.cs │ ├── propertytest.cs │ ├── subclasstest.cs │ └── threadtest.cs ├── tests ├── __init__.py ├── _missing_import.py ├── conftest.py ├── domain_tests │ ├── App.config │ ├── Python.DomainReloadTests.csproj │ ├── TestRunner.cs │ └── test_domain_reload.py ├── fixtures │ └── argv-fixture.py ├── importtest.py ├── leaktest.py ├── profile.py ├── runtests.py ├── stress.py ├── stresstest.py ├── test_array.py ├── test_callback.py ├── test_class.py ├── test_clrmethod.py ├── test_codec.py ├── test_collection_mixins.py ├── test_constructors.py ├── test_conversion.py ├── test_delegate.py ├── test_docstring.py ├── test_engine.py ├── test_enum.py ├── test_event.py ├── test_exceptions.py ├── test_field.py ├── test_generic.py ├── test_import.py ├── test_indexer.py ├── test_interface.py ├── test_method.py ├── test_module.py ├── test_mp_length.py ├── test_property.py ├── test_recursive_types.py ├── test_repr.py ├── test_subclass.py ├── test_sysargv.py ├── test_thread.py ├── tests.pyproj └── utils.py ├── tools └── geninterop │ ├── fake_libc_include │ ├── _ansi.h │ ├── _fake_defines.h │ ├── _fake_typedefs.h │ ├── _syslist.h │ ├── alloca.h │ ├── ar.h │ ├── argz.h │ ├── arpa │ │ └── inet.h │ ├── asm-generic │ │ └── int-ll64.h │ ├── assert.h │ ├── complex.h │ ├── crypt.h │ ├── ctype.h │ ├── dirent.h │ ├── dlfcn.h │ ├── endian.h │ ├── envz.h │ ├── errno.h │ ├── fastmath.h │ ├── fcntl.h │ ├── features.h │ ├── fenv.h │ ├── float.h │ ├── getopt.h │ ├── grp.h │ ├── iconv.h │ ├── ieeefp.h │ ├── inttypes.h │ ├── io.h │ ├── iso646.h │ ├── langinfo.h │ ├── libgen.h │ ├── libintl.h │ ├── limits.h │ ├── linux │ │ ├── socket.h │ │ └── version.h │ ├── locale.h │ ├── malloc.h │ ├── math.h │ ├── netdb.h │ ├── netinet │ │ ├── in.h │ │ └── tcp.h │ ├── newlib.h │ ├── openssl │ │ ├── err.h │ │ ├── evp.h │ │ ├── hmac.h │ │ ├── ssl.h │ │ └── x509v3.h │ ├── paths.h │ ├── process.h │ ├── pthread.h │ ├── pwd.h │ ├── reent.h │ ├── regdef.h │ ├── regex.h │ ├── sched.h │ ├── search.h │ ├── semaphore.h │ ├── setjmp.h │ ├── signal.h │ ├── stdarg.h │ ├── stdbool.h │ ├── stddef.h │ ├── stdint.h │ ├── stdio.h │ ├── stdlib.h │ ├── string.h │ ├── sys │ │ ├── ioctl.h │ │ ├── mman.h │ │ ├── poll.h │ │ ├── resource.h │ │ ├── select.h │ │ ├── socket.h │ │ ├── stat.h │ │ ├── sysctl.h │ │ ├── time.h │ │ ├── types.h │ │ ├── uio.h │ │ ├── un.h │ │ ├── utsname.h │ │ └── wait.h │ ├── syslog.h │ ├── tar.h │ ├── termios.h │ ├── tgmath.h │ ├── time.h │ ├── unctrl.h │ ├── unistd.h │ ├── utime.h │ ├── utmp.h │ ├── wchar.h │ ├── wctype.h │ └── zlib.h │ └── geninterop.py └── version.txt /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | end_of_line = lf 10 | indent_style = space 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | # config files 15 | [*.{ini,yaml,yml}] 16 | indent_size = 2 17 | 18 | # Xml project files 19 | [*.{csproj,pyproj,config}] 20 | indent_size = 2 21 | 22 | # .NET formatting settings 23 | [*.{cs,vb}] 24 | dotnet_sort_system_directives_first = true 25 | dotnet_separate_import_directive_groups = true 26 | 27 | [*.cs] 28 | csharp_new_line_before_open_brace = all 29 | csharp_new_line_before_else = true 30 | csharp_new_line_before_catch = true 31 | csharp_new_line_before_finally = true 32 | 33 | # Solution 34 | [*.sln] 35 | indent_style = tab 36 | 37 | [*.csproj] 38 | charset = utf-8 39 | insert_final_newline = true 40 | 41 | # bumpversion reformats itself after every bump 42 | [.bumpversion.cfg] 43 | trim_trailing_whitespace = false 44 | indent_style = tab 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Environment 2 | 3 | - Pythonnet version: 4 | - Python version: 5 | - Operating System: 6 | - .NET Runtime: 7 | 8 | ### Details 9 | 10 | - Describe what you were trying to get done. 11 | 12 | _TODO_ 13 | 14 | - What commands did you run to trigger this issue? If you can provide a 15 | [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) 16 | this will help us understand the issue. 17 | 18 | ```python 19 | print('TODO') 20 | ``` 21 | 22 | - If there was a crash, please include the traceback here. 23 | 24 | ```python 25 | print('TODO') 26 | ``` 27 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What does this implement/fix? Explain your changes. 2 | 3 | ... 4 | 5 | ### Does this close any currently open issues? 6 | 7 | ... 8 | 9 | ### Any other comments? 10 | 11 | ... 12 | 13 | ### Checklist 14 | 15 | Check all those that are applicable and complete. 16 | 17 | - [ ] Make sure to include one or more tests for your change 18 | - [ ] If an enhancement PR, please create docs and at best an example 19 | - [ ] Ensure you have signed the [.NET Foundation CLA](https://cla.dotnetfoundation.org/pythonnet/pythonnet) 20 | - [ ] Add yourself to [`AUTHORS`](../blob/master/AUTHORS.md) 21 | - [ ] Updated the [`CHANGELOG`](../blob/master/CHANGELOG.md) 22 | -------------------------------------------------------------------------------- /.github/workflows/ARM.yml: -------------------------------------------------------------------------------- 1 | name: Main (ARM) 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build-test-arm: 11 | name: Build and Test ARM64 12 | runs-on: [self-hosted, linux, ARM64] 13 | timeout-minutes: 15 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v2 18 | 19 | - name: Setup .NET 20 | uses: actions/setup-dotnet@v1 21 | with: 22 | dotnet-version: '6.0.x' 23 | 24 | - name: Clean previous install 25 | run: | 26 | pip uninstall -y pythonnet 27 | 28 | - name: Install dependencies 29 | run: | 30 | pip install --upgrade -r requirements.txt 31 | pip install pytest numpy # for tests 32 | 33 | - name: Build and Install 34 | run: | 35 | pip install -v . 36 | 37 | - name: Set Python DLL path (non Windows) 38 | run: | 39 | echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV 40 | 41 | - name: Embedding tests 42 | run: dotnet test --logger "console;verbosity=detailed" src/embed_tests/ 43 | 44 | - name: Python Tests (Mono) 45 | run: python -m pytest --runtime mono 46 | 47 | - name: Python Tests (.NET Core) 48 | run: python -m pytest --runtime coreclr 49 | 50 | - name: Python tests run from .NET 51 | run: dotnet test src/python_tests_runner/ 52 | 53 | #- name: Perf tests 54 | # run: | 55 | # pip install --force --no-deps --target src/perf_tests/baseline/ pythonnet==2.5.2 56 | # dotnet test --configuration Release --logger "console;verbosity=detailed" src/perf_tests/ 57 | -------------------------------------------------------------------------------- /.github/workflows/nuget-preview.yml: -------------------------------------------------------------------------------- 1 | name: NuGet Preview Release 2 | 3 | on: 4 | schedule: 5 | - cron: "5 4 3 */1 *" # once a month, at 4:05 on 3rd 6 | workflow_dispatch: 7 | 8 | jobs: 9 | release: 10 | name: Release Preview 11 | runs-on: ubuntu-latest 12 | environment: NuGet 13 | timeout-minutes: 10 14 | 15 | env: 16 | PYTHONNET_SHUTDOWN_MODE: Normal 17 | 18 | steps: 19 | - name: Get Date 20 | run: | 21 | echo "DATE_VER=$(date "+%Y-%m-%d")" >> $GITHUB_ENV 22 | 23 | - name: Checkout code 24 | uses: actions/checkout@v2 25 | 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: '6.0.x' 30 | 31 | - name: Set up Python 3.8 32 | uses: actions/setup-python@v2 33 | with: 34 | python-version: 3.8 35 | architecture: x64 36 | 37 | - name: Install dependencies 38 | run: | 39 | pip install --upgrade -r requirements.txt 40 | pip install numpy # for tests 41 | 42 | - name: Build and Install 43 | run: | 44 | pip install -v . 45 | 46 | - name: Set Python DLL path (non Windows) 47 | if: ${{ matrix.os != 'windows' }} 48 | run: | 49 | echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV 50 | 51 | - name: Python Tests 52 | run: pytest 53 | 54 | - name: Embedding tests 55 | run: dotnet test --runtime any-ubuntu src/embed_tests/ 56 | 57 | - name: Pack 58 | run: dotnet pack --configuration Release --version-suffix preview${{env.DATE_VER}} --output "Release-Preview" 59 | 60 | - name: Publish NuGet 61 | run: | 62 | dotnet nuget push --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_MONTHLY }} Release-Preview/*.nupkg 63 | dotnet nuget push --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_MONTHLY }} Release-Preview/*.snupkg 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /src/runtime/interopNative.cs 2 | /src/perf_tests/baseline/ 3 | 4 | # General binaries and Build results 5 | *.dll 6 | *.exe 7 | *.pdb 8 | *.deps.json 9 | 10 | ### JetBrains ### 11 | .idea/ 12 | 13 | ### Python ### 14 | # Byte-compiled / optimized / DLL files 15 | __pycache__/ 16 | *.py[cod] 17 | 18 | # Distribution / packaging 19 | build/ 20 | dist/ 21 | *.egg-info/ 22 | .eggs/ 23 | 24 | # Unit test / coverage reports 25 | htmlcov/ 26 | .tox/ 27 | .coverage 28 | .coverage.* 29 | .cache 30 | coverage.xml 31 | 32 | ### CSharp ### 33 | # User-specific files 34 | *.suo 35 | *.user 36 | *.vcxproj.filters 37 | *.userprefs 38 | *.DotSettings.user 39 | 40 | # Build results 41 | [Bb]in/ 42 | [Oo]bj/ 43 | 44 | # Visual Studio cache/options directory 45 | .vs/ 46 | 47 | # NUNIT 48 | *.VisualState.xml 49 | TestResult.xml 50 | 51 | # OpenCover 52 | /results.xml 53 | 54 | # NuGet Packages 55 | **/packages/* 56 | 57 | # VS Project upgrade log files 58 | _UpgradeReport_Files/ 59 | Backup*/ 60 | UpgradeLog*.XML 61 | UpgradeLog*.htm 62 | 63 | # Coverity 64 | cov-int/ 65 | 66 | # Visual Studio Code 67 | .vscode/* 68 | !.vscode/settings.json 69 | !.vscode/tasks.json 70 | !.vscode/launch.json 71 | !.vscode/extensions.json 72 | -------------------------------------------------------------------------------- /.mention-bot: -------------------------------------------------------------------------------- 1 | { 2 | "maxReviewers": 5, 3 | "numFilesToCheck": 10, 4 | "message": "@pullRequester, thanks! @reviewers, please review this.", 5 | "alwaysNotifyForPaths": [ 6 | { 7 | "name": "ghuser", 8 | "files": ["src/js/**/*.js"], 9 | "skipTeamPrs": false 10 | } 11 | ], 12 | "fallbackNotifyForPaths": [ 13 | { 14 | "name": "ghuser", 15 | "files": ["src/js/**/*.js"], 16 | "skipTeamPrs": false 17 | } 18 | ], 19 | "findPotentialReviewers": true, 20 | "fileBlacklist": ["*.md"], 21 | "userBlacklist": [], 22 | "userBlacklistForPR": [], 23 | "requiredOrgs": [], 24 | "actions": ["opened"], 25 | "skipAlreadyAssignedPR": false, 26 | "skipAlreadyMentionedPR": false, 27 | "assignToReviewer": false, 28 | "createReviewRequest": false, 29 | "createComment": true, 30 | "skipTitle": "", 31 | "withLabel": "", 32 | "delayed": false, 33 | "delayedUntil": "3d", 34 | "skipCollaboratorPR": false 35 | } 36 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Python.NET is developed and maintained by unpaid community members so well 4 | written, documented and tested pull requests are encouraged. 5 | 6 | By submitting a pull request for this project, you agree to license your 7 | contribution under the MIT license to this project. 8 | 9 | This project has adopted the code of conduct defined by the Contributor 10 | Covenant to clarify expected behavior in our community. For more information 11 | see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). 12 | 13 | ## Getting Started 14 | 15 | - Make sure you have a [GitHub account](https://github.com/signup/free) 16 | - Submit a ticket for your issue, assuming one does not already exist. 17 | - Clearly describe the issue including steps to reproduce the bug. 18 | - Include what Python version and operating system you are using. 19 | - Fork the repository on GitHub 20 | 21 | ## Making Changes 22 | 23 | - Create a topic branch from where you want to base your work. 24 | - This is usually the master branch. 25 | - Only target release branches if you are certain your fix must be on 26 | that branch. 27 | - To quickly create a topic branch based on master; 28 | `git checkout -b fix/develop/my_contribution master`. 29 | Please avoid working directly on the `master` branch for anything 30 | other than trivial changes. 31 | - Make commits of logical units. 32 | - Check for unnecessary whitespace with `git diff --check` before committing. 33 | - Make sure your commit messages are in the proper format. 34 | - Make sure you have added the necessary tests for your changes. 35 | - Run _all_ the tests to assure nothing else was accidentally broken. 36 | 37 | ## Submitting Changes 38 | 39 | - Merge the topic branch into master and push to your fork of the repository. 40 | - Submit a pull request to the repository in the pythonnet organization. 41 | - After feedback has been given we expect responses within two weeks. After 42 | two weeks we may close the pull request if it isn't showing any activity. 43 | 44 | # Additional Resources 45 | 46 | - [General GitHub documentation](https://help.github.com/) 47 | - [GitHub pull request documentation](https://help.github.com/send-pull-requests/) 48 | - [.NET Foundation Code of Conduct](https://dotnetfoundation.org/about/code-of-conduct) 49 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (c) 2006-2022 The Contributors of the Python.NET Project 5 | pythonnet 6 | Python.NET 7 | 10.0 8 | false 9 | $([System.IO.File]::ReadAllText($(MSBuildThisFileDirectory)version.txt)) 10 | $(FullVersion.Split('-', 2)[0]) 11 | $(FullVersion.Split('-', 2)[1]) 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2006-2021 the contributors of the Python.NET project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src/runtime 2 | prune src/runtime/obj 3 | prune src/runtime/bin 4 | include Directory.Build.* 5 | include pythonnet.sln 6 | include version.txt 7 | -------------------------------------------------------------------------------- /clr.py: -------------------------------------------------------------------------------- 1 | """ 2 | Legacy Python.NET loader for backwards compatibility 3 | """ 4 | 5 | from pythonnet import load 6 | load() 7 | -------------------------------------------------------------------------------- /demo/DynamicGrid.py: -------------------------------------------------------------------------------- 1 | import clr 2 | import sys 3 | if sys.platform.lower() not in ['cli','win32']: 4 | print("only windows is supported for wpf") 5 | clr.AddReference(r"wpf\PresentationFramework") 6 | from System.IO import StreamReader 7 | from System.Windows.Markup import XamlReader 8 | from System.Threading import Thread, ThreadStart, ApartmentState 9 | from System.Windows import Application, Window 10 | 11 | 12 | class MyWindow(Window): 13 | def __init__(self): 14 | stream = StreamReader("DynamicGrid.xaml") 15 | window = XamlReader.Load(stream.BaseStream) 16 | Application().Run(window) 17 | 18 | 19 | if __name__ == '__main__': 20 | thread = Thread(ThreadStart(MyWindow)) 21 | thread.SetApartmentState(ApartmentState.STA) 22 | thread.Start() 23 | thread.Join() 24 | -------------------------------------------------------------------------------- /demo/DynamicGrid.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 34 | 35 | -------------------------------------------------------------------------------- /demo/helloform.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import clr 5 | 6 | clr.AddReference("System.Windows.Forms") 7 | import System.Windows.Forms as WinForms 8 | from System.Drawing import Size, Point 9 | 10 | 11 | class HelloApp(WinForms.Form): 12 | """A simple hello world app that demonstrates the essentials of 13 | winforms programming and event-based programming in Python.""" 14 | 15 | def __init__(self): 16 | super().__init__() 17 | self.Text = "Hello World From Python" 18 | self.AutoScaleBaseSize = Size(5, 13) 19 | self.ClientSize = Size(392, 117) 20 | h = WinForms.SystemInformation.CaptionHeight 21 | self.MinimumSize = Size(392, (117 + h)) 22 | 23 | # Create the button 24 | self.button = WinForms.Button() 25 | self.button.Location = Point(160, 64) 26 | self.button.Size = Size(820, 20) 27 | self.button.TabIndex = 2 28 | self.button.Text = "Click Me!" 29 | 30 | # Register the event handler 31 | self.button.Click += self.button_Click 32 | 33 | # Create the text box 34 | self.textbox = WinForms.TextBox() 35 | self.textbox.Text = "Hello World" 36 | self.textbox.TabIndex = 1 37 | self.textbox.Size = Size(1260, 40) 38 | self.textbox.Location = Point(160, 24) 39 | 40 | # Add the controls to the form 41 | self.AcceptButton = self.button 42 | self.Controls.Add(self.button) 43 | self.Controls.Add(self.textbox) 44 | 45 | def button_Click(self, sender, args): 46 | """Button click event handler""" 47 | print ("Click") 48 | WinForms.MessageBox.Show("Please do not press this button again.") 49 | 50 | def run(self): 51 | WinForms.Application.Run(self) 52 | 53 | 54 | def main(): 55 | form = HelloApp() 56 | print ("form created") 57 | app = WinForms.Application 58 | print ("app referenced") 59 | app.Run(form) 60 | 61 | 62 | if __name__ == '__main__': 63 | main() 64 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "pythonnet" 7 | description = ".NET and Mono integration for Python" 8 | license = {text = "MIT"} 9 | 10 | readme = "README.rst" 11 | 12 | dependencies = [ 13 | "clr_loader==0.1.7" 14 | ] 15 | 16 | classifiers = [ 17 | "Development Status :: 5 - Production/Stable", 18 | "Intended Audience :: Developers", 19 | "License :: OSI Approved :: MIT License", 20 | "Programming Language :: C#", 21 | "Programming Language :: Python :: 3", 22 | "Programming Language :: Python :: 3.7", 23 | "Programming Language :: Python :: 3.8", 24 | "Programming Language :: Python :: 3.9", 25 | "Programming Language :: Python :: 3.10", 26 | "Operating System :: Microsoft :: Windows", 27 | "Operating System :: POSIX :: Linux", 28 | "Operating System :: MacOS :: MacOS X", 29 | ] 30 | 31 | dynamic = ["version"] 32 | 33 | [[project.authors]] 34 | name = "The Contributors of the Python.NET Project" 35 | email = "pythonnet@python.org" 36 | 37 | [project.urls] 38 | Homepage = "https://pythonnet.github.io/" 39 | Sources = "https://github.com/pythonnet/pythonnet" 40 | 41 | [tool.setuptools] 42 | zip-safe = false 43 | py-modules = ["clr"] 44 | 45 | [tool.setuptools.dynamic.version] 46 | file = "version.txt" 47 | 48 | [tool.setuptools.packages.find] 49 | include = ["pythonnet*"] 50 | 51 | [tool.pytest.ini_options] 52 | xfail_strict = true 53 | testpaths = [ 54 | "tests" 55 | ] 56 | -------------------------------------------------------------------------------- /pythonnet/.gitignore: -------------------------------------------------------------------------------- 1 | mono/ 2 | netfx/ 3 | runtime/ 4 | -------------------------------------------------------------------------------- /pythonnet/runtime/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-Technologies/pythonnet/37c442666046d941af912d005c96c7a0aef9dd49/pythonnet/runtime/.gitkeep -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements for both Travis and AppVeyor 2 | pytest 3 | psutil 4 | 5 | # Coverage upload 6 | coverage 7 | codecov 8 | 9 | wheel 10 | pycparser 11 | setuptools 12 | clr-loader 13 | 14 | # Discover libpython 15 | find_libpython -------------------------------------------------------------------------------- /src/console/Console.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net472;net6.0 4 | x64;x86 5 | Exe 6 | nPython 7 | Python.Runtime 8 | nPython 9 | python-clear.ico 10 | 11 | 12 | 13 | 14 | 15 | Python.Runtime.dll 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/console/python-clear.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-Technologies/pythonnet/37c442666046d941af912d005c96c7a0aef9dd49/src/console/python-clear.ico -------------------------------------------------------------------------------- /src/console/python-clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-Technologies/pythonnet/37c442666046d941af912d005c96c7a0aef9dd49/src/console/python-clear.png -------------------------------------------------------------------------------- /src/embed_tests/ClassManagerTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | using Python.Runtime; 4 | 5 | namespace Python.EmbeddingTest 6 | { 7 | public class ClassManagerTests 8 | { 9 | [OneTimeSetUp] 10 | public void SetUp() 11 | { 12 | PythonEngine.Initialize(); 13 | } 14 | 15 | [OneTimeTearDown] 16 | public void Dispose() 17 | { 18 | PythonEngine.Shutdown(); 19 | } 20 | 21 | [Test] 22 | public void NestedClassDerivingFromParent() 23 | { 24 | var f = new NestedTestContainer().ToPython(); 25 | f.GetAttr(nameof(NestedTestContainer.Bar)); 26 | } 27 | } 28 | 29 | public class NestedTestParent 30 | { 31 | public class Nested : NestedTestParent 32 | { 33 | } 34 | } 35 | 36 | public class NestedTestContainer 37 | { 38 | public NestedTestParent Bar = new NestedTestParent.Nested(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/embed_tests/ExtensionTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using NUnit.Framework; 4 | 5 | using Python.Runtime; 6 | 7 | namespace Python.EmbeddingTest; 8 | 9 | public class ExtensionTypes 10 | { 11 | [OneTimeSetUp] 12 | public void SetUp() 13 | { 14 | PythonEngine.Initialize(); 15 | } 16 | 17 | [OneTimeTearDown] 18 | public void Dispose() 19 | { 20 | PythonEngine.Shutdown(); 21 | } 22 | 23 | [Test] 24 | public void WeakrefIsNone_AfterBoundMethodIsGone() 25 | { 26 | using var makeref = Py.Import("weakref").GetAttr("ref"); 27 | var boundMethod = new UriBuilder().ToPython().GetAttr(nameof(UriBuilder.GetHashCode)); 28 | var weakref = makeref.Invoke(boundMethod); 29 | boundMethod.Dispose(); 30 | Assert.IsTrue(weakref.Invoke().IsNone()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/embed_tests/GlobalTestsSetup.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Python.Runtime; 3 | 4 | namespace Python.EmbeddingTest 5 | { 6 | 7 | // As the SetUpFixture, the OneTimeTearDown of this class is executed after 8 | // all tests have run. 9 | [SetUpFixture] 10 | public partial class GlobalTestsSetup 11 | { 12 | [OneTimeSetUp] 13 | public void GlobalSetup() 14 | { 15 | Finalizer.Instance.ErrorHandler += FinalizerErrorHandler; 16 | } 17 | 18 | private void FinalizerErrorHandler(object sender, Finalizer.ErrorArgs e) 19 | { 20 | if (e.Error is RuntimeShutdownException) 21 | { 22 | // allow objects to leak after the python runtime run 23 | // they were created in is gone 24 | e.Handled = true; 25 | } 26 | } 27 | 28 | [OneTimeTearDown] 29 | public void FinalCleanup() 30 | { 31 | if (PythonEngine.IsInitialized) 32 | { 33 | PythonEngine.Shutdown(); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/embed_tests/Inspect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using NUnit.Framework; 5 | 6 | using Python.Runtime; 7 | 8 | namespace Python.EmbeddingTest 9 | { 10 | public class Inspect 11 | { 12 | [OneTimeSetUp] 13 | public void SetUp() 14 | { 15 | PythonEngine.Initialize(); 16 | } 17 | 18 | [OneTimeTearDown] 19 | public void Dispose() 20 | { 21 | PythonEngine.Shutdown(); 22 | } 23 | 24 | [Test] 25 | public void InstancePropertiesVisibleOnClass() 26 | { 27 | var uri = new Uri("http://example.org").ToPython(); 28 | var uriClass = uri.GetPythonType(); 29 | var property = uriClass.GetAttr(nameof(Uri.AbsoluteUri)); 30 | var pyProp = (PropertyObject)ManagedType.GetManagedObject(property.Reference); 31 | Assert.AreEqual(nameof(Uri.AbsoluteUri), pyProp.info.Value.Name); 32 | } 33 | 34 | [Test] 35 | public void BoundMethodsAreInspectable() 36 | { 37 | using var scope = Py.CreateScope(); 38 | try 39 | { 40 | scope.Import("inspect"); 41 | } 42 | catch (PythonException) 43 | { 44 | Assert.Inconclusive("Python build does not include inspect module"); 45 | return; 46 | } 47 | 48 | var obj = new Class(); 49 | scope.Set(nameof(obj), obj); 50 | using var spec = scope.Eval($"inspect.getfullargspec({nameof(obj)}.{nameof(Class.Method)})"); 51 | } 52 | 53 | class Class 54 | { 55 | public void Method(int a, int b = 10) { } 56 | public void Method(int a, object b) { } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/embed_tests/Python.EmbeddingTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472;net6.0 5 | ..\pythonnet.snk 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | $(DefineConstants);$(ConfiguredConstants) 19 | 20 | 21 | 22 | 23 | 24 | all 25 | runtime; build; native; contentfiles; analyzers; buildtransitive 26 | 27 | 28 | 29 | 1.0.0 30 | all 31 | runtime; build; native; contentfiles; analyzers 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/embed_tests/References.cs: -------------------------------------------------------------------------------- 1 | namespace Python.EmbeddingTest 2 | { 3 | using NUnit.Framework; 4 | using Python.Runtime; 5 | 6 | public class References 7 | { 8 | [OneTimeSetUp] 9 | public void SetUp() 10 | { 11 | PythonEngine.Initialize(); 12 | } 13 | 14 | [OneTimeTearDown] 15 | public void Dispose() 16 | { 17 | PythonEngine.Shutdown(); 18 | } 19 | 20 | [Test] 21 | public void MoveToPyObject_SetsNull() 22 | { 23 | var dict = new PyDict(); 24 | NewReference reference = Runtime.PyDict_Items(dict.Reference); 25 | try 26 | { 27 | Assert.IsFalse(reference.IsNull()); 28 | 29 | using (reference.MoveToPyObject()) 30 | Assert.IsTrue(reference.IsNull()); 31 | } 32 | finally 33 | { 34 | reference.Dispose(); 35 | } 36 | } 37 | 38 | [Test] 39 | public void CanBorrowFromNewReference() 40 | { 41 | var dict = new PyDict(); 42 | using NewReference reference = Runtime.PyDict_Items(dict.Reference); 43 | BorrowedReference borrowed = reference.BorrowOrThrow(); 44 | PythonException.ThrowIfIsNotZero(Runtime.PyList_Reverse(borrowed)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/embed_tests/StateSerialization/MethodSerialization.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Reflection; 3 | 4 | using NUnit.Framework; 5 | 6 | using Python.Runtime; 7 | 8 | namespace Python.EmbeddingTest.StateSerialization; 9 | 10 | public class MethodSerialization 11 | { 12 | [Test] 13 | public void GenericRoundtrip() 14 | { 15 | var method = typeof(MethodTestHost).GetMethod(nameof(MethodTestHost.Generic)); 16 | var maybeMethod = new MaybeMethodBase(method); 17 | var restored = SerializationRoundtrip(maybeMethod); 18 | Assert.IsTrue(restored.Valid); 19 | Assert.AreEqual(method, restored.Value); 20 | } 21 | 22 | [Test] 23 | public void ConstrctorRoundtrip() 24 | { 25 | var ctor = typeof(MethodTestHost).GetConstructor(new[] { typeof(int) }); 26 | var maybeConstructor = new MaybeMethodBase(ctor); 27 | var restored = SerializationRoundtrip(maybeConstructor); 28 | Assert.IsTrue(restored.Valid); 29 | Assert.AreEqual(ctor, restored.Value); 30 | } 31 | 32 | static T SerializationRoundtrip(T item) 33 | { 34 | using var buf = new MemoryStream(); 35 | var formatter = RuntimeData.CreateFormatter(); 36 | formatter.Serialize(buf, item); 37 | buf.Position = 0; 38 | return (T)formatter.Deserialize(buf); 39 | } 40 | } 41 | 42 | public class MethodTestHost 43 | { 44 | public MethodTestHost(int _) { } 45 | public void Generic(T item, T[] array, ref T @ref) { } 46 | } 47 | -------------------------------------------------------------------------------- /src/embed_tests/TestCallbacks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using NUnit.Framework; 4 | using Python.Runtime; 5 | 6 | namespace Python.EmbeddingTest { 7 | public class TestCallbacks { 8 | [OneTimeSetUp] 9 | public void SetUp() { 10 | PythonEngine.Initialize(); 11 | } 12 | 13 | [OneTimeTearDown] 14 | public void Dispose() { 15 | PythonEngine.Shutdown(); 16 | } 17 | 18 | [Test] 19 | public void TestNoOverloadException() { 20 | int passed = 0; 21 | var aFunctionThatCallsIntoPython = new Action(value => passed = value); 22 | using (Py.GIL()) { 23 | using dynamic callWith42 = PythonEngine.Eval("lambda f: f([42])"); 24 | using var pyFunc = aFunctionThatCallsIntoPython.ToPython(); 25 | var error = Assert.Throws(() => callWith42(pyFunc)); 26 | Assert.AreEqual("TypeError", error.Type.Name); 27 | string expectedArgTypes = "()"; 28 | StringAssert.EndsWith(expectedArgTypes, error.Message); 29 | error.Traceback.Dispose(); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/embed_tests/TestCustomMarshal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Python.Runtime; 4 | 5 | namespace Python.EmbeddingTest 6 | { 7 | public class TestCustomMarshal 8 | { 9 | [OneTimeSetUp] 10 | public void SetUp() 11 | { 12 | PythonEngine.Initialize(); 13 | } 14 | 15 | [OneTimeTearDown] 16 | public void Dispose() 17 | { 18 | PythonEngine.Shutdown(); 19 | } 20 | 21 | [Test] 22 | public static void GetManagedStringTwice() 23 | { 24 | const string expected = "FooBar"; 25 | 26 | using var op = Runtime.Runtime.PyString_FromString(expected); 27 | string s1 = Runtime.Runtime.GetManagedString(op.BorrowOrThrow()); 28 | string s2 = Runtime.Runtime.GetManagedString(op.Borrow()); 29 | 30 | Assert.AreEqual(1, Runtime.Runtime.Refcount32(op.Borrow())); 31 | Assert.AreEqual(expected, s1); 32 | Assert.AreEqual(expected, s2); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/embed_tests/TestGILState.cs: -------------------------------------------------------------------------------- 1 | namespace Python.EmbeddingTest 2 | { 3 | using NUnit.Framework; 4 | using Python.Runtime; 5 | 6 | public class TestGILState 7 | { 8 | /// 9 | /// Ensure, that calling multiple times is safe 10 | /// 11 | [Test] 12 | public void CanDisposeMultipleTimes() 13 | { 14 | using (var gilState = Py.GIL()) 15 | { 16 | for(int i = 0; i < 50; i++) 17 | gilState.Dispose(); 18 | } 19 | } 20 | 21 | [OneTimeSetUp] 22 | public void SetUp() 23 | { 24 | PythonEngine.Initialize(); 25 | } 26 | 27 | [OneTimeTearDown] 28 | public void Dispose() 29 | { 30 | PythonEngine.Shutdown(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/embed_tests/TestInstanceWrapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using NUnit.Framework; 4 | using Python.Runtime; 5 | 6 | namespace Python.EmbeddingTest 7 | { 8 | public class TestInstanceWrapping 9 | { 10 | [OneTimeSetUp] 11 | public void SetUp() 12 | { 13 | PythonEngine.Initialize(); 14 | } 15 | 16 | [OneTimeTearDown] 17 | public void Dispose() 18 | { 19 | PythonEngine.Shutdown(); 20 | } 21 | 22 | // regression test for https://github.com/pythonnet/pythonnet/issues/811 23 | [Test] 24 | public void OverloadResolution_UnknownToObject() 25 | { 26 | var overloaded = new Overloaded(); 27 | using (Py.GIL()) 28 | { 29 | var o = overloaded.ToPython(); 30 | 31 | dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(object())"); 32 | callWithSelf(o); 33 | Assert.AreEqual(Overloaded.Object, overloaded.Value); 34 | } 35 | } 36 | 37 | [Test] 38 | public void WeakRefIsNone_AfterObjectIsGone() 39 | { 40 | using var makeref = Py.Import("weakref").GetAttr("ref"); 41 | var ub = new UriBuilder().ToPython(); 42 | using var weakref = makeref.Invoke(ub); 43 | ub.Dispose(); 44 | Assert.IsTrue(weakref.Invoke().IsNone()); 45 | } 46 | 47 | class Base {} 48 | class Derived: Base { } 49 | 50 | class Overloaded: Derived 51 | { 52 | public int Value { get; set; } 53 | public void IntOrStr(int arg) => this.Value = arg; 54 | public void IntOrStr(string arg) => 55 | this.Value = int.Parse(arg, NumberStyles.Integer, CultureInfo.InvariantCulture); 56 | 57 | public const int Object = 1; 58 | public const int ConcreteClass = 2; 59 | public void ObjOrClass(object _) => this.Value = Object; 60 | public void ObjOrClass(Overloaded _) => this.Value = ConcreteClass; 61 | 62 | public const int Base = ConcreteClass + 1; 63 | public const int Derived = Base + 1; 64 | public void BaseOrDerived(Base _) => this.Value = Base; 65 | public void BaseOrDerived(Derived _) => this.Value = Derived; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/embed_tests/TestNamedArguments.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Python.Runtime; 4 | 5 | namespace Python.EmbeddingTest 6 | { 7 | public class TestNamedArguments 8 | { 9 | [OneTimeSetUp] 10 | public void SetUp() 11 | { 12 | PythonEngine.Initialize(); 13 | } 14 | 15 | [OneTimeTearDown] 16 | public void Dispose() 17 | { 18 | PythonEngine.Shutdown(); 19 | } 20 | 21 | /// 22 | /// Test named arguments support through Py.kw method 23 | /// 24 | [Test] 25 | public void TestKeywordArgs() 26 | { 27 | dynamic a = CreateTestClass(); 28 | var result = (int)a.Test3(2, Py.kw("a4", 8)); 29 | 30 | Assert.AreEqual(12, result); 31 | } 32 | 33 | 34 | /// 35 | /// Test keyword arguments with .net named arguments 36 | /// 37 | [Test] 38 | public void TestNamedArgs() 39 | { 40 | dynamic a = CreateTestClass(); 41 | var result = (int)a.Test3(2, a4: 8); 42 | 43 | Assert.AreEqual(12, result); 44 | } 45 | 46 | 47 | 48 | private static PyObject CreateTestClass() 49 | { 50 | var locals = new PyDict(); 51 | 52 | PythonEngine.Exec(@" 53 | class cmTest3: 54 | def Test3(self, a1 = 1, a2 = 1, a3 = 1, a4 = 1): 55 | return a1 + a2 + a3 + a4 56 | 57 | a = cmTest3() 58 | ", null, locals); 59 | 60 | return locals.GetItem("a"); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/embed_tests/TestNativeTypeOffset.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | using NUnit.Framework; 9 | 10 | using Python.Runtime; 11 | 12 | namespace Python.EmbeddingTest 13 | { 14 | public class TestNativeTypeOffset 15 | { 16 | [OneTimeSetUp] 17 | public void SetUp() 18 | { 19 | PythonEngine.Initialize(); 20 | } 21 | 22 | [OneTimeTearDown] 23 | public void Dispose() 24 | { 25 | PythonEngine.Shutdown(); 26 | } 27 | 28 | /// 29 | /// Tests that installation has generated code for NativeTypeOffset and that it can be loaded. 30 | /// 31 | [Test] 32 | public void LoadNativeTypeOffsetClass() 33 | { 34 | PyObject sys = Py.Import("sys"); 35 | // We can safely ignore the "m" abi flag 36 | var abiflags = sys.HasAttr("abiflags") ? sys.GetAttr("abiflags").ToString() : ""; 37 | abiflags = abiflags.Replace("m", ""); 38 | if (!string.IsNullOrEmpty(abiflags)) 39 | { 40 | string typeName = "Python.Runtime.NativeTypeOffset, Python.Runtime"; 41 | Assert.NotNull(Type.GetType(typeName), $"{typeName} does not exist and sys.abiflags={abiflags}"); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/embed_tests/TestPyIter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | 4 | using NUnit.Framework; 5 | 6 | using Python.Runtime; 7 | 8 | namespace Python.EmbeddingTest 9 | { 10 | class TestPyIter 11 | { 12 | [OneTimeSetUp] 13 | public void SetUp() 14 | { 15 | PythonEngine.Initialize(); 16 | } 17 | 18 | [OneTimeTearDown] 19 | public void Dispose() 20 | { 21 | PythonEngine.Shutdown(); 22 | } 23 | 24 | [Test] 25 | public void KeepOldObjects() 26 | { 27 | using (Py.GIL()) 28 | using (var testString = new PyString("hello world! !$%&/()=?")) 29 | { 30 | PyObject[] chars = testString.ToArray(); 31 | Assert.IsTrue(chars.Length > 1); 32 | string reconstructed = string.Concat(chars.Select(c => c.As())); 33 | Assert.AreEqual(testString.As(), reconstructed); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/embed_tests/TestPyNumber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Python.Runtime; 4 | 5 | namespace Python.EmbeddingTest 6 | { 7 | public class TestPyNumber 8 | { 9 | [OneTimeSetUp] 10 | public void SetUp() 11 | { 12 | PythonEngine.Initialize(); 13 | } 14 | 15 | [OneTimeTearDown] 16 | public void Dispose() 17 | { 18 | PythonEngine.Shutdown(); 19 | } 20 | 21 | [Test] 22 | public void IsNumberTypeTrue() 23 | { 24 | var i = new PyInt(1); 25 | Assert.True(PyNumber.IsNumberType(i)); 26 | } 27 | 28 | [Test] 29 | public void IsNumberTypeFalse() 30 | { 31 | var s = new PyString("Foo"); 32 | Assert.False(PyNumber.IsNumberType(s)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/embed_tests/TestPySequence.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Python.Runtime; 4 | 5 | namespace Python.EmbeddingTest 6 | { 7 | public class TestPySequence 8 | { 9 | [OneTimeSetUp] 10 | public void SetUp() 11 | { 12 | PythonEngine.Initialize(); 13 | } 14 | 15 | [OneTimeTearDown] 16 | public void Dispose() 17 | { 18 | PythonEngine.Shutdown(); 19 | } 20 | 21 | [Test] 22 | public void TestIsSequenceTrue() 23 | { 24 | var t = new PyString("FooBar"); 25 | Assert.True(PySequence.IsSequenceType(t)); 26 | } 27 | 28 | [Test] 29 | public void TestIsSequenceFalse() 30 | { 31 | var t = new PyInt(5); 32 | Assert.False(PySequence.IsSequenceType(t)); 33 | } 34 | 35 | [Test] 36 | public void TestGetSlice() 37 | { 38 | var t = new PyString("FooBar"); 39 | 40 | PyObject s = t.GetSlice(0, 3); 41 | Assert.AreEqual("Foo", s.ToString()); 42 | 43 | PyObject s2 = t.GetSlice(3, 6); 44 | Assert.AreEqual("Bar", s2.ToString()); 45 | 46 | PyObject s3 = t.GetSlice(0, 6); 47 | Assert.AreEqual("FooBar", s3.ToString()); 48 | 49 | PyObject s4 = t.GetSlice(0, 12); 50 | Assert.AreEqual("FooBar", s4.ToString()); 51 | } 52 | 53 | [Test] 54 | public void TestConcat() 55 | { 56 | var t1 = new PyString("Foo"); 57 | var t2 = new PyString("Bar"); 58 | 59 | PyObject actual = t1.Concat(t2); 60 | 61 | Assert.AreEqual("FooBar", actual.ToString()); 62 | } 63 | 64 | [Test] 65 | public void TestRepeat() 66 | { 67 | var t1 = new PyString("Foo"); 68 | 69 | PyObject actual = t1.Repeat(3); 70 | Assert.AreEqual("FooFooFoo", actual.ToString()); 71 | 72 | actual = t1.Repeat(-3); 73 | Assert.AreEqual("", actual.ToString()); 74 | } 75 | 76 | [Test] 77 | public void TestContains() 78 | { 79 | var t1 = new PyString("FooBar"); 80 | 81 | Assert.True(t1.Contains(new PyString("a"))); 82 | Assert.False(t1.Contains(new PyString("z"))); 83 | } 84 | 85 | [Test] 86 | public void TestIndex() 87 | { 88 | var t1 = new PyString("FooBar"); 89 | 90 | Assert.AreEqual(4, t1.Index32(new PyString("a"))); 91 | Assert.AreEqual(5L, t1.Index64(new PyString("r"))); 92 | Assert.AreEqual(-(nint)1, t1.Index(new PyString("z"))); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/embed_tests/TestPyType.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | 4 | using NUnit.Framework; 5 | 6 | using Python.Runtime; 7 | using Python.Runtime.Native; 8 | 9 | namespace Python.EmbeddingTest 10 | { 11 | public class TestPyType 12 | { 13 | [OneTimeSetUp] 14 | public void SetUp() 15 | { 16 | PythonEngine.Initialize(); 17 | } 18 | 19 | [OneTimeTearDown] 20 | public void Dispose() 21 | { 22 | PythonEngine.Shutdown(); 23 | } 24 | 25 | [Test] 26 | public void CanCreateHeapType() 27 | { 28 | const string name = "nÁmæ"; 29 | const string docStr = "dÁcæ"; 30 | 31 | using var doc = new StrPtr(docStr, Encoding.UTF8); 32 | var spec = new TypeSpec( 33 | name: name, 34 | basicSize: Util.ReadInt32(Runtime.Runtime.PyBaseObjectType, TypeOffset.tp_basicsize), 35 | slots: new TypeSpec.Slot[] { 36 | new (TypeSlotID.tp_doc, doc.RawPointer), 37 | }, 38 | TypeFlags.Default | TypeFlags.HeapType 39 | ); 40 | 41 | using var type = new PyType(spec); 42 | Assert.AreEqual(name, type.GetAttr("__name__").As()); 43 | Assert.AreEqual(name, type.Name); 44 | Assert.AreEqual(docStr, type.GetAttr("__doc__").As()); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/embed_tests/TestPyWith.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Python.Runtime; 4 | 5 | namespace Python.EmbeddingTest 6 | { 7 | public class TestPyWith 8 | { 9 | [OneTimeSetUp] 10 | public void SetUp() 11 | { 12 | PythonEngine.Initialize(); 13 | } 14 | 15 | [OneTimeTearDown] 16 | public void Dispose() 17 | { 18 | PythonEngine.Shutdown(); 19 | } 20 | 21 | /// 22 | /// Test that exception is raised in context manager that ignores it. 23 | /// 24 | [Test] 25 | public void TestWithPositive() 26 | { 27 | var locals = new PyDict(); 28 | 29 | PythonEngine.Exec(@" 30 | class CmTest: 31 | def __enter__(self): 32 | return self 33 | def __exit__(self, t, v, tb): 34 | # Exception not handled, return will be False 35 | pass 36 | def fail(self): 37 | return 5 / 0 38 | 39 | a = CmTest() 40 | ", null, locals); 41 | 42 | var a = locals.GetItem("a"); 43 | 44 | try 45 | { 46 | Py.With(a, cmTest => 47 | { 48 | cmTest.fail(); 49 | }); 50 | } 51 | catch (PythonException e) 52 | { 53 | TestContext.Out.WriteLine(e.Message); 54 | Assert.IsTrue(e.Type.Name == "ZeroDivisionError"); 55 | } 56 | } 57 | 58 | 59 | /// 60 | /// Test that exception is not raised in context manager that handles it 61 | /// 62 | [Test] 63 | public void TestWithNegative() 64 | { 65 | var locals = new PyDict(); 66 | 67 | PythonEngine.Exec(@" 68 | class CmTest: 69 | def __enter__(self): 70 | print('Enter') 71 | return self 72 | def __exit__(self, t, v, tb): 73 | # Signal exception is handled by returning true 74 | return True 75 | def fail(self): 76 | return 5 / 0 77 | 78 | a = CmTest() 79 | ", null, locals); 80 | 81 | var a = locals.GetItem("a"); 82 | Py.With(a, cmTest => 83 | { 84 | cmTest.fail(); 85 | }); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/embed_tests/fixtures/PyImportTest/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /src/embed_tests/fixtures/PyImportTest/cast_global_var.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | FOO = 1 4 | 5 | 6 | def test_foo(): 7 | return FOO 8 | -------------------------------------------------------------------------------- /src/embed_tests/fixtures/PyImportTest/sysargv.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | # if argv is available, as expected, then no exception 5 | num_args = len(sys.argv) 6 | -------------------------------------------------------------------------------- /src/embed_tests/fixtures/PyImportTest/test/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /src/embed_tests/fixtures/PyImportTest/test/one.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /src/embed_tests/pyrunstring.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Python.Runtime; 4 | 5 | namespace Python.EmbeddingTest 6 | { 7 | public class RunStringTest 8 | { 9 | [OneTimeSetUp] 10 | public void SetUp() 11 | { 12 | PythonEngine.Initialize(); 13 | } 14 | 15 | [OneTimeTearDown] 16 | public void Dispose() 17 | { 18 | PythonEngine.Shutdown(); 19 | } 20 | 21 | [Test] 22 | public void TestRunSimpleString() 23 | { 24 | int aa = PythonEngine.RunSimpleString("import sys"); 25 | Assert.AreEqual(0, aa); 26 | 27 | int bb = PythonEngine.RunSimpleString("import 1234"); 28 | Assert.AreEqual(-1, bb); 29 | } 30 | 31 | [Test] 32 | public void TestEval() 33 | { 34 | dynamic sys = Py.Import("sys"); 35 | sys.attr1 = 100; 36 | var locals = new PyDict(); 37 | locals.SetItem("sys", sys); 38 | locals.SetItem("a", new PyInt(10)); 39 | 40 | object b = PythonEngine.Eval("sys.attr1 + a + 1", null, locals) 41 | .AsManagedObject(typeof(int)); 42 | Assert.AreEqual(111, b); 43 | } 44 | 45 | [Test] 46 | public void TestExec() 47 | { 48 | dynamic sys = Py.Import("sys"); 49 | sys.attr1 = 100; 50 | var locals = new PyDict(); 51 | locals.SetItem("sys", sys); 52 | locals.SetItem("a", new PyInt(10)); 53 | 54 | PythonEngine.Exec("c = sys.attr1 + a + 1", null, locals); 55 | object c = locals.GetItem("c").AsManagedObject(typeof(int)); 56 | Assert.AreEqual(111, c); 57 | } 58 | 59 | [Test] 60 | public void TestExec2() 61 | { 62 | string code = @" 63 | class Test1(): 64 | pass 65 | 66 | class Test2(): 67 | def __init__(self): 68 | Test1() 69 | 70 | Test2()"; 71 | PythonEngine.Exec(code); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/perf_tests/BaselineComparisonConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | 6 | using BenchmarkDotNet.Configs; 7 | using BenchmarkDotNet.Jobs; 8 | 9 | using Perfolizer.Horology; 10 | 11 | namespace Python.PerformanceTests 12 | { 13 | public class BaselineComparisonConfig : ManualConfig 14 | { 15 | public const string EnvironmentVariableName = "PythonRuntimeDLL"; 16 | 17 | public BaselineComparisonConfig() 18 | { 19 | this.Options |= ConfigOptions.DisableOptimizationsValidator; 20 | 21 | string deploymentRoot = BenchmarkTests.DeploymentRoot; 22 | 23 | var baseJob = Job.Default 24 | .WithLaunchCount(1) 25 | .WithWarmupCount(3) 26 | .WithMaxIterationCount(100) 27 | .WithIterationTime(TimeInterval.FromMilliseconds(100)); 28 | this.Add(baseJob 29 | .WithId("baseline") 30 | .WithEnvironmentVariable(EnvironmentVariableName, 31 | Path.Combine(deploymentRoot, "baseline", "Python.Runtime.dll")) 32 | .WithBaseline(true)); 33 | this.Add(baseJob 34 | .WithId("new") 35 | .WithEnvironmentVariable(EnvironmentVariableName, 36 | Path.Combine(deploymentRoot, "new", "Python.Runtime.dll"))); 37 | } 38 | 39 | static BaselineComparisonConfig() { 40 | AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve; 41 | } 42 | 43 | static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) { 44 | Console.WriteLine(args.Name); 45 | if (!args.Name.StartsWith("Python.Runtime")) 46 | return null; 47 | string pythonRuntimeDll = Environment.GetEnvironmentVariable(EnvironmentVariableName); 48 | if (string.IsNullOrEmpty(pythonRuntimeDll)) 49 | pythonRuntimeDll = Path.Combine(BenchmarkTests.DeploymentRoot, "baseline", "Python.Runtime.dll"); 50 | return Assembly.LoadFrom(pythonRuntimeDll); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/perf_tests/Python.PerformanceTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472 5 | false 6 | x64 7 | x64 8 | 9 | 10 | 11 | 12 | 13 | PreserveNewest 14 | 15 | 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 23 | 24 | 25 | 26 | all 27 | runtime; build; native; contentfiles; analyzers; buildtransitive 28 | 29 | 30 | 31 | all 32 | runtime; build; native; contentfiles; analyzers; buildtransitive 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/perf_tests/PythonCallingNetBenchmark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | 6 | using BenchmarkDotNet.Attributes; 7 | using Python.Runtime; 8 | 9 | namespace Python.PerformanceTests 10 | { 11 | [Config(typeof(BaselineComparisonConfig))] 12 | public class PythonCallingNetBenchmark: BaselineComparisonBenchmarkBase 13 | { 14 | [Benchmark] 15 | public void ReadInt64Property() 16 | { 17 | using (Py.GIL()) 18 | { 19 | var locals = new PyDict(); 20 | locals.SetItem("a", new NetObject().ToPython()); 21 | Exec($@" 22 | s = 0 23 | for i in range(50000): 24 | s += a.{nameof(NetObject.LongProperty)} 25 | ", locals: locals); 26 | } 27 | } 28 | 29 | [Benchmark] 30 | public void WriteInt64Property() { 31 | using (Py.GIL()) { 32 | var locals = new PyDict(); 33 | locals.SetItem("a", new NetObject().ToPython()); 34 | Exec($@" 35 | s = 0 36 | for i in range(50000): 37 | a.{nameof(NetObject.LongProperty)} += i 38 | ", locals: locals); 39 | } 40 | } 41 | 42 | static void Exec(string code, PyDict locals) 43 | { 44 | MethodInfo exec = typeof(PythonEngine).GetMethod(nameof(PythonEngine.Exec)); 45 | object localsArg = typeof(PyObject).Assembly.GetName().Version.Major >= 3 46 | ? locals : locals.Handle; 47 | exec.Invoke(null, new[] 48 | { 49 | code, localsArg, null 50 | }); 51 | } 52 | } 53 | 54 | class NetObject 55 | { 56 | public long LongProperty { get; set; } = 42; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/perf_tests/baseline/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-Technologies/pythonnet/37c442666046d941af912d005c96c7a0aef9dd49/src/perf_tests/baseline/.gitkeep -------------------------------------------------------------------------------- /src/python_tests_runner/Python.PythonTestsRunner.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472;net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 1.0.0 21 | all 22 | runtime; build; native; contentfiles; analyzers 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/pythonnet.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-Technologies/pythonnet/37c442666046d941af912d005c96c7a0aef9dd49/src/pythonnet.snk -------------------------------------------------------------------------------- /src/runtime/Codecs/EnumPyIntCodec.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime.Codecs 4 | { 5 | [Obsolete] 6 | public sealed class EnumPyIntCodec : IPyObjectEncoder, IPyObjectDecoder 7 | { 8 | public static EnumPyIntCodec Instance { get; } = new EnumPyIntCodec(); 9 | 10 | public bool CanDecode(PyType objectType, Type targetType) 11 | { 12 | return targetType.IsEnum 13 | && objectType.IsSubclass(Runtime.PyLongType); 14 | } 15 | 16 | public bool CanEncode(Type type) 17 | { 18 | return type == typeof(object) || type == typeof(ValueType) || type.IsEnum; 19 | } 20 | 21 | public bool TryDecode(PyObject pyObj, out T? value) 22 | { 23 | value = default; 24 | if (!typeof(T).IsEnum) return false; 25 | 26 | Type etype = Enum.GetUnderlyingType(typeof(T)); 27 | 28 | if (!PyInt.IsIntType(pyObj)) return false; 29 | 30 | object? result; 31 | try 32 | { 33 | result = pyObj.AsManagedObject(etype); 34 | } 35 | catch (InvalidCastException) 36 | { 37 | return false; 38 | } 39 | 40 | if (Enum.IsDefined(typeof(T), result) || typeof(T).IsFlagsEnum()) 41 | { 42 | value = (T)Enum.ToObject(typeof(T), result); 43 | return true; 44 | } 45 | 46 | return false; 47 | } 48 | 49 | public PyObject? TryEncode(object value) 50 | { 51 | if (value is null) return null; 52 | 53 | var enumType = value.GetType(); 54 | if (!enumType.IsEnum) return null; 55 | 56 | try 57 | { 58 | return new PyInt(Convert.ToInt64(value)); 59 | } 60 | catch (OverflowException) 61 | { 62 | return new PyInt(Convert.ToUInt64(value)); 63 | } 64 | } 65 | 66 | private EnumPyIntCodec() { } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/runtime/Codecs/IPyObjectDecoder.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime; 2 | 3 | using System; 4 | 5 | /// 6 | /// Defines conversion to CLR types (unmarshalling) 7 | /// 8 | public interface IPyObjectDecoder 9 | { 10 | /// 11 | /// Checks if this decoder can decode from to 12 | /// 13 | bool CanDecode(PyType objectType, Type targetType); 14 | /// 15 | /// Attempts do decode into a variable of specified type 16 | /// 17 | /// CLR type to decode into 18 | /// Object to decode 19 | /// The variable, that will receive decoding result 20 | /// 21 | bool TryDecode(PyObject pyObj, out T? value); 22 | } 23 | -------------------------------------------------------------------------------- /src/runtime/Codecs/IPyObjectEncoder.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime; 2 | 3 | using System; 4 | 5 | /// 6 | /// Defines conversion from CLR objects into Python objects (e.g. ) (marshalling) 7 | /// 8 | public interface IPyObjectEncoder 9 | { 10 | /// 11 | /// Checks if encoder can encode CLR objects of specified type 12 | /// 13 | bool CanEncode(Type type); 14 | /// 15 | /// Attempts to encode CLR object into Python object 16 | /// 17 | PyObject? TryEncode(object value); 18 | } 19 | -------------------------------------------------------------------------------- /src/runtime/Codecs/IterableDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime.Codecs 5 | { 6 | public class IterableDecoder : IPyObjectDecoder 7 | { 8 | internal static bool IsIterable(Type targetType) 9 | { 10 | //if it is a plain IEnumerable, we can decode it using sequence protocol. 11 | if (targetType == typeof(System.Collections.IEnumerable)) 12 | return true; 13 | 14 | if (!targetType.IsGenericType) 15 | return false; 16 | 17 | return targetType.GetGenericTypeDefinition() == typeof(IEnumerable<>); 18 | } 19 | 20 | internal static bool IsIterable(PyType objectType) 21 | { 22 | return objectType.HasAttr("__iter__"); 23 | } 24 | 25 | public bool CanDecode(PyType objectType, Type targetType) 26 | { 27 | return IsIterable(objectType) && IsIterable(targetType); 28 | } 29 | 30 | public bool TryDecode(PyObject pyObj, out T value) 31 | { 32 | //first see if T is a plan IEnumerable 33 | if (typeof(T) == typeof(System.Collections.IEnumerable)) 34 | { 35 | object enumerable = new CollectionWrappers.IterableWrapper(pyObj); 36 | value = (T)enumerable; 37 | return true; 38 | } 39 | 40 | var elementType = typeof(T).GetGenericArguments()[0]; 41 | var collectionType = typeof(CollectionWrappers.IterableWrapper<>).MakeGenericType(elementType); 42 | 43 | var instance = Activator.CreateInstance(collectionType, new[] { pyObj }); 44 | value = (T)instance; 45 | return true; 46 | } 47 | 48 | public static IterableDecoder Instance { get; } = new IterableDecoder(); 49 | 50 | public static void Register() 51 | { 52 | PyObjectConversions.RegisterDecoder(Instance); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/runtime/Codecs/ListDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime.Codecs 5 | { 6 | public class ListDecoder : IPyObjectDecoder 7 | { 8 | private static bool IsList(Type targetType) 9 | { 10 | if (!targetType.IsGenericType) 11 | return false; 12 | 13 | return targetType.GetGenericTypeDefinition() == typeof(IList<>); 14 | } 15 | 16 | private static bool IsList(PyType objectType) 17 | { 18 | //TODO accept any python object that implements the sequence and list protocols 19 | //must implement sequence protocol to fully implement list protocol 20 | //if (!SequenceDecoder.IsSequence(objectType)) return false; 21 | 22 | //returns wheter the type is a list. 23 | return PythonReferenceComparer.Instance.Equals(objectType, Runtime.PyListType); 24 | } 25 | 26 | public bool CanDecode(PyType objectType, Type targetType) 27 | { 28 | return IsList(objectType) && IsList(targetType); 29 | } 30 | 31 | public bool TryDecode(PyObject pyObj, out T value) 32 | { 33 | if (pyObj == null) throw new ArgumentNullException(nameof(pyObj)); 34 | 35 | var elementType = typeof(T).GetGenericArguments()[0]; 36 | Type collectionType = typeof(CollectionWrappers.ListWrapper<>).MakeGenericType(elementType); 37 | 38 | var instance = Activator.CreateInstance(collectionType, new[] { pyObj }); 39 | value = (T)instance; 40 | return true; 41 | } 42 | 43 | public static ListDecoder Instance { get; } = new ListDecoder(); 44 | 45 | public static void Register() 46 | { 47 | PyObjectConversions.RegisterDecoder(Instance); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/runtime/Codecs/RawProxyEncoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime.Codecs 4 | { 5 | /// 6 | /// A .NET object encoder, that returns raw proxies (e.g. no conversion to Python types). 7 | /// You must inherit from this class and override . 8 | /// 9 | public class RawProxyEncoder: IPyObjectEncoder 10 | { 11 | public PyObject TryEncode(object value) 12 | { 13 | if (value is null) throw new ArgumentNullException(nameof(value)); 14 | 15 | return PyObject.FromManagedObject(value); 16 | } 17 | 18 | public virtual bool CanEncode(Type type) => false; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/runtime/Codecs/SequenceDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime.Codecs 5 | { 6 | public class SequenceDecoder : IPyObjectDecoder 7 | { 8 | internal static bool IsSequence(Type targetType) 9 | { 10 | if (!targetType.IsGenericType) 11 | return false; 12 | 13 | return targetType.GetGenericTypeDefinition() == typeof(ICollection<>); 14 | } 15 | 16 | internal static bool IsSequence(PyType objectType) 17 | { 18 | //must implement iterable protocol to fully implement sequence protocol 19 | if (!IterableDecoder.IsIterable(objectType)) return false; 20 | 21 | //returns wheter it implements the sequence protocol 22 | //according to python doc this needs to exclude dict subclasses 23 | //but I don't know how to look for that given the objectType 24 | //rather than the instance. 25 | return objectType.HasAttr("__getitem__"); 26 | } 27 | 28 | public bool CanDecode(PyType objectType, Type targetType) 29 | { 30 | return IsSequence(objectType) && IsSequence(targetType); 31 | } 32 | 33 | public bool TryDecode(PyObject pyObj, out T value) 34 | { 35 | if (pyObj == null) throw new ArgumentNullException(nameof(pyObj)); 36 | 37 | var elementType = typeof(T).GetGenericArguments()[0]; 38 | Type collectionType = typeof(CollectionWrappers.SequenceWrapper<>).MakeGenericType(elementType); 39 | 40 | var instance = Activator.CreateInstance(collectionType, new[] { pyObj }); 41 | value = (T)instance; 42 | return true; 43 | } 44 | 45 | public static SequenceDecoder Instance { get; } = new SequenceDecoder(); 46 | 47 | public static void Register() 48 | { 49 | PyObjectConversions.RegisterDecoder(Instance); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/runtime/CollectionWrappers/IterableWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections; 4 | 5 | namespace Python.Runtime.CollectionWrappers 6 | { 7 | internal class IterableWrapper : IEnumerable 8 | { 9 | protected readonly PyObject pyObject; 10 | 11 | public IterableWrapper(PyObject pyObj) 12 | { 13 | if (pyObj == null) 14 | throw new ArgumentNullException(); 15 | pyObject = new PyObject(pyObj.Reference); 16 | } 17 | 18 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 19 | 20 | public IEnumerator GetEnumerator() 21 | { 22 | PyIter iterObject; 23 | using (Py.GIL()) 24 | { 25 | iterObject = PyIter.GetIter(pyObject); 26 | } 27 | 28 | using var _ = iterObject; 29 | while (true) 30 | { 31 | using var GIL = Py.GIL(); 32 | 33 | if (!iterObject.MoveNext()) 34 | { 35 | iterObject.Dispose(); 36 | break; 37 | } 38 | yield return iterObject.Current.As()!; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/runtime/CollectionWrappers/ListWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime.CollectionWrappers 5 | { 6 | internal class ListWrapper : SequenceWrapper, IList 7 | { 8 | public ListWrapper(PyObject pyObj) : base(pyObj) 9 | { 10 | 11 | } 12 | 13 | public T this[int index] 14 | { 15 | get 16 | { 17 | var item = Runtime.PyList_GetItem(pyObject, index); 18 | var pyItem = new PyObject(item); 19 | return pyItem.As()!; 20 | } 21 | set 22 | { 23 | var pyItem = value.ToPython(); 24 | var result = Runtime.PyList_SetItem(pyObject, index, new NewReference(pyItem).Steal()); 25 | if (result == -1) 26 | Runtime.CheckExceptionOccurred(); 27 | } 28 | } 29 | 30 | public int IndexOf(T item) 31 | { 32 | return indexOf(item); 33 | } 34 | 35 | public void Insert(int index, T item) 36 | { 37 | if (IsReadOnly) 38 | throw new InvalidOperationException("Collection is read-only"); 39 | 40 | var pyItem = item.ToPython(); 41 | 42 | int result = Runtime.PyList_Insert(pyObject, index, pyItem); 43 | if (result == -1) 44 | Runtime.CheckExceptionOccurred(); 45 | } 46 | 47 | public void RemoveAt(int index) 48 | { 49 | var result = removeAt(index); 50 | 51 | //PySequence_DelItem will set an error if it fails. throw it here 52 | //since RemoveAt does not have a bool return value. 53 | if (result == false) 54 | Runtime.CheckExceptionOccurred(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/runtime/DefaultBaseTypeProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime 5 | { 6 | /// Minimal Python base type provider 7 | public sealed class DefaultBaseTypeProvider : IPythonBaseTypeProvider 8 | { 9 | public IEnumerable GetBaseTypes(Type type, IList existingBases) 10 | { 11 | if (type is null) 12 | throw new ArgumentNullException(nameof(type)); 13 | if (existingBases is null) 14 | throw new ArgumentNullException(nameof(existingBases)); 15 | if (existingBases.Count > 0) 16 | throw new ArgumentException("To avoid confusion, this type provider requires the initial set of base types to be empty"); 17 | 18 | return new[] { new PyType(GetBaseType(type)) }; 19 | } 20 | 21 | static BorrowedReference GetBaseType(Type type) 22 | { 23 | if (type == typeof(Exception)) 24 | return Exceptions.Exception; 25 | 26 | return type.BaseType is not null 27 | ? ClassManager.GetClass(type.BaseType) 28 | : Runtime.PyBaseObjectType; 29 | } 30 | 31 | DefaultBaseTypeProvider(){} 32 | public static DefaultBaseTypeProvider Instance { get; } = new DefaultBaseTypeProvider(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/runtime/IPythonBaseTypeProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime 5 | { 6 | public interface IPythonBaseTypeProvider 7 | { 8 | /// 9 | /// Get Python types, that should be presented to Python as the base types 10 | /// for the specified .NET type. 11 | /// 12 | IEnumerable GetBaseTypes(Type type, IList existingBases); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/runtime/Interfaces.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime 4 | { 5 | /// 6 | /// xxx 7 | /// 8 | internal interface IReflectedType 9 | { 10 | string PythonTypeName(); 11 | Type GetReflectedType(); 12 | } 13 | 14 | internal interface IReflectedClass : IReflectedType 15 | { 16 | bool IsException(); 17 | } 18 | 19 | internal interface IReflectedInterface : IReflectedType 20 | { 21 | } 22 | 23 | internal interface IReflectedArray : IReflectedType 24 | { 25 | } 26 | 27 | internal interface IReflectedGenericClass : IReflectedClass 28 | { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/runtime/InteropConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | using Python.Runtime.Mixins; 8 | 9 | public sealed class InteropConfiguration: IDisposable 10 | { 11 | internal readonly PythonBaseTypeProviderGroup pythonBaseTypeProviders 12 | = new(); 13 | 14 | /// Enables replacing base types of CLR types as seen from Python 15 | public IList PythonBaseTypeProviders => this.pythonBaseTypeProviders; 16 | 17 | public static InteropConfiguration MakeDefault() 18 | { 19 | return new InteropConfiguration 20 | { 21 | PythonBaseTypeProviders = 22 | { 23 | DefaultBaseTypeProvider.Instance, 24 | new CollectionMixinsProvider(new Lazy(() => Py.Import("clr._extras.collections"))), 25 | }, 26 | }; 27 | } 28 | 29 | public void Dispose() 30 | { 31 | foreach (var provider in PythonBaseTypeProviders.OfType()) 32 | { 33 | provider.Dispose(); 34 | } 35 | PythonBaseTypeProviders.Clear(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/runtime/Loader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace Python.Runtime 5 | { 6 | using static Runtime; 7 | 8 | [Obsolete("Only to be used from within Python")] 9 | static class Loader 10 | { 11 | public unsafe static int Initialize(IntPtr data, int size) 12 | { 13 | try 14 | { 15 | var dllPath = Encoding.UTF8.GetString((byte*)data.ToPointer(), size); 16 | 17 | if (!string.IsNullOrEmpty(dllPath)) 18 | { 19 | PythonDLL = dllPath; 20 | } 21 | else 22 | { 23 | PythonDLL = null; 24 | } 25 | 26 | using var _ = Py.GIL(); 27 | PythonEngine.InitExt(); 28 | } 29 | catch (Exception exc) 30 | { 31 | Console.Error.Write( 32 | $"Failed to initialize pythonnet: {exc}\n{exc.StackTrace}" 33 | ); 34 | return 1; 35 | } 36 | 37 | return 0; 38 | } 39 | 40 | public unsafe static int Shutdown(IntPtr data, int size) 41 | { 42 | try 43 | { 44 | var command = Encoding.UTF8.GetString((byte*)data.ToPointer(), size); 45 | 46 | if (command == "full_shutdown") 47 | { 48 | using var _ = Py.GIL(); 49 | PythonEngine.Shutdown(); 50 | } 51 | } 52 | catch (Exception exc) 53 | { 54 | Console.Error.Write( 55 | $"Failed to shutdown pythonnet: {exc}\n{exc.StackTrace}" 56 | ); 57 | return 1; 58 | } 59 | 60 | return 0; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/runtime/Native/ABI.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime.Native 2 | { 3 | using System; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | static class ABI 9 | { 10 | public static int RefCountOffset { get; } = GetRefCountOffset(); 11 | public static int ObjectHeadOffset => RefCountOffset; 12 | 13 | internal static void Initialize(Version version) 14 | { 15 | string offsetsClassSuffix = string.Format(CultureInfo.InvariantCulture, 16 | "{0}{1}", version.Major, version.Minor); 17 | 18 | var thisAssembly = Assembly.GetExecutingAssembly(); 19 | 20 | const string nativeTypeOffsetClassName = "Python.Runtime.NativeTypeOffset"; 21 | string className = "Python.Runtime.TypeOffset" + offsetsClassSuffix; 22 | Type nativeOffsetsClass = thisAssembly.GetType(nativeTypeOffsetClassName, throwOnError: false); 23 | Type typeOffsetsClass = 24 | // Try platform native offsets first. It is only present when generated by setup.py 25 | nativeOffsetsClass ?? thisAssembly.GetType(className, throwOnError: false); 26 | if (typeOffsetsClass is null) 27 | { 28 | var types = thisAssembly.GetTypes().Select(type => type.Name).Where(name => name.StartsWith("TypeOffset")); 29 | string message = $"Searching for {className}, found {string.Join(",", types)}."; 30 | throw new NotSupportedException($"Python ABI v{version} is not supported: {message}"); 31 | } 32 | 33 | var typeOffsets = (ITypeOffsets)Activator.CreateInstance(typeOffsetsClass); 34 | TypeOffset.Use(typeOffsets, nativeOffsetsClass == null ? ObjectHeadOffset : 0); 35 | } 36 | 37 | static unsafe int GetRefCountOffset() 38 | { 39 | using var tempObject = Runtime.PyList_New(0); 40 | IntPtr* tempPtr = (IntPtr*)tempObject.DangerousGetAddress(); 41 | int offset = 0; 42 | while(tempPtr[offset] != (IntPtr)1) 43 | { 44 | offset++; 45 | if (offset > 100) 46 | throw new InvalidProgramException("PyObject_HEAD could not be found withing reasonable distance from the start of PyObject"); 47 | } 48 | return offset * IntPtr.Size; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/runtime/Native/BorrowedReference.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | 6 | /// 7 | /// Represents a reference to a Python object, that is being lent, and 8 | /// can only be safely used until execution returns to the caller. 9 | /// 10 | readonly ref struct BorrowedReference 11 | { 12 | readonly IntPtr pointer; 13 | public bool IsNull => this.pointer == IntPtr.Zero; 14 | 15 | /// Gets a raw pointer to the Python object 16 | [DebuggerHidden] 17 | public IntPtr DangerousGetAddress() 18 | => this.IsNull ? throw new NullReferenceException() : this.pointer; 19 | /// Gets a raw pointer to the Python object 20 | public IntPtr DangerousGetAddressOrNull() => this.pointer; 21 | 22 | public static BorrowedReference Null => new(); 23 | 24 | /// 25 | /// Creates new instance of from raw pointer. Unsafe. 26 | /// 27 | public BorrowedReference(IntPtr pointer) 28 | { 29 | this.pointer = pointer; 30 | } 31 | 32 | public static bool operator ==(BorrowedReference a, BorrowedReference b) 33 | => a.pointer == b.pointer; 34 | public static bool operator !=(BorrowedReference a, BorrowedReference b) 35 | => a.pointer != b.pointer; 36 | public static bool operator ==(BorrowedReference reference, NullOnly? @null) 37 | => reference.IsNull; 38 | public static bool operator !=(BorrowedReference reference, NullOnly? @null) 39 | => !reference.IsNull; 40 | public static bool operator ==(NullOnly? @null, BorrowedReference reference) 41 | => reference.IsNull; 42 | public static bool operator !=(NullOnly? @null, BorrowedReference reference) 43 | => !reference.IsNull; 44 | 45 | public override bool Equals(object obj) { 46 | if (obj is IntPtr ptr) 47 | return ptr == pointer; 48 | 49 | return false; 50 | } 51 | 52 | public static implicit operator BorrowedReference(PyObject pyObject) => pyObject.Reference; 53 | public static implicit operator BorrowedReference(NullOnly? @null) => Null; 54 | 55 | public override int GetHashCode() => pointer.GetHashCode(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/runtime/Native/GeneratedTypeOffsets.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime.Native 2 | { 3 | using System; 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | [StructLayout(LayoutKind.Sequential)] 8 | abstract class GeneratedTypeOffsets 9 | { 10 | protected GeneratedTypeOffsets() 11 | { 12 | var type = this.GetType(); 13 | var offsetProperties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); 14 | int fieldSize = IntPtr.Size; 15 | for (int fieldIndex = 0; fieldIndex < offsetProperties.Length; fieldIndex++) 16 | { 17 | int offset = fieldIndex * fieldSize; 18 | offsetProperties[fieldIndex].SetValue(this, offset, index: null); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/runtime/Native/ITypeOffsets.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable InconsistentNaming 2 | // ReSharper disable IdentifierTypo 3 | namespace Python.Runtime.Native 4 | { 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | [SuppressMessage("Style", "IDE1006:Naming Styles", 8 | Justification = "Following CPython", 9 | Scope = "type")] 10 | interface ITypeOffsets 11 | { 12 | int bf_getbuffer { get; } 13 | int mp_ass_subscript { get; } 14 | int mp_length { get; } 15 | int mp_subscript { get; } 16 | int name { get; } 17 | int nb_positive { get; } 18 | int nb_negative { get; } 19 | int nb_add { get; } 20 | int nb_subtract { get; } 21 | int nb_multiply { get; } 22 | int nb_true_divide { get; } 23 | int nb_and { get; } 24 | int nb_int { get; } 25 | int nb_or { get; } 26 | int nb_xor { get; } 27 | int nb_lshift { get; } 28 | int nb_rshift { get; } 29 | int nb_remainder { get; } 30 | int nb_invert { get; } 31 | int nb_inplace_add { get; } 32 | int nb_inplace_subtract { get; } 33 | int ob_size { get; } 34 | int ob_type { get; } 35 | int qualname { get; } 36 | int sq_contains { get; } 37 | int sq_length { get; } 38 | int tp_alloc { get; } 39 | int tp_as_buffer { get; } 40 | int tp_as_mapping { get; } 41 | int tp_as_number { get; } 42 | int tp_as_sequence { get; } 43 | int tp_base { get; } 44 | int tp_bases { get; } 45 | int tp_basicsize { get; } 46 | int tp_call { get; } 47 | int tp_clear { get; } 48 | int tp_dealloc { get; } 49 | int tp_descr_get { get; } 50 | int tp_descr_set { get; } 51 | int tp_dict { get; } 52 | int tp_dictoffset { get; } 53 | int tp_flags { get; } 54 | int tp_free { get; } 55 | int tp_getattro { get; } 56 | int tp_hash { get; } 57 | int tp_is_gc { get; } 58 | int tp_itemsize { get; } 59 | int tp_iter { get; } 60 | int tp_iternext { get; } 61 | int tp_methods { get; } 62 | int tp_mro { get; } 63 | int tp_name { get; } 64 | int tp_new { get; } 65 | int tp_repr { get; } 66 | int tp_richcompare { get; } 67 | int tp_weaklistoffset { get; } 68 | int tp_setattro { get; } 69 | int tp_str { get; } 70 | int tp_traverse { get; } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/runtime/Native/NativeCall.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime 4 | { 5 | /// 6 | /// Provides support for calling native code indirectly through 7 | /// function pointers. Most of the important parts of the Python 8 | /// C API can just be wrapped with p/invoke, but there are some 9 | /// situations (specifically, calling functions through Python 10 | /// type structures) where we need to call functions indirectly. 11 | /// 12 | internal unsafe class NativeCall 13 | { 14 | public static void CallDealloc(IntPtr fp, StolenReference a1) 15 | { 16 | var d = (delegate* unmanaged[Cdecl])fp; 17 | d(a1); 18 | } 19 | 20 | public static NewReference Call_3(IntPtr fp, BorrowedReference a1, BorrowedReference a2, BorrowedReference a3) 21 | { 22 | var d = (delegate* unmanaged[Cdecl])fp; 23 | return d(a1, a2, a3); 24 | } 25 | 26 | 27 | public static int Int_Call_3(IntPtr fp, BorrowedReference a1, BorrowedReference a2, BorrowedReference a3) 28 | { 29 | var d = (delegate* unmanaged[Cdecl])fp; 30 | return d(a1, a2, a3); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/runtime/Native/NativeFunc.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime.Native; 2 | 3 | /// Catch-all type for native function objects (to be pointed to) 4 | struct NativeFunc 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /src/runtime/Native/NativeTypeSpec.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace Python.Runtime.Native 6 | { 7 | [StructLayout(LayoutKind.Sequential)] 8 | struct NativeTypeSpec : IDisposable 9 | { 10 | public readonly StrPtr Name; 11 | public readonly int BasicSize; 12 | public readonly int ItemSize; 13 | public readonly int Flags; 14 | public IntPtr Slots; 15 | 16 | public NativeTypeSpec(TypeSpec spec) 17 | { 18 | if (spec is null) throw new ArgumentNullException(nameof(spec)); 19 | 20 | this.Name = new StrPtr(spec.Name, Encoding.UTF8); 21 | this.BasicSize = spec.BasicSize; 22 | this.ItemSize = spec.ItemSize; 23 | this.Flags = (int)spec.Flags; 24 | 25 | unsafe 26 | { 27 | int slotsBytes = checked((spec.Slots.Count + 1) * Marshal.SizeOf()); 28 | var slots = (TypeSpec.Slot*)Marshal.AllocHGlobal(slotsBytes); 29 | for (int slotIndex = 0; slotIndex < spec.Slots.Count; slotIndex++) 30 | slots[slotIndex] = spec.Slots[slotIndex]; 31 | slots[spec.Slots.Count] = default; 32 | this.Slots = (IntPtr)slots; 33 | } 34 | } 35 | 36 | public void Dispose() 37 | { 38 | // we have to leak the name 39 | // this.Name.Dispose(); 40 | Marshal.FreeHGlobal(this.Slots); 41 | this.Slots = IntPtr.Zero; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/runtime/Native/PyCompilerFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime.Native 4 | { 5 | [Flags] 6 | enum PyCompilerFlags 7 | { 8 | SOURCE_IS_UTF8 = 0x0100, 9 | DONT_IMPLY_DEDENT = 0x0200, 10 | ONLY_AST = 0x0400, 11 | IGNORE_COOKIE = 0x0800, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/runtime/Native/PyGILState.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime.Native; 2 | 3 | /// PyGILState_STATE 4 | enum PyGILState 5 | { 6 | PyGILState_LOCKED, 7 | PyGILState_UNLOCKED 8 | } 9 | -------------------------------------------------------------------------------- /src/runtime/Native/PyIdentifier_.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="true" hostSpecific="true" #> 2 | <#@ output extension=".cs" #> 3 | <# 4 | string[] internNames = new string[] 5 | { 6 | "__name__", 7 | "__dict__", 8 | "__doc__", 9 | "__class__", 10 | "__clear_reentry_guard__", 11 | "__module__", 12 | "__file__", 13 | "__slots__", 14 | "__self__", 15 | "__annotations__", 16 | 17 | "__init__", 18 | "__repr__", 19 | "__import__", 20 | "__builtins__", 21 | 22 | "builtins", 23 | 24 | "__overloads__", 25 | "Overloads", 26 | }; 27 | #> 28 | using System; 29 | 30 | namespace Python.Runtime 31 | { 32 | static class PyIdentifier 33 | { 34 | #pragma warning disable CS0649 // indentifier is never assigned to (assigned with reflection) 35 | <# 36 | foreach (var name in internNames) 37 | { 38 | #> 39 | static IntPtr f<#= name #>; 40 | public static BorrowedReference <#= name #> => new(f<#= name #>); 41 | <# 42 | } 43 | #> 44 | #pragma warning restore CS0649 // indentifier is never assigned to (assigned with reflection) 45 | } 46 | 47 | 48 | static partial class InternString 49 | { 50 | private static readonly string[] _builtinNames = new string[] 51 | { 52 | <# 53 | foreach (var name in internNames) 54 | { 55 | #> 56 | "<#= name #>", 57 | <# 58 | } 59 | #> 60 | }; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/runtime/Native/PyInterpreterState.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime.Native; 2 | 3 | struct PyInterpreterState 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/runtime/Native/PyMemberFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime.Native; 4 | 5 | [Flags] 6 | enum PyMemberFlags: int 7 | { 8 | None = 0, 9 | ReadOnly = 1, 10 | ReadRestricted = 2, 11 | WriteRestricted = 4, 12 | Restricted = (ReadRestricted | WriteRestricted), 13 | AuditRead = ReadRestricted, 14 | } 15 | -------------------------------------------------------------------------------- /src/runtime/Native/PyMemberType.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime.Native; 2 | 3 | enum PyMemberType: int 4 | { 5 | Short = 0, 6 | Int = 1, 7 | Long = 2, 8 | Float = 3, 9 | Double = 4, 10 | String = 5, 11 | Object = 6, 12 | /// 1-character string 13 | Char = 7, 14 | /// 8-bit signed int 15 | Byte = 8, 16 | 17 | UByte = 9, 18 | UShort = 10, 19 | UInt = 11, 20 | ULong = 12, 21 | 22 | StringInPlace = 13, 23 | 24 | /// bools contained in the structure (assumed char) 25 | Bool = 14, 26 | 27 | /// 28 | /// Like but raises AttributeError 29 | /// when the value is NULL, instead of converting to None 30 | /// 31 | ObjectEx = 16, 32 | 33 | LongLong = 17, 34 | ULongLong = 18, 35 | 36 | PySignedSizeT = 19, 37 | AlwaysNone = 20, 38 | } 39 | -------------------------------------------------------------------------------- /src/runtime/Native/PyMethodFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime.Native; 4 | 5 | [Flags] 6 | enum PyMethodFlags : int 7 | { 8 | [Obsolete] 9 | OLDARGS = 0, 10 | VarArgs = 1, 11 | Keywords = 2, 12 | NoArgs = 4, 13 | O = 8, 14 | 15 | Class = 0x10, 16 | Static = 0x20, 17 | 18 | /// 19 | /// Allows a method to be entered even though a slot has 20 | /// already filled the entry. When defined, the flag allows a separate 21 | /// method, "__contains__" for example, to coexist with a defined 22 | /// slot like sq_contains. 23 | /// 24 | Coexist = 0x40, 25 | 26 | /// 3.10+ 27 | FastCall = 0x80, 28 | 29 | /// 30 | /// The function stores an 31 | /// additional reference to the class that defines it; 32 | /// both self and class are passed to it. 33 | /// It uses PyCMethodObject instead of PyCFunctionObject. 34 | /// May not be combined with METH_NOARGS, METH_O, METH_CLASS or METH_STATIC. 35 | /// 36 | /// 3.9+ 37 | Method = 0x0200, 38 | } 39 | -------------------------------------------------------------------------------- /src/runtime/Native/PyThreadState.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime.Native; 2 | 3 | struct PyThreadState 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/runtime/Native/ReferenceExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime 2 | { 3 | using System.Diagnostics.Contracts; 4 | 5 | static class ReferenceExtensions 6 | { 7 | /// 8 | /// Checks if the reference points to Python object None. 9 | /// 10 | [Pure] 11 | public static bool IsNone(this in NewReference reference) 12 | => reference.BorrowNullable() == Runtime.PyNone; 13 | /// 14 | /// Checks if the reference points to Python object None. 15 | /// 16 | [Pure] 17 | public static bool IsNone(this BorrowedReference reference) 18 | => reference == Runtime.PyNone; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/runtime/Native/StrPtr.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace Python.Runtime.Native 6 | { 7 | [StructLayout(LayoutKind.Sequential)] 8 | struct StrPtr : IDisposable 9 | { 10 | public IntPtr RawPointer { get; set; } 11 | unsafe byte* Bytes => (byte*)this.RawPointer; 12 | 13 | public unsafe StrPtr(string value, Encoding encoding) 14 | { 15 | if (value is null) throw new ArgumentNullException(nameof(value)); 16 | if (encoding is null) throw new ArgumentNullException(nameof(encoding)); 17 | 18 | var bytes = encoding.GetBytes(value); 19 | this.RawPointer = Marshal.AllocHGlobal(checked(bytes.Length + 1)); 20 | try 21 | { 22 | Marshal.Copy(bytes, 0, this.RawPointer, bytes.Length); 23 | this.Bytes[bytes.Length] = 0; 24 | } 25 | catch 26 | { 27 | this.Dispose(); 28 | throw; 29 | } 30 | } 31 | 32 | public unsafe string? ToString(Encoding encoding) 33 | { 34 | if (encoding is null) throw new ArgumentNullException(nameof(encoding)); 35 | if (this.RawPointer == IntPtr.Zero) return null; 36 | 37 | return encoding.GetString((byte*)this.RawPointer, byteCount: checked((int)this.ByteCount)); 38 | } 39 | 40 | public unsafe nuint ByteCount 41 | { 42 | get 43 | { 44 | if (this.RawPointer == IntPtr.Zero) throw new NullReferenceException(); 45 | 46 | nuint zeroIndex = 0; 47 | while (this.Bytes[zeroIndex] != 0) 48 | { 49 | zeroIndex++; 50 | } 51 | return zeroIndex; 52 | } 53 | } 54 | 55 | public void Dispose() 56 | { 57 | if (this.RawPointer == IntPtr.Zero) 58 | return; 59 | 60 | Marshal.FreeHGlobal(this.RawPointer); 61 | this.RawPointer = IntPtr.Zero; 62 | } 63 | 64 | internal static Encoding GetEncodingByPythonName(string pyEncodingName) 65 | { 66 | // https://stackoverflow.com/a/7798749/231238 67 | if (pyEncodingName == "mbcs") return Encoding.Default; 68 | 69 | return Encoding.GetEncoding(pyEncodingName); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/runtime/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")] 4 | 5 | [assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")] -------------------------------------------------------------------------------- /src/runtime/PyExportAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime { 2 | using System; 3 | 4 | /// 5 | /// Controls visibility to Python for public .NET type or an entire assembly 6 | /// 7 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum 8 | | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Assembly, 9 | AllowMultiple = false, 10 | Inherited = false)] 11 | public class PyExportAttribute : Attribute 12 | { 13 | internal readonly bool Export; 14 | public PyExportAttribute(bool export) { this.Export = export; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/runtime/PythonBaseTypeProviderGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Python.Runtime 6 | { 7 | class PythonBaseTypeProviderGroup : List, IPythonBaseTypeProvider 8 | { 9 | public IEnumerable GetBaseTypes(Type type, IList existingBases) 10 | { 11 | if (type is null) 12 | throw new ArgumentNullException(nameof(type)); 13 | if (existingBases is null) 14 | throw new ArgumentNullException(nameof(existingBases)); 15 | 16 | foreach (var provider in this) 17 | { 18 | existingBases = provider.GetBaseTypes(type, existingBases).ToList(); 19 | } 20 | 21 | return existingBases; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/runtime/PythonTypes/PyIterable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Runtime.Serialization; 5 | 6 | namespace Python.Runtime 7 | { 8 | [Serializable] 9 | public class PyIterable : PyObject, IEnumerable 10 | { 11 | internal PyIterable(BorrowedReference reference) : base(reference) { } 12 | internal PyIterable(in StolenReference reference) : base(reference) { } 13 | protected PyIterable(SerializationInfo info, StreamingContext context) 14 | : base(info, context) { } 15 | 16 | /// 17 | /// Creates new instance from an existing object. 18 | /// 19 | /// This constructor does not check if is actually iterable. 20 | public PyIterable(PyObject o) : base(FromObject(o)) { } 21 | 22 | static BorrowedReference FromObject(PyObject o) 23 | { 24 | if (o is null) throw new ArgumentNullException(nameof(o)); 25 | return o.Reference; 26 | } 27 | 28 | /// 29 | /// Return a new PyIter object for the object. This allows any iterable 30 | /// python object to be iterated over in C#. A PythonException will be 31 | /// raised if the object is not iterable. 32 | /// 33 | public PyIter GetEnumerator() 34 | { 35 | return PyIter.GetIter(this); 36 | } 37 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 38 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/runtime/PythonTypes/PyNumber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Python.Runtime 5 | { 6 | /// 7 | /// Represents a generic Python number. The methods of this class are 8 | /// equivalent to the Python "abstract number API". See 9 | /// PY3: https://docs.python.org/3/c-api/number.html 10 | /// for details. 11 | /// 12 | /// 13 | /// TODO: add all of the PyNumber_XXX methods. 14 | /// 15 | public class PyNumber : PyObject 16 | { 17 | internal PyNumber(in StolenReference reference) : base(reference) { } 18 | internal PyNumber(BorrowedReference reference) : base(reference) { } 19 | protected PyNumber(SerializationInfo info, StreamingContext context) 20 | : base(info, context) { } 21 | 22 | /// 23 | /// IsNumberType Method 24 | /// 25 | /// 26 | /// Returns true if the given object is a Python numeric type. 27 | /// 28 | public static bool IsNumberType(PyObject value) 29 | { 30 | if (value is null) throw new ArgumentNullException(nameof(value)); 31 | 32 | return Runtime.PyNumber_Check(value.obj); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/runtime/PythonTypes/PyObject.IConvertible.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime; 4 | 5 | public partial class PyObject : IConvertible 6 | { 7 | public virtual TypeCode GetTypeCode() => TypeCode.Object; 8 | 9 | private T DoConvert() 10 | { 11 | using var _ = Py.GIL(); 12 | if (Converter.ToPrimitive(Reference, typeof(T), out object? result, setError: false)) 13 | { 14 | return (T)result!; 15 | } 16 | else 17 | { 18 | throw new InvalidCastException(); 19 | } 20 | } 21 | 22 | public bool ToBoolean(IFormatProvider provider) => DoConvert(); 23 | public byte ToByte(IFormatProvider provider) => DoConvert(); 24 | public char ToChar(IFormatProvider provider) => DoConvert(); 25 | public short ToInt16(IFormatProvider provider) => DoConvert(); 26 | public int ToInt32(IFormatProvider provider) => DoConvert(); 27 | public long ToInt64(IFormatProvider provider) => DoConvert(); 28 | public sbyte ToSByte(IFormatProvider provider) => DoConvert(); 29 | public ushort ToUInt16(IFormatProvider provider) => DoConvert(); 30 | public uint ToUInt32(IFormatProvider provider) => DoConvert(); 31 | public ulong ToUInt64(IFormatProvider provider) => DoConvert(); 32 | 33 | public float ToSingle(IFormatProvider provider) => DoConvert(); 34 | public double ToDouble(IFormatProvider provider) => DoConvert(); 35 | 36 | public string ToString(IFormatProvider provider) => DoConvert(); 37 | 38 | public DateTime ToDateTime(IFormatProvider provider) => throw new InvalidCastException(); 39 | public decimal ToDecimal(IFormatProvider provider) => throw new InvalidCastException(); 40 | 41 | public object ToType(Type conversionType, IFormatProvider provider) 42 | { 43 | if (Converter.ToManaged(Reference, conversionType, out object? result, setError: false)) 44 | { 45 | return result!; 46 | } 47 | else 48 | { 49 | throw new InvalidCastException(); 50 | } 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/runtime/PythonTypes/PyString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Python.Runtime 5 | { 6 | /// 7 | /// Represents a Python (ANSI) string object. See the documentation at 8 | /// PY2: https://docs.python.org/2/c-api/string.html 9 | /// PY3: No Equivalent 10 | /// for details. 11 | /// 12 | /// 13 | /// 2011-01-29: ...Then why does the string constructor call PyUnicode_FromUnicode()??? 14 | /// 15 | [Serializable] 16 | public class PyString : PySequence 17 | { 18 | internal PyString(in StolenReference reference) : base(reference) { } 19 | internal PyString(BorrowedReference reference) : base(reference) { } 20 | protected PyString(SerializationInfo info, StreamingContext context) : base(info, context) { } 21 | 22 | private static BorrowedReference FromObject(PyObject o) 23 | { 24 | if (o is null) throw new ArgumentNullException(nameof(o)); 25 | if (!IsStringType(o)) 26 | { 27 | throw new ArgumentException("object is not a string"); 28 | } 29 | return o.Reference; 30 | } 31 | 32 | /// 33 | /// PyString Constructor 34 | /// 35 | /// 36 | /// Copy constructor - obtain a PyString from a generic PyObject. 37 | /// An ArgumentException will be thrown if the given object is not 38 | /// a Python string object. 39 | /// 40 | public PyString(PyObject o) : base(FromObject(o)) 41 | { 42 | } 43 | 44 | /// 45 | /// PyString Constructor 46 | /// 47 | /// 48 | /// Creates a Python string from a managed string. 49 | /// 50 | public PyString(string s) : base(Runtime.PyString_FromString(s).StealOrThrow()) 51 | { 52 | } 53 | 54 | 55 | /// 56 | /// Returns true if the given object is a Python string. 57 | /// 58 | public static bool IsStringType(PyObject value) 59 | { 60 | return Runtime.PyString_Check(value.obj); 61 | } 62 | 63 | public override TypeCode GetTypeCode() => TypeCode.String; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/runtime/README.md: -------------------------------------------------------------------------------- 1 | `pythonnet` is a package that gives .NET programmers ability to 2 | integrate Python engine and use Python libraries. 3 | 4 | ## Embedding Python in .NET 5 | 6 | - You must set `Runtime.PythonDLL` property or `PYTHONNET_PYDLL` environment variable, 7 | otherwise you will receive `BadPythonDllException` 8 | (internal, derived from `MissingMethodException`) upon calling `Initialize`. 9 | Typical values are `python38.dll` (Windows), `libpython3.8.dylib` (Mac), 10 | `libpython3.8.so` (most other *nix). Full path may be required. 11 | - All calls to Python should be inside a 12 | `using (Py.GIL()) {/* Your code here */}` block. 13 | - Import python modules using `dynamic mod = Py.Import("mod")`, then 14 | you can call functions as normal, eg `mod.func(args)`. 15 | You can also access Python objects via `PyObject` and dervied types 16 | instead of using `dynamic`. 17 | - Use `mod.func(args, Py.kw("keywordargname", keywordargvalue))` or 18 | `mod.func(args, keywordargname: keywordargvalue)` to apply keyword 19 | arguments. 20 | - Mathematical operations involving python and literal/managed types 21 | must have the python object first, eg. `np.pi * 2` works, 22 | `2 * np.pi` doesn't. 23 | 24 | ## Example 25 | 26 | ```csharp 27 | using var _ = Py.GIL(); 28 | 29 | dynamic np = Py.Import("numpy"); 30 | Console.WriteLine(np.cos(np.pi * 2)); 31 | 32 | dynamic sin = np.sin; 33 | Console.WriteLine(sin(5)); 34 | 35 | double c = (double)(np.cos(5) + sin(5)); 36 | Console.WriteLine(c); 37 | 38 | dynamic a = np.array(new List { 1, 2, 3 }); 39 | Console.WriteLine(a.dtype); 40 | 41 | dynamic b = np.array(new List { 6, 5, 4 }, dtype: np.int32); 42 | Console.WriteLine(b.dtype); 43 | 44 | Console.WriteLine(a * b); 45 | Console.ReadKey(); 46 | ``` 47 | 48 | Output: 49 | 50 | ``` 51 | 1.0 52 | -0.958924274663 53 | -0.6752620892 54 | float64 55 | int32 56 | [ 6. 10. 12.] 57 | ``` 58 | 59 | 60 | 61 | ## Resources 62 | 63 | Information on installation, FAQ, troubleshooting, debugging, and 64 | projects using pythonnet can be found in the Wiki: 65 | 66 | https://github.com/pythonnet/pythonnet/wiki 67 | 68 | Mailing list 69 | https://mail.python.org/mailman/listinfo/pythondotnet 70 | Chat 71 | https://gitter.im/pythonnet/pythonnet 72 | 73 | ### .NET Foundation 74 | 75 | This project is supported by the [.NET Foundation](https://dotnetfoundation.org). 76 | -------------------------------------------------------------------------------- /src/runtime/Resources/interop.py: -------------------------------------------------------------------------------- 1 | _UNSET = object() 2 | 3 | class PyErr: 4 | def __init__(self, type=_UNSET, value=_UNSET, traceback=_UNSET): 5 | if not(type is _UNSET): 6 | self.type = type 7 | if not(value is _UNSET): 8 | self.value = value 9 | if not(traceback is _UNSET): 10 | self.traceback = traceback 11 | -------------------------------------------------------------------------------- /src/runtime/RuntimeState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | 5 | using static Python.Runtime.Runtime; 6 | 7 | namespace Python.Runtime 8 | { 9 | class RuntimeState 10 | { 11 | public static void Save() 12 | { 13 | if (!PySys_GetObject("initial_modules").IsNull) 14 | { 15 | throw new Exception("Runtime State set already"); 16 | } 17 | 18 | using var modules = PySet_New(default); 19 | int res = PySys_SetObject("initial_modules", modules.Borrow()); 20 | PythonException.ThrowIfIsNotZero(res); 21 | 22 | foreach (var name in GetModuleNames()) 23 | { 24 | res = PySet_Add(modules.Borrow(), new BorrowedReference(name)); 25 | PythonException.ThrowIfIsNotZero(res); 26 | } 27 | } 28 | 29 | public static void Restore() 30 | { 31 | RestoreModules(); 32 | } 33 | 34 | private static void RestoreModules() 35 | { 36 | var intialModules = PySys_GetObject("initial_modules"); 37 | Debug.Assert(!intialModules.IsNull); 38 | var modules = PyImport_GetModuleDict(); 39 | foreach (var nameRaw in GetModuleNames()) 40 | { 41 | var name = new BorrowedReference(nameRaw); 42 | if (PySet_Contains(intialModules, name) == 1) 43 | { 44 | continue; 45 | } 46 | if (PyDict_DelItem(modules, name) != 0) 47 | { 48 | PyErr_Print(); 49 | } 50 | } 51 | } 52 | 53 | public static IEnumerable GetModuleNames() 54 | { 55 | var modules = PyImport_GetModuleDict(); 56 | using var names = PyDict_Keys(modules); 57 | nint length = PyList_Size(names.BorrowOrThrow()); 58 | if (length < 0) throw PythonException.ThrowLastAsClrException(); 59 | var result = new IntPtr[length]; 60 | for (int i = 0; i < length; i++) 61 | { 62 | result[i] = PyList_GetItem(names.Borrow(), i).DangerousGetAddress(); 63 | } 64 | return result; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/CLRMappedItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Python.Runtime; 4 | 5 | public class CLRMappedItem 6 | { 7 | public object Instance { get; private set; } 8 | public List PyRefs { get; set; } = new List(); 9 | public bool Stored { get; set; } 10 | 11 | public CLRMappedItem(object instance) 12 | { 13 | Instance = instance; 14 | } 15 | 16 | internal void AddRef(PyObject pyRef) 17 | { 18 | this.PyRefs.Add(pyRef); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/CLRWrapperCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace Python.Runtime; 5 | 6 | public class CLRWrapperCollection : KeyedCollection 7 | { 8 | public bool TryGetValue(object key, [NotNullWhen(true)] out CLRMappedItem? value) 9 | { 10 | if (Dictionary == null) 11 | { 12 | value = null; 13 | return false; 14 | } 15 | return Dictionary.TryGetValue(key, out value); 16 | } 17 | 18 | protected override object GetKeyForItem(CLRMappedItem item) 19 | { 20 | return item.Instance; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/ClassManagerState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime.StateSerialization; 5 | 6 | // Workaround for the lack of required properties: https://github.com/dotnet/csharplang/issues/3630 7 | // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. 8 | #pragma warning disable CS8618 9 | 10 | [Serializable] 11 | internal class ClassManagerState 12 | { 13 | public Dictionary> Contexts { get; set; } 14 | public Dictionary Cache { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/ICLRObjectStorer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Python.Runtime; 4 | 5 | public interface ICLRObjectStorer 6 | { 7 | ICollection Store(CLRWrapperCollection wrappers, Dictionary storage); 8 | CLRWrapperCollection Restore(Dictionary storage); 9 | } 10 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/ImportHookState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime.StateSerialization; 5 | 6 | // Workaround for the lack of required properties: https://github.com/dotnet/csharplang/issues/3630 7 | // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. 8 | #pragma warning disable CS8618 9 | 10 | [Serializable] 11 | internal class ImportHookState 12 | { 13 | public PyModule PyCLRModule { get; init; } 14 | public PyObject Root { get; init; } 15 | public Dictionary Modules { get; init; } 16 | } 17 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/MaybeType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.Serialization; 4 | using System.Runtime.Serialization.Formatters.Binary; 5 | using System.IO; 6 | 7 | namespace Python.Runtime 8 | { 9 | [Serializable] 10 | internal struct MaybeType : ISerializable 11 | { 12 | public static implicit operator MaybeType (Type ob) => new(ob); 13 | 14 | // The AssemblyQualifiedName of the serialized Type 15 | const string SerializationName = "n"; 16 | readonly string name; 17 | readonly Type type; 18 | 19 | public string DeletedMessage 20 | { 21 | get 22 | { 23 | return $"The .NET Type {name} no longer exists"; 24 | } 25 | } 26 | 27 | public Type Value 28 | { 29 | get 30 | { 31 | if (type == null) 32 | { 33 | throw new SerializationException(DeletedMessage); 34 | } 35 | return type; 36 | } 37 | } 38 | 39 | public string Name => name; 40 | public bool Valid => type != null; 41 | 42 | public override string ToString() 43 | { 44 | return (type != null ? type.ToString() : $"missing type: {name}"); 45 | } 46 | 47 | public MaybeType(Type tp) 48 | { 49 | type = tp; 50 | name = tp.AssemblyQualifiedName; 51 | } 52 | 53 | private MaybeType(SerializationInfo serializationInfo, StreamingContext context) 54 | { 55 | name = (string)serializationInfo.GetValue(SerializationName, typeof(string)); 56 | type = Type.GetType(name, throwOnError:false); 57 | } 58 | 59 | public void GetObjectData(SerializationInfo serializationInfo, StreamingContext context) 60 | { 61 | serializationInfo.AddValue(SerializationName, name); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/runtime/StateSerialization/MetatypeState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime.StateSerialization; 4 | 5 | // Workaround for the lack of required properties: https://github.com/dotnet/csharplang/issues/3630 6 | // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. 7 | #pragma warning disable CS8618 8 | 9 | [Serializable] 10 | internal class MetatypeState 11 | { 12 | public PyType CLRMetaType { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/PythonNetState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime.StateSerialization; 4 | 5 | // Workaround for the lack of required properties: https://github.com/dotnet/csharplang/issues/3630 6 | // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. 7 | #pragma warning disable CS8618 8 | 9 | [Serializable] 10 | internal class PythonNetState 11 | { 12 | public MetatypeState Metatype { get; init; } 13 | public SharedObjectsState SharedObjects { get; init; } 14 | public TypeManagerState Types { get; init; } 15 | public ClassManagerState Classes { get; init; } 16 | public ImportHookState ImportHookState { get; init; } 17 | } 18 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/SharedObjectsState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime.StateSerialization; 5 | 6 | // Workaround for the lack of required properties: https://github.com/dotnet/csharplang/issues/3630 7 | // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. 8 | #pragma warning disable CS8618 9 | 10 | [Serializable] 11 | internal class SharedObjectsState 12 | { 13 | public Dictionary InternalStores { get; init; } 14 | public Dictionary Extensions { get; init; } 15 | public Dictionary Wrappers { get; init; } 16 | public Dictionary> Contexts { get; init; } 17 | } 18 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/TypeManagerState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime.StateSerialization; 5 | 6 | // Workaround for the lack of required properties: https://github.com/dotnet/csharplang/issues/3630 7 | // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. 8 | #pragma warning disable CS8618 9 | 10 | [Serializable] 11 | internal class TypeManagerState 12 | { 13 | public Dictionary Cache { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/runtime/StateSerialization/UnloadedClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime; 4 | 5 | [Serializable] 6 | internal class UnloadedClass : ClassBase 7 | { 8 | readonly string name; 9 | 10 | internal UnloadedClass(string name) : base(typeof(object)) 11 | { 12 | this.name = name; 13 | } 14 | 15 | public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) 16 | { 17 | var self = (UnloadedClass)GetManagedObject(tp)!; 18 | return self.RaiseTypeError(); 19 | } 20 | 21 | public override NewReference type_subscript(BorrowedReference idx) => RaiseTypeError(); 22 | 23 | private NewReference RaiseTypeError() 24 | => Exceptions.RaiseTypeError("The .NET type no longer exists: " + name); 25 | 26 | internal override bool CanSubclass() => false; 27 | } 28 | -------------------------------------------------------------------------------- /src/runtime/Types/ClrObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Python.Runtime 7 | { 8 | [Serializable] 9 | [DebuggerDisplay("clrO: {inst}")] 10 | internal sealed class CLRObject : ManagedType 11 | { 12 | internal readonly object inst; 13 | 14 | // "borrowed" references 15 | internal static readonly HashSet reflectedObjects = new(); 16 | static NewReference Create(object ob, BorrowedReference tp) 17 | { 18 | Debug.Assert(tp != null); 19 | var py = Runtime.PyType_GenericAlloc(tp, 0); 20 | 21 | var self = new CLRObject(ob); 22 | 23 | GCHandle gc = GCHandle.Alloc(self); 24 | InitGCHandle(py.Borrow(), type: tp, gc); 25 | 26 | bool isNew = reflectedObjects.Add(py.DangerousGetAddress()); 27 | Debug.Assert(isNew); 28 | 29 | // Fix the BaseException args (and __cause__ in case of Python 3) 30 | // slot if wrapping a CLR exception 31 | if (ob is Exception e) Exceptions.SetArgsAndCause(py.Borrow(), e); 32 | 33 | return py; 34 | } 35 | 36 | CLRObject(object inst) 37 | { 38 | this.inst = inst; 39 | } 40 | 41 | internal static NewReference GetReference(object ob, BorrowedReference pyType) 42 | => Create(ob, pyType); 43 | 44 | internal static NewReference GetReference(object ob, Type type) 45 | { 46 | BorrowedReference cc = ClassManager.GetClass(type); 47 | return Create(ob, cc); 48 | } 49 | 50 | internal static NewReference GetReference(object ob) 51 | { 52 | BorrowedReference cc = ClassManager.GetClass(ob.GetType()); 53 | return Create(ob, cc); 54 | } 55 | 56 | internal static void Restore(object ob, BorrowedReference pyHandle, Dictionary context) 57 | { 58 | var co = new CLRObject(ob); 59 | co.OnLoad(pyHandle, context); 60 | } 61 | 62 | protected override void OnLoad(BorrowedReference ob, Dictionary? context) 63 | { 64 | base.OnLoad(ob, context); 65 | GCHandle gc = GCHandle.Alloc(this); 66 | SetGCHandle(ob, gc); 67 | 68 | bool isNew = reflectedObjects.Add(ob.DangerousGetAddress()); 69 | Debug.Assert(isNew); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/runtime/Types/ExceptionClassObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Runtime; 4 | 5 | /// 6 | /// Base class for Python types that reflect managed exceptions based on 7 | /// System.Exception 8 | /// 9 | [Serializable] 10 | internal class ExceptionClassObject : ClassObject 11 | { 12 | internal ExceptionClassObject(Type tp) : base(tp) 13 | { 14 | } 15 | 16 | internal static Exception? ToException(BorrowedReference ob) 17 | { 18 | var co = GetManagedObject(ob) as CLRObject; 19 | return co?.inst as Exception; 20 | } 21 | 22 | /// 23 | /// Exception __repr__ implementation 24 | /// 25 | public new static NewReference tp_repr(BorrowedReference ob) 26 | { 27 | Exception? e = ToException(ob); 28 | if (e == null) 29 | { 30 | return Exceptions.RaiseTypeError("invalid object"); 31 | } 32 | string name = e.GetType().Name; 33 | string message; 34 | if (e.Message != String.Empty) 35 | { 36 | message = String.Format("{0}('{1}')", name, e.Message); 37 | } 38 | else 39 | { 40 | message = String.Format("{0}()", name); 41 | } 42 | return Runtime.PyString_FromString(message); 43 | } 44 | 45 | /// 46 | /// Exception __str__ implementation 47 | /// 48 | public new static NewReference tp_str(BorrowedReference ob) 49 | { 50 | Exception? e = ToException(ob); 51 | if (e == null) 52 | { 53 | return Exceptions.RaiseTypeError("invalid object"); 54 | } 55 | 56 | string message = e.ToString(); 57 | string fullTypeName = e.GetType().FullName; 58 | string prefix = fullTypeName + ": "; 59 | if (message.StartsWith(prefix)) 60 | { 61 | message = message.Substring(prefix.Length); 62 | } 63 | else if (message.StartsWith(fullTypeName)) 64 | { 65 | message = message.Substring(fullTypeName.Length); 66 | } 67 | return Runtime.PyString_FromString(message); 68 | } 69 | 70 | public override bool Init(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) 71 | { 72 | if (!base.Init(obj, args, kw)) return false; 73 | 74 | var e = (CLRObject)GetManagedObject(obj)!; 75 | 76 | return Exceptions.SetArgsAndCause(obj, (Exception)e.inst); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/runtime/Types/Iterator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace Python.Runtime 5 | { 6 | /// 7 | /// Implements a generic Python iterator for IEnumerable objects and 8 | /// managed array objects. This supports 'for i in object:' in Python. 9 | /// 10 | internal class Iterator : ExtensionType 11 | { 12 | private readonly IEnumerator iter; 13 | private readonly Type elemType; 14 | 15 | public Iterator(IEnumerator e, Type elemType) 16 | { 17 | iter = e; 18 | this.elemType = elemType; 19 | } 20 | 21 | 22 | /// 23 | /// Implements support for the Python iteration protocol. 24 | /// 25 | public static NewReference tp_iternext(BorrowedReference ob) 26 | { 27 | var self = (Iterator)GetManagedObject(ob)!; 28 | try 29 | { 30 | if (!self.iter.MoveNext()) 31 | { 32 | Exceptions.SetError(Exceptions.StopIteration, Runtime.PyNone); 33 | return default; 34 | } 35 | } 36 | catch (Exception e) 37 | { 38 | if (e.InnerException != null) 39 | { 40 | e = e.InnerException; 41 | } 42 | Exceptions.SetError(e); 43 | return default; 44 | } 45 | object item = self.iter.Current; 46 | return Converter.ToPython(item, self.elemType); 47 | } 48 | 49 | public static NewReference tp_iter(BorrowedReference ob) => new (ob); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/runtime/Types/ModuleFunctionObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace Python.Runtime 6 | { 7 | /// 8 | /// Module level functions 9 | /// 10 | [Serializable] 11 | internal class ModuleFunctionObject : MethodObject 12 | { 13 | public ModuleFunctionObject(Type type, string name, MethodInfo[] info, bool allow_threads) 14 | : base(type, name, info, allow_threads) 15 | { 16 | if (info.Any(item => !item.IsStatic)) 17 | { 18 | throw new Exception("Module function must be static."); 19 | } 20 | } 21 | 22 | /// 23 | /// __call__ implementation. 24 | /// 25 | public static NewReference tp_call(BorrowedReference ob, BorrowedReference args, BorrowedReference kw) 26 | { 27 | var self = (ModuleFunctionObject)GetManagedObject(ob)!; 28 | return self.Invoke(ob, args, kw); 29 | } 30 | 31 | /// 32 | /// __repr__ implementation. 33 | /// 34 | public new static NewReference tp_repr(BorrowedReference ob) 35 | { 36 | var self = (ModuleFunctionObject)GetManagedObject(ob)!; 37 | return Runtime.PyString_FromString($""); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/runtime/Types/ModulePropertyObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Python.Runtime 5 | { 6 | /// 7 | /// Module level properties (attributes) 8 | /// 9 | internal class ModulePropertyObject : ExtensionType 10 | { 11 | public ModulePropertyObject(PropertyInfo md) 12 | { 13 | throw new NotImplementedException("ModulePropertyObject"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/runtime/Types/MpLengthSlot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace Python.Runtime.Slots 9 | { 10 | internal static class MpLengthSlot 11 | { 12 | public static bool CanAssign(Type clrType) 13 | { 14 | if (typeof(ICollection).IsAssignableFrom(clrType)) 15 | { 16 | return true; 17 | } 18 | if (clrType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>))) 19 | { 20 | return true; 21 | } 22 | if (clrType.IsInterface && clrType.IsGenericType && clrType.GetGenericTypeDefinition() == typeof(ICollection<>)) 23 | { 24 | return true; 25 | } 26 | return false; 27 | } 28 | 29 | /// 30 | /// Implements __len__ for classes that implement ICollection 31 | /// (this includes any IList implementer or Array subclass) 32 | /// 33 | internal static nint impl(BorrowedReference ob) 34 | { 35 | if (ManagedType.GetManagedObject(ob) is not CLRObject co) 36 | { 37 | Exceptions.RaiseTypeError("invalid object"); 38 | return -1; 39 | } 40 | 41 | // first look for ICollection implementation directly 42 | if (co.inst is ICollection c) 43 | { 44 | return c.Count; 45 | } 46 | 47 | Type clrType = co.inst.GetType(); 48 | 49 | // now look for things that implement ICollection directly (non-explicitly) 50 | PropertyInfo p = clrType.GetProperty("Count"); 51 | if (p != null && clrType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>))) 52 | { 53 | return (int)p.GetValue(co.inst, null); 54 | } 55 | 56 | // finally look for things that implement the interface explicitly 57 | var iface = clrType.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)); 58 | if (iface != null) 59 | { 60 | p = iface.GetProperty(nameof(ICollection.Count)); 61 | return (int)p.GetValue(co.inst, null); 62 | } 63 | 64 | Exceptions.SetError(Exceptions.TypeError, $"object of type '{clrType.Name}' has no len()"); 65 | return -1; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/runtime/Types/OverloadMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Python.Runtime 5 | { 6 | /// 7 | /// Implements the __overloads__ attribute of method objects. This object 8 | /// supports the [] syntax to explicitly select an overload by signature. 9 | /// 10 | internal class OverloadMapper : ExtensionType 11 | { 12 | private readonly MethodObject m; 13 | private readonly PyObject? target; 14 | 15 | public OverloadMapper(MethodObject m, PyObject? target) 16 | { 17 | this.target = target; 18 | this.m = m; 19 | } 20 | 21 | /// 22 | /// Implement explicit overload selection using subscript syntax ([]). 23 | /// 24 | public static NewReference mp_subscript(BorrowedReference tp, BorrowedReference idx) 25 | { 26 | var self = (OverloadMapper)GetManagedObject(tp)!; 27 | 28 | // Note: if the type provides a non-generic method with N args 29 | // and a generic method that takes N params, then we always 30 | // prefer the non-generic version in doing overload selection. 31 | 32 | Type[]? types = Runtime.PythonArgsToTypeArray(idx); 33 | if (types == null) 34 | { 35 | return Exceptions.RaiseTypeError("type(s) expected"); 36 | } 37 | 38 | MethodBase? mi = MethodBinder.MatchSignature(self.m.info, types); 39 | if (mi == null) 40 | { 41 | var e = "No match found for signature"; 42 | return Exceptions.RaiseTypeError(e); 43 | } 44 | 45 | var mb = new MethodBinding(self.m, self.target) { info = mi }; 46 | return mb.Alloc(); 47 | } 48 | 49 | /// 50 | /// OverloadMapper __repr__ implementation. 51 | /// 52 | public static NewReference tp_repr(BorrowedReference op) 53 | { 54 | var self = (OverloadMapper)GetManagedObject(op)!; 55 | return self.m.GetDocString(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/runtime/Types/UnsafeReferenceWithRun.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace Python.Runtime; 5 | 6 | [EditorBrowsable(EditorBrowsableState.Never)] 7 | [Obsolete(Util.InternalUseOnly)] 8 | public struct UnsafeReferenceWithRun 9 | { 10 | internal UnsafeReferenceWithRun(BorrowedReference pyObj) 11 | { 12 | RawObj = pyObj.DangerousGetAddressOrNull(); 13 | Run = Runtime.GetRun(); 14 | } 15 | 16 | internal IntPtr RawObj; 17 | internal BorrowedReference Ref => new(RawObj); 18 | internal int Run; 19 | 20 | internal BorrowedReference CheckRun() 21 | { 22 | if (Run != Runtime.GetRun()) 23 | throw new RuntimeShutdownException(RawObj); 24 | 25 | return Ref; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/runtime/Util/InitOnly.cs: -------------------------------------------------------------------------------- 1 | namespace System.Runtime.CompilerServices 2 | { 3 | internal static class IsExternalInit { } 4 | } 5 | -------------------------------------------------------------------------------- /src/runtime/Util/NonCopyableAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime 2 | { 3 | using System; 4 | [AttributeUsage(AttributeTargets.Struct)] 5 | class NonCopyableAttribute : Attribute { } 6 | } 7 | -------------------------------------------------------------------------------- /src/runtime/Util/NullOnly.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime 2 | { 3 | /// 4 | /// An utility class, that can only have one value: null. 5 | /// Useful for overloading operators on structs, 6 | /// that have meaningful concept of null value (e.g. pointers and references). 7 | /// 8 | class NullOnly : PyObject 9 | { 10 | private NullOnly() : base(BorrowedReference.Null) { } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/runtime/Util/PythonReferenceComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Python.Runtime 5 | { 6 | /// 7 | /// Compares Python object wrappers by Python object references. 8 | /// Similar to but for Python objects 9 | /// 10 | [Serializable] 11 | public sealed class PythonReferenceComparer : IEqualityComparer 12 | { 13 | public static PythonReferenceComparer Instance { get; } = new PythonReferenceComparer(); 14 | public bool Equals(PyObject? x, PyObject? y) 15 | { 16 | return x?.DangerousGetAddressOrNull() == y?.DangerousGetAddressOrNull(); 17 | } 18 | 19 | public int GetHashCode(PyObject obj) => obj.DangerousGetAddressOrNull().GetHashCode(); 20 | 21 | private PythonReferenceComparer() { } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/runtime/Util/ReflectionPolyfills.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Reflection.Emit; 5 | 6 | namespace Python.Runtime 7 | { 8 | internal static class ReflectionPolyfills 9 | { 10 | public static AssemblyBuilder DefineDynamicAssembly(this AppDomain _, AssemblyName assemblyName, AssemblyBuilderAccess assemblyBuilderAccess) 11 | { 12 | return AssemblyBuilder.DefineDynamicAssembly(assemblyName, assemblyBuilderAccess); 13 | } 14 | 15 | public static Type CreateType(this TypeBuilder typeBuilder) 16 | { 17 | return typeBuilder.CreateTypeInfo(); 18 | } 19 | 20 | public static T GetCustomAttribute(this Type type) where T: Attribute 21 | { 22 | return type.GetCustomAttributes(typeof(T), inherit: false) 23 | .Cast() 24 | .SingleOrDefault(); 25 | } 26 | 27 | public static T GetCustomAttribute(this Assembly assembly) where T: Attribute 28 | { 29 | return assembly.GetCustomAttributes(typeof(T), inherit: false) 30 | .Cast() 31 | .SingleOrDefault(); 32 | } 33 | 34 | public static bool IsFlagsEnum(this Type type) 35 | => type.GetCustomAttribute() is not null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/runtime/Util/ReflectionUtil.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Runtime; 2 | 3 | using System; 4 | using System.Reflection; 5 | 6 | static class ReflectionUtil 7 | { 8 | public static MethodInfo? GetBaseGetMethod(this PropertyInfo property, bool nonPublic) 9 | { 10 | if (property is null) throw new ArgumentNullException(nameof(property)); 11 | 12 | Type baseType = property.DeclaringType.BaseType; 13 | BindingFlags bindingFlags = property.GetBindingFlags(); 14 | 15 | while (baseType is not null) 16 | { 17 | var baseProperty = baseType.GetProperty(property.Name, bindingFlags | BindingFlags.DeclaredOnly); 18 | var accessor = baseProperty?.GetGetMethod(nonPublic); 19 | if (accessor is not null) 20 | return accessor; 21 | 22 | baseType = baseType.BaseType; 23 | } 24 | 25 | return null; 26 | } 27 | 28 | public static MethodInfo? GetBaseSetMethod(this PropertyInfo property, bool nonPublic) 29 | { 30 | if (property is null) throw new ArgumentNullException(nameof(property)); 31 | 32 | Type baseType = property.DeclaringType.BaseType; 33 | BindingFlags bindingFlags = property.GetBindingFlags(); 34 | 35 | while (baseType is not null) 36 | { 37 | var baseProperty = baseType.GetProperty(property.Name, bindingFlags | BindingFlags.DeclaredOnly); 38 | var accessor = baseProperty?.GetSetMethod(nonPublic); 39 | if (accessor is not null) 40 | return accessor; 41 | 42 | baseType = baseType.BaseType; 43 | } 44 | 45 | return null; 46 | } 47 | 48 | public static BindingFlags GetBindingFlags(this PropertyInfo property) 49 | { 50 | var accessor = property.GetMethod ?? property.SetMethod; 51 | BindingFlags flags = default; 52 | flags |= accessor.IsStatic ? BindingFlags.Static : BindingFlags.Instance; 53 | flags |= accessor.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic; 54 | return flags; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/testing/CodecTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Python.Runtime; 6 | 7 | namespace Python.Test 8 | { 9 | public class ListMember 10 | { 11 | public ListMember(int value, string name) 12 | { 13 | Value = value; 14 | Name = name; 15 | } 16 | 17 | public int Value { get; set; } 18 | public string Name { get; set; } 19 | } 20 | 21 | public class ListConversionTester 22 | { 23 | public int GetLength(IEnumerable o) 24 | { 25 | return o.Count(); 26 | } 27 | public int GetLength(ICollection o) 28 | { 29 | return o.Count; 30 | } 31 | public int GetLength(IList o) 32 | { 33 | return o.Count; 34 | } 35 | public int GetLength2(IEnumerable o) 36 | { 37 | return o.Count(); 38 | } 39 | public int GetLength2(ICollection o) 40 | { 41 | return o.Count; 42 | } 43 | public int GetLength2(IList o) 44 | { 45 | return o.Count; 46 | } 47 | } 48 | 49 | public static class CodecResetter 50 | { 51 | public static void Reset() 52 | { 53 | PyObjectConversions.Reset(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/testing/InheritanceTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Test 4 | { 5 | public class BaseClass 6 | { 7 | public bool IsBase() => true; 8 | } 9 | 10 | public class DerivedClass : BaseClass 11 | { 12 | public new bool IsBase() => false; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/testing/Python.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0;net6.0 4 | true 5 | true 6 | ..\pythonnet.snk 7 | true 8 | IDE0051;IDE0060 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/testing/callbacktest.cs: -------------------------------------------------------------------------------- 1 | using Python.Runtime; 2 | 3 | namespace Python.Test 4 | { 5 | /// 6 | /// Tests callbacks into python code. 7 | /// 8 | public class CallbackTest 9 | { 10 | public string Call_simpleDefaultArg_WithNull(string moduleName) 11 | { 12 | using (Py.GIL()) 13 | { 14 | dynamic module = Py.Import(moduleName); 15 | return module.simpleDefaultArg(null); 16 | } 17 | } 18 | 19 | public string Call_simpleDefaultArg_WithEmptyArgs(string moduleName) 20 | { 21 | using (Py.GIL()) 22 | { 23 | dynamic module = Py.Import(moduleName); 24 | return module.simpleDefaultArg(); 25 | } 26 | } 27 | } 28 | 29 | /// 30 | /// Tests calling from Python into C# and back into Python using a PyObject. 31 | /// SelfCallbackTest should be inherited by a Python class. 32 | /// Used in test_class.py / testCallback 33 | /// 34 | public class SelfCallbackTest 35 | { 36 | public void Callback(PyObject self) 37 | { 38 | using (Py.GIL()) 39 | { 40 | ((dynamic)self).PyCallback(self); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/testing/classtest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace Python.Test 4 | { 5 | /// 6 | /// Supports CLR class unit tests. 7 | /// 8 | public class ClassTest 9 | { 10 | public static ArrayList GetArrayList() 11 | { 12 | var list = new ArrayList(); 13 | for (var i = 0; i < 10; i++) 14 | { 15 | list.Add(i); 16 | } 17 | return list; 18 | } 19 | 20 | public static Hashtable GetHashtable() 21 | { 22 | var dict = new Hashtable(); 23 | dict.Add("one", 1); 24 | dict.Add("two", 2); 25 | dict.Add("three", 3); 26 | dict.Add("four", 4); 27 | dict.Add("five", 5); 28 | return dict; 29 | } 30 | 31 | public static IEnumerator GetEnumerator() 32 | { 33 | var temp = "test string"; 34 | return temp.GetEnumerator(); 35 | } 36 | } 37 | 38 | 39 | public class ClassCtorTest1 40 | { 41 | public string value; 42 | 43 | public ClassCtorTest1() 44 | { 45 | value = "default"; 46 | } 47 | } 48 | 49 | public class ClassCtorTest2 50 | { 51 | public string value; 52 | 53 | public ClassCtorTest2(string v) 54 | { 55 | value = v; 56 | } 57 | } 58 | 59 | internal class InternalClass 60 | { 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/testing/constructortests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Python.Test 5 | { 6 | /// 7 | /// These classes support the CLR constructor unit tests. 8 | /// 9 | public class EnumConstructorTest 10 | { 11 | public TypeCode value; 12 | 13 | public EnumConstructorTest(TypeCode v) 14 | { 15 | value = v; 16 | } 17 | } 18 | 19 | 20 | public class FlagsConstructorTest 21 | { 22 | public FileAccess value; 23 | 24 | public FlagsConstructorTest(FileAccess v) 25 | { 26 | value = v; 27 | } 28 | } 29 | 30 | 31 | public class StructConstructorTest 32 | { 33 | public Guid value; 34 | 35 | public StructConstructorTest(Guid v) 36 | { 37 | value = v; 38 | } 39 | } 40 | 41 | public struct GenericStructConstructorTest where T : struct 42 | { 43 | public T Value; 44 | 45 | public GenericStructConstructorTest(T value) 46 | { 47 | this.Value = value; 48 | } 49 | } 50 | 51 | 52 | public class SubclassConstructorTest 53 | { 54 | public Exception value; 55 | 56 | public SubclassConstructorTest(Exception v) 57 | { 58 | value = v; 59 | } 60 | } 61 | 62 | public class MultipleConstructorsTest 63 | { 64 | public string value; 65 | public Type[] type; 66 | 67 | public MultipleConstructorsTest() 68 | { 69 | value = ""; 70 | type = new Type[1] { null }; 71 | } 72 | 73 | public MultipleConstructorsTest(string s, params Type[] tp) 74 | { 75 | value = s; 76 | type = tp; 77 | } 78 | } 79 | 80 | public class DefaultConstructorMatching 81 | { 82 | public double a; 83 | public DefaultConstructorMatching() { a = 1; } 84 | public DefaultConstructorMatching(double a) { this.a = a; } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/testing/conversiontest.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Test 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | /// 7 | /// Supports unit tests for field access. 8 | /// 9 | public class ConversionTest 10 | { 11 | public ConversionTest() 12 | { 13 | EnumField = ShortEnum.Zero; 14 | SpamField = new Spam("spam"); 15 | StringField = "spam"; 16 | } 17 | 18 | public bool BooleanField = false; 19 | public byte ByteField = 0; 20 | public sbyte SByteField = 0; 21 | public char CharField = 'A'; 22 | public short Int16Field = 0; 23 | public int Int32Field = 0; 24 | public long Int64Field = 0; 25 | public ushort UInt16Field = 0; 26 | public uint UInt32Field = 0; 27 | public ulong UInt64Field = 0; 28 | public float SingleField = 0.0F; 29 | public double DoubleField = 0.0; 30 | public IntPtr IntPtrField = IntPtr.Zero; 31 | public UIntPtr UIntPtrField = UIntPtr.Zero; 32 | public decimal DecimalField = 0; 33 | public string StringField; 34 | public ShortEnum EnumField; 35 | public object ObjectField = null; 36 | public ISpam SpamField; 37 | 38 | public byte[] ByteArrayField; 39 | public sbyte[] SByteArrayField; 40 | public readonly List ListField = new List(); 41 | 42 | public T? Echo(T? arg) where T: struct { 43 | return arg; 44 | } 45 | 46 | } 47 | 48 | 49 | 50 | public interface ISpam 51 | { 52 | string GetValue(); 53 | } 54 | 55 | public class Spam : ISpam 56 | { 57 | private string value; 58 | 59 | public Spam(string value) 60 | { 61 | this.value = value; 62 | } 63 | 64 | public string GetValue() 65 | { 66 | return value; 67 | } 68 | } 69 | 70 | public class UnicodeString 71 | { 72 | public string value = "안녕"; 73 | 74 | public string GetString() 75 | { 76 | return value; 77 | } 78 | 79 | public override string ToString() 80 | { 81 | return value; 82 | } 83 | } 84 | 85 | public class MethodResolutionInt 86 | { 87 | public IEnumerable MethodA(ulong address, int size) 88 | { 89 | return new byte[10]; 90 | } 91 | 92 | public int MethodA(string dummy, ulong address, int size) 93 | { 94 | return 0; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/testing/doctest.cs: -------------------------------------------------------------------------------- 1 | using Python.Runtime; 2 | 3 | namespace Python.Test 4 | { 5 | /// 6 | /// Supports units tests for exposing docstrings from C# to Python 7 | /// 8 | /// 9 | /// Classes with a constructor have their docstring set to the ctor signature. 10 | /// Test if a class has an explicit doc string it gets set correctly. 11 | /// 12 | [DocString("DocWithCtorTest Class")] 13 | public class DocWithCtorTest 14 | { 15 | public DocWithCtorTest() 16 | { 17 | } 18 | 19 | [DocString("DocWithCtorTest TestMethod")] 20 | public void TestMethod() 21 | { 22 | } 23 | 24 | [DocString("DocWithCtorTest StaticTestMethod")] 25 | public static void StaticTestMethod() 26 | { 27 | } 28 | } 29 | 30 | public class DocWithCtorNoDocTest 31 | { 32 | public DocWithCtorNoDocTest(bool x) 33 | { 34 | } 35 | 36 | public void TestMethod(double a, int b) 37 | { 38 | } 39 | 40 | public static void StaticTestMethod(double a, int b) 41 | { 42 | } 43 | } 44 | 45 | [DocString("DocWithoutCtorTest Class")] 46 | public class DocWithoutCtorTest 47 | { 48 | [DocString("DocWithoutCtorTest TestMethod")] 49 | public void TestMethod() 50 | { 51 | } 52 | 53 | [DocString("DocWithoutCtorTest StaticTestMethod")] 54 | public static void StaticTestMethod() 55 | { 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/testing/enumtest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Python.Test 4 | { 5 | /// 6 | /// Supports CLR enum unit tests. 7 | /// 8 | public enum ByteEnum : byte 9 | { 10 | Zero, 11 | One, 12 | Two, 13 | Three, 14 | Four, 15 | Five 16 | } 17 | 18 | public enum SByteEnum : sbyte 19 | { 20 | Zero, 21 | One, 22 | Two, 23 | Three, 24 | Four, 25 | Five 26 | } 27 | 28 | public enum ShortEnum : short 29 | { 30 | Zero, 31 | One, 32 | Two, 33 | Three, 34 | Four, 35 | Five 36 | } 37 | 38 | public enum UShortEnum : ushort 39 | { 40 | Zero, 41 | One, 42 | Two, 43 | Three, 44 | Four, 45 | Five 46 | } 47 | 48 | public enum IntEnum : int 49 | { 50 | Zero, 51 | One, 52 | Two, 53 | Three, 54 | Four, 55 | Five 56 | } 57 | 58 | public enum UIntEnum : uint 59 | { 60 | Zero, 61 | One, 62 | Two, 63 | Three, 64 | Four, 65 | Five 66 | } 67 | 68 | public enum LongEnum : long 69 | { 70 | Zero, 71 | One, 72 | Two, 73 | Three, 74 | Four, 75 | Five, 76 | Max = long.MaxValue, 77 | Min = long.MinValue, 78 | } 79 | 80 | public enum ULongEnum : ulong 81 | { 82 | Zero, 83 | One, 84 | Two, 85 | Three, 86 | Four, 87 | Five, 88 | Max = ulong.MaxValue, 89 | } 90 | 91 | [Flags] 92 | public enum FlagsEnum 93 | { 94 | Zero, 95 | One, 96 | Two, 97 | Three, 98 | Four, 99 | Five 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/testing/fieldtest.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Test 2 | { 3 | /// 4 | /// Supports units tests for field access. 5 | /// 6 | public class FieldTest 7 | { 8 | public FieldTest() 9 | { 10 | EnumField = ShortEnum.Zero; 11 | SpamField = new Spam("spam"); 12 | StringField = "spam"; 13 | } 14 | 15 | public void Shutup() 16 | { 17 | int i = PrivateStaticField; 18 | int j = PrivateField; 19 | } 20 | 21 | public static readonly int ReadOnlyStaticField = 0; 22 | protected static int ProtectedStaticField = 0; 23 | internal static int InternalStaticField = 0; 24 | private static int PrivateStaticField = 0; 25 | public static int PublicStaticField = 0; 26 | 27 | public const int ConstField = 0; 28 | public readonly int ReadOnlyField = 0; 29 | internal int InternalField = 0; 30 | protected int ProtectedField = 0; 31 | private int PrivateField = 0; 32 | public int PublicField = 0; 33 | 34 | public bool BooleanField = false; 35 | public byte ByteField = 0; 36 | public sbyte SByteField = 0; 37 | public char CharField = 'A'; 38 | public short Int16Field = 0; 39 | public int Int32Field = 0; 40 | public long Int64Field = 0; 41 | public ushort UInt16Field = 0; 42 | public uint UInt32Field = 0; 43 | public ulong UInt64Field = 0; 44 | public float SingleField = 0.0F; 45 | public double DoubleField = 0.0; 46 | public decimal DecimalField = 0; 47 | public string StringField; 48 | public ShortEnum EnumField; 49 | public FlagsEnum FlagsField; 50 | public object ObjectField; 51 | public ISpam SpamField; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/testing/globaltest.cs: -------------------------------------------------------------------------------- 1 | 2 | /// 3 | /// Supports units tests for access to types without a namespace. 4 | /// 5 | public class NoNamespaceType 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/testing/interfacetest.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Test 2 | { 3 | /// 4 | /// Supports CLR class unit tests. 5 | /// 6 | public interface IPublicInterface 7 | { 8 | } 9 | 10 | internal interface IInternalInterface 11 | { 12 | } 13 | 14 | public interface ISayHello1 15 | { 16 | string SayHello(); 17 | } 18 | 19 | public interface ISayHello2 20 | { 21 | string SayHello(); 22 | } 23 | 24 | public class InterfaceTest : ISayHello1, ISayHello2 25 | { 26 | public InterfaceTest() 27 | { 28 | } 29 | 30 | public string HelloProperty 31 | { 32 | get { return "hello"; } 33 | } 34 | 35 | string ISayHello1.SayHello() 36 | { 37 | return "hello 1"; 38 | } 39 | 40 | string ISayHello2.SayHello() 41 | { 42 | return "hello 2"; 43 | } 44 | 45 | public ISayHello1 GetISayHello1() 46 | { 47 | return this; 48 | } 49 | 50 | public void GetISayHello2(out ISayHello2 hello2) 51 | { 52 | hello2 = this; 53 | } 54 | 55 | public ISayHello1 GetNoSayHello(out ISayHello2 hello2) 56 | { 57 | hello2 = null; 58 | return null; 59 | } 60 | 61 | public ISayHello1 [] GetISayHello1Array() 62 | { 63 | return new[] { this }; 64 | } 65 | 66 | public interface IPublic 67 | { 68 | } 69 | 70 | protected interface IProtected 71 | { 72 | } 73 | 74 | internal interface IInternal 75 | { 76 | } 77 | 78 | private interface IPrivate 79 | { 80 | } 81 | } 82 | 83 | public interface IOutArg 84 | { 85 | string MyMethod_Out(string name, out int index); 86 | } 87 | 88 | public class OutArgCaller 89 | { 90 | public static int CallMyMethod_Out(IOutArg myInterface) 91 | { 92 | myInterface.MyMethod_Out("myclient", out int index); 93 | return index; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/testing/moduletest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading; 4 | 5 | namespace Python.Test 6 | { 7 | public class ModuleTest 8 | { 9 | private static Thread _thread; 10 | 11 | public static void RunThreads() 12 | { 13 | _thread = new Thread(() => 14 | { 15 | AppDomain appdomain = AppDomain.CurrentDomain; 16 | Assembly[] assemblies = appdomain.GetAssemblies(); 17 | foreach (Assembly assembly in assemblies) 18 | { 19 | assembly.GetTypes(); 20 | } 21 | }); 22 | _thread.Start(); 23 | } 24 | 25 | public static void JoinThreads() 26 | { 27 | _thread.Join(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/testing/nonexportable.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Test 2 | { 3 | using Python.Runtime; 4 | 5 | // this class should not be visible to Python 6 | [PyExport(false)] 7 | public class NonExportable { } 8 | } 9 | -------------------------------------------------------------------------------- /src/testing/propertytest.cs: -------------------------------------------------------------------------------- 1 | namespace Python.Test 2 | { 3 | /// 4 | /// Supports units tests for property access. 5 | /// 6 | public class PropertyTest 7 | { 8 | public PropertyTest() 9 | { 10 | } 11 | 12 | private int _public_property = 0; 13 | 14 | public int PublicProperty 15 | { 16 | get { return _public_property; } 17 | set { _public_property = value; } 18 | } 19 | 20 | private static int _public_static_property = 0; 21 | 22 | public static int PublicStaticProperty 23 | { 24 | get { return _public_static_property; } 25 | set { _public_static_property = value; } 26 | } 27 | 28 | private int _protected_property = 0; 29 | 30 | protected int ProtectedProperty 31 | { 32 | get { return _protected_property; } 33 | set { _protected_property = value; } 34 | } 35 | 36 | private static int _protected_static_property = 0; 37 | 38 | protected static int ProtectedStaticProperty 39 | { 40 | get { return _protected_static_property; } 41 | set { _protected_static_property = value; } 42 | } 43 | 44 | private int _internal_property = 0; 45 | 46 | internal int InternalProperty 47 | { 48 | get { return _internal_property; } 49 | set { _internal_property = value; } 50 | } 51 | 52 | private static int _internal_static_property = 0; 53 | 54 | internal static int InternalStaticProperty 55 | { 56 | get { return _internal_static_property; } 57 | set { _internal_static_property = value; } 58 | } 59 | 60 | private int _private_property = 0; 61 | 62 | private int PrivateProperty 63 | { 64 | get { return _private_property; } 65 | set { _private_property = value; } 66 | } 67 | 68 | private static int _private_static_property = 0; 69 | 70 | private static int PrivateStaticProperty 71 | { 72 | get { return _private_static_property; } 73 | set { _private_static_property = value; } 74 | } 75 | 76 | private ShortEnum _enum_property = ShortEnum.Zero; 77 | 78 | public ShortEnum EnumProperty 79 | { 80 | get { return _enum_property; } 81 | set { _enum_property = value; } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/testing/threadtest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Python.Runtime; 3 | 4 | namespace Python.Test 5 | { 6 | /// 7 | /// Supports CLR threading / reentrant unit tests. 8 | /// 9 | public class ThreadTest 10 | { 11 | private static PyObject module; 12 | 13 | private static string testmod = 14 | "import clr\n" + 15 | "from Python.Test import ThreadTest\n" + 16 | "\n" + 17 | "def echostring(value):\n" + 18 | " return value\n" + 19 | "\n" + 20 | "def echostring2(value):\n" + 21 | " return ThreadTest.CallEchoString(value)\n" + 22 | "\n"; 23 | 24 | 25 | /// 26 | /// This method calls back into the CPython runtime - tests 27 | /// call this from Python to check that we don't hang on 28 | /// nested transitions from managed to Python code and back. 29 | /// 30 | public static string CallEchoString(string arg) 31 | { 32 | using (Py.GIL()) 33 | { 34 | if (module == null) 35 | { 36 | module = PyModule.FromString("tt", testmod); 37 | } 38 | PyObject func = module.GetAttr("echostring"); 39 | var parg = new PyString(arg); 40 | PyObject temp = func.Invoke(parg); 41 | var result = (string)temp.AsManagedObject(typeof(string)); 42 | func.Dispose(); 43 | parg.Dispose(); 44 | temp.Dispose(); 45 | return result; 46 | } 47 | } 48 | 49 | public static string CallEchoString2(string arg) 50 | { 51 | using (Py.GIL()) 52 | { 53 | if (module == null) 54 | { 55 | module = PyModule.FromString("tt", testmod); 56 | } 57 | 58 | PyObject func = module.GetAttr("echostring2"); 59 | var parg = new PyString(arg); 60 | PyObject temp = func.Invoke(parg); 61 | var result = (string)temp.AsManagedObject(typeof(string)); 62 | func.Dispose(); 63 | parg.Dispose(); 64 | temp.Dispose(); 65 | return result; 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/_missing_import.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import this_package_should_never_exist_ever 4 | -------------------------------------------------------------------------------- /tests/domain_tests/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/domain_tests/Python.DomainReloadTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472 5 | bin\ 6 | Exe 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/fixtures/argv-fixture.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Helper script to test argv. 4 | Ensures that argv isn't modified after importing clr. 5 | For more details see GH#404 - argv not found""" 6 | 7 | from __future__ import print_function 8 | 9 | import sys 10 | import clr 11 | 12 | print(sys.argv) 13 | -------------------------------------------------------------------------------- /tests/importtest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | try: 5 | del sys.modules["System.IO"] 6 | except KeyError: 7 | pass 8 | 9 | assert "FileStream" not in globals() 10 | import System.IO 11 | from System.IO import * 12 | 13 | assert "FileStream" in globals() 14 | -------------------------------------------------------------------------------- /tests/profile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # FIXME: FAIL: testImplicitAssemblyLoad AssertionError: 0 != 1 4 | 5 | """Run all of the unit tests for this package over and over, 6 | in order to provide for better profiling. 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | import gc 12 | import os 13 | import sys 14 | import time 15 | 16 | import runtests 17 | 18 | 19 | def main(): 20 | dirname = os.path.split(__file__) 21 | sys.path.append(dirname) 22 | 23 | gc.set_debug(gc.DEBUG_LEAK) 24 | 25 | start = time.clock() 26 | 27 | for i in range(50): 28 | print('iteration: {0:d}'.format(i)) 29 | runtests.main() 30 | 31 | stop = time.clock() 32 | took = str(stop - start) 33 | print('Total Time: {0}'.format(took)) 34 | 35 | for item in gc.get_objects(): 36 | print(item, sys.getrefcount(item)) 37 | 38 | 39 | if __name__ == '__main__': 40 | main() 41 | -------------------------------------------------------------------------------- /tests/runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Run all of the unit tests for this package.""" 5 | 6 | from __future__ import print_function 7 | 8 | import sys 9 | import pytest 10 | 11 | try: 12 | import System 13 | except ImportError: 14 | print("Load clr import hook") 15 | import clr 16 | 17 | clr.AddReference("Python.Test") 18 | clr.AddReference("System.Collections") 19 | clr.AddReference("System.Data") 20 | clr.AddReference("System.Management") 21 | 22 | 23 | def main(verbosity=1): 24 | # test_module passes on its own, but not here if 25 | # other test modules that import System.Windows.Forms 26 | # run first. They must not do module level import/AddReference() 27 | # of the System.Windows.Forms namespace. 28 | 29 | # FIXME: test_engine has tests that are being skipped. 30 | # FIXME: test_subclass has tests that are being skipped. 31 | pytest.main() 32 | 33 | 34 | if __name__ == '__main__': 35 | main() 36 | if '--pause' in sys.argv: 37 | print("Press enter to continue") 38 | input() 39 | -------------------------------------------------------------------------------- /tests/stress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # FIXME: FAIL: testImplicitAssemblyLoad AssertionError: 0 != 1 4 | 5 | """ 6 | Run all of the unit tests for this package multiple times in a highly 7 | multi-threaded way to stress the system. This makes it possible to look 8 | for memory leaks and threading issues and provides a good target for a 9 | profiler to accumulate better data. 10 | """ 11 | 12 | from __future__ import print_function 13 | 14 | import gc 15 | import os 16 | import sys 17 | import threading 18 | import time 19 | 20 | import _thread as thread 21 | from .utils import dprint 22 | 23 | 24 | class StressTest(object): 25 | def __init__(self): 26 | self.dirname = os.path.split(__file__)[0] 27 | sys.path.append(self.dirname) 28 | gc.set_debug(gc.DEBUG_LEAK) 29 | import runtests 30 | self.module = runtests 31 | self.done = [] 32 | 33 | def mark_start(self): 34 | self._start = time.clock() 35 | 36 | def mark_finish(self): 37 | self._finish = time.clock() 38 | 39 | def elapsed(self): 40 | return self._finish - self._start 41 | 42 | def print_gc_report(self): 43 | for item in gc.get_objects(): 44 | print(item, sys.getrefcount(item)) 45 | 46 | def run_thread(self, iterations): 47 | thread_id = thread.get_ident() 48 | dprint("thread {0} starting...".format(thread_id)) 49 | time.sleep(0.1) 50 | for i in range(iterations): 51 | dprint("thread {0} iter {1} start".format(thread_id, i)) 52 | self.module.main() 53 | dprint("thread {0} iter {1} end".format(thread_id, i)) 54 | self.done.append(None) 55 | dprint("thread {0} done".format(thread_id)) 56 | 57 | def stress_test(self, iterations=1, threads=1): 58 | args = (iterations,) 59 | self.mark_start() 60 | for _ in range(threads): 61 | thread = threading.Thread(target=self.run_thread, args=args) 62 | thread.start() 63 | while len(self.done) < (iterations * threads): 64 | dprint(len(self.done)) 65 | time.sleep(0.1) 66 | self.mark_finish() 67 | took = self.elapsed() 68 | self.print_gc_report() 69 | 70 | 71 | def main(): 72 | test = StressTest() 73 | test.stress_test(2, 10) 74 | 75 | 76 | if __name__ == '__main__': 77 | main() 78 | -------------------------------------------------------------------------------- /tests/stresstest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # FIXME: FAIL: testImplicitAssemblyLoad AssertionError: 0 != 1 4 | 5 | """Basic stress test.""" 6 | 7 | from __future__ import print_function 8 | 9 | import gc 10 | import time 11 | import unittest 12 | # import pdb 13 | 14 | try: 15 | import System 16 | except ImportError: 17 | print("Load clr import hook") 18 | import clr 19 | clr.AddReference("Python.Test") 20 | clr.AddReference("System.Collections") 21 | clr.AddReference("System.Data") 22 | clr.AddReference("System.Management") 23 | 24 | 25 | def main(): 26 | start = time.clock() 27 | 28 | for i in range(2000): 29 | print(i) 30 | for name in ( 31 | 'test_module', 32 | 'test_conversion', 33 | # 'test_class', 34 | 'test_interface', 35 | 'test_enum', 36 | 'test_field', 37 | 'test_property', 38 | 'test_indexer', 39 | 'test_event', 40 | 'test_method', 41 | # 'test_delegate', 42 | 'test_array', 43 | ): 44 | module = __import__(name) 45 | unittest.TextTestRunner().run(module.test_suite()) 46 | 47 | # pdb.set_trace() 48 | 49 | stop = time.clock() 50 | took = str(stop - start) 51 | print('Total Time: {0}'.format(took)) 52 | 53 | for i in gc.get_objects(): 54 | print(i) 55 | 56 | 57 | if __name__ == '__main__': 58 | main() 59 | -------------------------------------------------------------------------------- /tests/test_callback.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Test that callbacks from C# into python work.""" 4 | 5 | 6 | def simpleDefaultArg(arg='test'): 7 | return arg 8 | 9 | 10 | def test_default_for_null(): 11 | """Test that C# can use null for an optional python argument""" 12 | from Python.Test import CallbackTest 13 | 14 | test_instance = CallbackTest() 15 | ret_val = test_instance.Call_simpleDefaultArg_WithNull(__name__) 16 | python_ret_val = simpleDefaultArg(None) 17 | assert ret_val == python_ret_val 18 | 19 | 20 | def test_default_for_none(): 21 | """Test that C# can use no argument for an optional python argument""" 22 | from Python.Test import CallbackTest 23 | 24 | test_instance = CallbackTest() 25 | ret_val = test_instance.Call_simpleDefaultArg_WithEmptyArgs(__name__) 26 | python_ret_val = simpleDefaultArg() 27 | assert ret_val == python_ret_val 28 | -------------------------------------------------------------------------------- /tests/test_clrmethod.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Test clrmethod and clrproperty support for calling methods and getting/setting python properties from CLR.""" 4 | 5 | import Python.Test as Test 6 | import System 7 | import pytest 8 | import clr 9 | 10 | class ExampleClrClass(System.Object): 11 | __namespace__ = "PyTest" 12 | def __init__(self): 13 | self._x = 3 14 | @clr.clrmethod(int, [int]) 15 | def test(self, x): 16 | return x*2 17 | 18 | def get_X(self): 19 | return self._x 20 | def set_X(self, value): 21 | self._x = value 22 | X = clr.clrproperty(int, get_X, set_X) 23 | 24 | @clr.clrproperty(int) 25 | def Y(self): 26 | return self._x * 2 27 | 28 | def test_set_and_get_property_from_py(): 29 | """Test setting and getting clr-accessible properties from python.""" 30 | t = ExampleClrClass() 31 | assert t.X == 3 32 | assert t.Y == 3 * 2 33 | t.X = 4 34 | assert t.X == 4 35 | assert t.Y == 4 * 2 36 | 37 | def test_set_and_get_property_from_clr(): 38 | """Test setting and getting clr-accessible properties from the clr.""" 39 | t = ExampleClrClass() 40 | assert t.GetType().GetProperty("X").GetValue(t) == 3 41 | assert t.GetType().GetProperty("Y").GetValue(t) == 3 * 2 42 | t.GetType().GetProperty("X").SetValue(t, 4) 43 | assert t.GetType().GetProperty("X").GetValue(t) == 4 44 | assert t.GetType().GetProperty("Y").GetValue(t) == 4 * 2 45 | 46 | 47 | def test_set_and_get_property_from_clr_and_py(): 48 | """Test setting and getting clr-accessible properties alternatingly from the clr and from python.""" 49 | t = ExampleClrClass() 50 | assert t.GetType().GetProperty("X").GetValue(t) == 3 51 | assert t.GetType().GetProperty("Y").GetValue(t) == 3 * 2 52 | assert t.X == 3 53 | assert t.Y == 3 * 2 54 | t.GetType().GetProperty("X").SetValue(t, 4) 55 | assert t.GetType().GetProperty("X").GetValue(t) == 4 56 | assert t.GetType().GetProperty("Y").GetValue(t) == 4 * 2 57 | assert t.X == 4 58 | assert t.Y == 4 * 2 59 | t.X = 5 60 | assert t.GetType().GetProperty("X").GetValue(t) == 5 61 | assert t.GetType().GetProperty("Y").GetValue(t) == 5 * 2 62 | assert t.X == 5 63 | assert t.Y == 5 * 2 64 | 65 | def test_method_invocation_from_py(): 66 | """Test calling a clr-accessible method from python.""" 67 | t = ExampleClrClass() 68 | assert t.test(41) == 41*2 69 | 70 | def test_method_invocation_from_clr(): 71 | """Test calling a clr-accessible method from the clr.""" 72 | t = ExampleClrClass() 73 | assert t.GetType().GetMethod("test").Invoke(t, [37]) == 37*2 74 | -------------------------------------------------------------------------------- /tests/test_collection_mixins.py: -------------------------------------------------------------------------------- 1 | import System.Collections.Generic as C 2 | 3 | def test_contains(): 4 | l = C.List[int]() 5 | l.Add(42) 6 | assert 42 in l 7 | assert 43 not in l 8 | 9 | def test_dict_items(): 10 | d = C.Dictionary[int, str]() 11 | d[42] = "a" 12 | items = d.items() 13 | assert len(items) == 1 14 | k,v = items[0] 15 | assert k == 42 16 | assert v == "a" 17 | 18 | # regression test for https://github.com/pythonnet/pythonnet/issues/1785 19 | def test_dict_in_keys(): 20 | d = C.Dictionary[str, int]() 21 | d["a"] = 42 22 | assert "a" in d.Keys 23 | assert "b" not in d.Keys 24 | -------------------------------------------------------------------------------- /tests/test_constructors.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Test CLR class constructor support.""" 4 | 5 | import pytest 6 | import sys 7 | 8 | import System 9 | 10 | 11 | def test_enum_constructor(): 12 | """Test enum constructor args""" 13 | from System import TypeCode 14 | from Python.Test import EnumConstructorTest 15 | 16 | ob = EnumConstructorTest(TypeCode.Int32) 17 | assert ob.value == TypeCode.Int32 18 | 19 | 20 | def test_flags_constructor(): 21 | """Test flags constructor args""" 22 | from Python.Test import FlagsConstructorTest 23 | from System.IO import FileAccess 24 | 25 | flags = FileAccess.Read | FileAccess.Write 26 | ob = FlagsConstructorTest(flags) 27 | assert ob.value == flags 28 | 29 | 30 | def test_struct_constructor(): 31 | """Test struct constructor args""" 32 | from System import Guid 33 | from Python.Test import StructConstructorTest 34 | 35 | guid = Guid.NewGuid() 36 | ob = StructConstructorTest(guid) 37 | assert ob.value == guid 38 | 39 | 40 | def test_datetime(): 41 | inst = System.DateTime(2021, 12, 29) 42 | assert inst.Year == 2021 43 | 44 | 45 | def test_subclass_constructor(): 46 | """Test subclass constructor args""" 47 | from Python.Test import SubclassConstructorTest 48 | 49 | class Sub(System.Exception): 50 | pass 51 | 52 | instance = Sub() 53 | ob = SubclassConstructorTest(instance) 54 | assert isinstance(ob.value, System.Exception) 55 | 56 | 57 | def test_multiple_constructor(): 58 | from Python.Test import MultipleConstructorsTest 59 | 60 | # Test parameterless 61 | ob = MultipleConstructorsTest() 62 | assert ob.value == "" 63 | 64 | 65 | def test_default_constructor_fallback(): 66 | from Python.Test import DefaultConstructorMatching 67 | 68 | ob = DefaultConstructorMatching(2) 69 | assert ob.a == 2 70 | 71 | with pytest.raises(TypeError): 72 | ob = DefaultConstructorMatching("2") 73 | 74 | 75 | def test_constructor_leak(): 76 | from System import Uri 77 | from Python.Runtime import Runtime 78 | 79 | uri = Uri("http://www.python.org") 80 | Runtime.TryCollectingGarbage(20) 81 | ref_count = sys.getrefcount(uri) 82 | 83 | # check disabled due to GC uncertainty 84 | # assert ref_count == 1 85 | 86 | 87 | 88 | def test_string_constructor(): 89 | from System import String, Char, Array 90 | 91 | ob = String('A', 10) 92 | assert ob == 'A' * 10 93 | 94 | arr = Array[Char](10) 95 | for i in range(10): 96 | arr[i] = Char(str(i)) 97 | 98 | ob = String(arr) 99 | assert ob == "0123456789" 100 | 101 | ob = String(arr, 5, 4) 102 | assert ob == "5678" 103 | -------------------------------------------------------------------------------- /tests/test_docstring.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Test doc strings support.""" 4 | 5 | 6 | def test_doc_with_ctor(): 7 | from Python.Test import DocWithCtorTest 8 | 9 | assert DocWithCtorTest.__doc__ == 'DocWithCtorTest Class' 10 | assert DocWithCtorTest.TestMethod.__doc__ == 'DocWithCtorTest TestMethod' 11 | assert DocWithCtorTest.StaticTestMethod.__doc__ == 'DocWithCtorTest StaticTestMethod' 12 | 13 | 14 | def test_doc_with_ctor_no_doc(): 15 | from Python.Test import DocWithCtorNoDocTest 16 | 17 | assert DocWithCtorNoDocTest.__doc__ == 'Void .ctor(Boolean)' 18 | assert DocWithCtorNoDocTest.TestMethod.__doc__ == 'Void TestMethod(Double, Int32)' 19 | assert DocWithCtorNoDocTest.StaticTestMethod.__doc__ == 'Void StaticTestMethod(Double, Int32)' 20 | 21 | 22 | def test_doc_without_ctor(): 23 | from Python.Test import DocWithoutCtorTest 24 | 25 | assert DocWithoutCtorTest.__doc__ == 'DocWithoutCtorTest Class' 26 | assert DocWithoutCtorTest.TestMethod.__doc__ == 'DocWithoutCtorTest TestMethod' 27 | assert DocWithoutCtorTest.StaticTestMethod.__doc__ == 'DocWithoutCtorTest StaticTestMethod' 28 | 29 | 30 | def test_doc_primitve(): 31 | from System import Int64, String 32 | 33 | assert Int64.__doc__ is not None 34 | assert String.__doc__ is not None 35 | -------------------------------------------------------------------------------- /tests/test_engine.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Test PythonEngine embedding APIs.""" 4 | 5 | import sys 6 | 7 | import System 8 | import pytest 9 | from Python.Runtime import PythonEngine 10 | 11 | 12 | def test_multiple_calls_to_initialize(): 13 | """Test that multiple initialize calls are harmless.""" 14 | try: 15 | PythonEngine.Initialize() 16 | PythonEngine.Initialize() 17 | PythonEngine.Initialize() 18 | except Exception: 19 | assert False # Initialize() raise an exception. 20 | 21 | 22 | def test_supported_version(): 23 | major, minor, build, *_ = sys.version_info 24 | ver = System.Version(major, minor, build) 25 | assert PythonEngine.IsSupportedVersion(ver) 26 | assert ver >= PythonEngine.MinSupportedVersion 27 | assert ver <= PythonEngine.MaxSupportedVersion 28 | 29 | 30 | @pytest.mark.skip(reason="FIXME: test crashes") 31 | def test_import_module(): 32 | """Test module import.""" 33 | m = PythonEngine.ImportModule("sys") 34 | n = m.GetAttr("__name__") 35 | assert n.AsManagedObject(System.String) == "sys" 36 | 37 | 38 | @pytest.mark.skip(reason="FIXME: test freezes") 39 | def test_run_string(): 40 | """Test the RunString method.""" 41 | PythonEngine.AcquireLock() 42 | 43 | code = "import sys; sys.singleline_worked = 1" 44 | PythonEngine.RunString(code) 45 | assert sys.singleline_worked == 1 46 | 47 | code = "import sys\nsys.multiline_worked = 1" 48 | PythonEngine.RunString(code) 49 | assert sys.multiline_worked == 1 50 | 51 | PythonEngine.ReleaseLock() 52 | 53 | def test_leak_type(): 54 | import clr 55 | sys._leaked_intptr = clr.GetClrType(System.IntPtr) 56 | -------------------------------------------------------------------------------- /tests/test_import.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Test the import statement.""" 4 | 5 | import pytest 6 | import sys 7 | 8 | def test_relative_missing_import(): 9 | """Test that a relative missing import doesn't crash. 10 | Some modules use this to check if a package is installed. 11 | Relative import in the site-packages folder""" 12 | with pytest.raises(ImportError): 13 | from . import _missing_import 14 | 15 | 16 | def test_import_all_on_second_time(): 17 | """Test import all attributes after a normal import without '*'. 18 | Due to import * only allowed at module level, the test body splitted 19 | to a module file.""" 20 | from . import importtest 21 | del sys.modules[importtest.__name__] 22 | 23 | -------------------------------------------------------------------------------- /tests/test_mp_length.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Test __len__ for .NET classes implementing ICollection/ICollection.""" 4 | 5 | import System 6 | import pytest 7 | from Python.Test import MpLengthCollectionTest, MpLengthExplicitCollectionTest, MpLengthGenericCollectionTest, MpLengthExplicitGenericCollectionTest 8 | 9 | def test_simple___len__(): 10 | """Test __len__ for simple ICollection implementers""" 11 | import System 12 | import System.Collections.Generic 13 | l = System.Collections.Generic.List[int]() 14 | assert len(l) == 0 15 | l.Add(5) 16 | l.Add(6) 17 | assert len(l) == 2 18 | 19 | d = System.Collections.Generic.Dictionary[int, int]() 20 | assert len(d) == 0 21 | d.Add(4, 5) 22 | assert len(d) == 1 23 | 24 | a = System.Array[int]([0,1,2,3]) 25 | assert len(a) == 4 26 | 27 | def test_custom_collection___len__(): 28 | """Test __len__ for custom collection class""" 29 | s = MpLengthCollectionTest() 30 | assert len(s) == 3 31 | 32 | def test_custom_collection_explicit___len__(): 33 | """Test __len__ for custom collection class that explicitly implements ICollection""" 34 | s = MpLengthExplicitCollectionTest() 35 | assert len(s) == 2 36 | 37 | def test_custom_generic_collection___len__(): 38 | """Test __len__ for custom generic collection class""" 39 | s = MpLengthGenericCollectionTest[int]() 40 | s.Add(1) 41 | s.Add(2) 42 | assert len(s) == 2 43 | 44 | def test_custom_generic_collection_explicit___len__(): 45 | """Test __len__ for custom generic collection that explicity implements ICollection""" 46 | s = MpLengthExplicitGenericCollectionTest[int]() 47 | s.Add(1) 48 | s.Add(10) 49 | assert len(s) == 2 50 | 51 | def test_len_through_interface_generic(): 52 | """Test __len__ for ICollection""" 53 | import System.Collections.Generic 54 | l = System.Collections.Generic.List[int]() 55 | coll = System.Collections.Generic.ICollection[int](l) 56 | assert len(coll) == 0 57 | 58 | def test_len_through_interface(): 59 | """Test __len__ for ICollection""" 60 | import System.Collections 61 | l = System.Collections.ArrayList() 62 | coll = System.Collections.ICollection(l) 63 | assert len(coll) == 0 64 | -------------------------------------------------------------------------------- /tests/test_recursive_types.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Test if interop with recursive type inheritance works.""" 4 | 5 | 6 | def test_recursive_type_creation(): 7 | """Test that a recursive types don't crash with a 8 | StackOverflowException""" 9 | from Python.Test import RecursiveInheritance 10 | 11 | test_instance = RecursiveInheritance.SubClass() 12 | test_instance.SomeMethod() 13 | -------------------------------------------------------------------------------- /tests/test_repr.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Test __repr__ output""" 4 | 5 | import System 6 | import pytest 7 | from Python.Test import ReprTest 8 | 9 | def test_basic(): 10 | """Test Point class which implements both ToString and __repr__ without inheritance""" 11 | ob = ReprTest.Point(1,2) 12 | # point implements ToString() and __repr__() 13 | assert ob.__repr__() == "Point(1,2)" 14 | assert str(ob) == "Python.Test.ReprTest+Point: X=1, Y=2" 15 | 16 | def test_system_string(): 17 | """Test system string""" 18 | ob = System.String("hello") 19 | assert str(ob) == "hello" 20 | assert "