├── .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 |
17 |
23 |
24 |
25 |
32 |
33 |
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