├── tests ├── cmd │ ├── build.stderr │ ├── init.stderr │ ├── new.stderr │ ├── sdist.stderr │ ├── upload.stderr │ ├── develop.stderr │ ├── generate-ci.stderr │ ├── list-python.stderr │ ├── maturin.stderr │ ├── publish.stderr │ ├── maturin.toml │ ├── new.toml │ ├── build.toml │ ├── init.toml │ ├── sdist.toml │ ├── upload.toml │ ├── develop.toml │ ├── publish.toml │ ├── generate-ci.toml │ ├── list-python.toml │ ├── list-python.stdout │ ├── sdist.stdout │ ├── new.stdout │ ├── init.stdout │ ├── maturin.stdout │ ├── generate-ci.stdout │ └── upload.stdout ├── manylinux_compliant.sh ├── cli.rs └── manylinux_incompliant.sh ├── guide ├── .gitignore ├── src │ ├── changelog.md │ ├── assets │ │ └── sponsors │ │ │ ├── rerun.png │ │ │ ├── astral.png │ │ │ ├── bytewax.png │ │ │ ├── prefect.png │ │ │ ├── sentry.png │ │ │ ├── pydantic.png │ │ │ ├── tsy-capital.png │ │ │ ├── quansightlabs.jpeg │ │ │ └── frontend-masters.png │ ├── SUMMARY.md │ ├── platform_support.md │ └── environment-variables.md ├── tweak.css └── book.toml ├── test-crates ├── pyo3-feature │ ├── src │ │ └── lib.rs │ ├── Readme.md │ └── Cargo.toml ├── wrong-python-source │ ├── src │ │ ├── lib.rs │ │ └── bin │ │ │ └── run_this.rs │ ├── python │ │ └── run_this │ │ │ └── __init__.py │ ├── Cargo.toml │ ├── pyproject.toml │ └── Cargo.lock ├── hello-world │ ├── LICENSE │ ├── src │ │ ├── main.rs │ │ └── bin │ │ │ └── foo.rs │ ├── licenses │ │ └── AUTHORS.txt │ ├── Cargo.lock │ ├── Cargo.toml │ ├── check_installed │ │ └── check_installed.py │ └── pyproject.toml ├── license-test │ ├── AUTHORS.txt │ ├── LICENCE.txt │ ├── LICENSE │ ├── NOTICE.md │ ├── _vendor │ │ ├── LICENCE │ │ ├── NOTICE │ │ ├── AUTHORS.txt │ │ ├── COPYING.md │ │ ├── nested │ │ │ ├── NOTICE.md │ │ │ └── LICENSE-BSD │ │ └── LICENSE-APACHE.txt │ ├── src │ │ └── main.rs │ ├── Cargo.lock │ ├── Cargo.toml │ ├── pyproject.toml │ └── check_installed │ │ └── check_installed.py ├── pyo3-abi3-without-version │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── pyo3-mixed │ ├── .gitignore │ ├── pyo3_mixed │ │ ├── python_module │ │ │ ├── __init__.py │ │ │ └── double.py │ │ ├── __init__.py │ │ └── assets │ │ │ └── asset.txt │ ├── tox.ini │ ├── pyo3-config.txt │ ├── tests │ │ └── test_pyo3_mixed.py │ ├── Cargo.toml │ ├── README.md │ ├── pyproject.toml │ ├── src │ │ └── lib.rs │ └── check_installed │ │ └── check_installed.py ├── pyo3-mixed-src │ ├── .gitignore │ ├── src │ │ ├── pyo3_mixed_src │ │ │ ├── python_module │ │ │ │ ├── __init__.py │ │ │ │ └── double.py │ │ │ └── __init__.py │ │ └── tests │ │ │ └── test_pyo3_mixed.py │ ├── tox.ini │ ├── check_installed │ │ └── check_installed.py │ ├── rust │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── README.md │ └── pyproject.toml ├── readme-duplication │ ├── readme-rs │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── README.md │ ├── readme-py │ │ ├── README.md │ │ ├── pyproject.toml │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── Cargo.toml │ └── check_installed │ │ └── check_installed.py ├── with-data │ ├── with_data.data │ │ ├── headers │ │ │ └── empty.h │ │ └── data │ │ │ └── data_subdir │ │ │ └── hello.txt │ ├── Cargo.lock │ ├── pyproject.toml │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── check_installed │ │ └── check_installed.py ├── cffi-pure │ ├── .gitignore │ ├── requirements_test.txt │ ├── tox.ini │ ├── Cargo.lock │ ├── Cargo.toml │ ├── check_installed │ │ └── check_installed.py │ ├── pyproject.toml │ ├── test_cffi_pure.py │ ├── Readme.md │ └── src │ │ └── lib.rs ├── pyo3-mixed-implicit │ ├── .gitignore │ ├── tox.ini │ ├── python │ │ └── pyo3_mixed_implicit │ │ │ └── some_rust │ │ │ ├── __init__.py │ │ │ └── double.py │ ├── check_installed │ │ └── check_installed.py │ ├── src │ │ └── lib.rs │ ├── tests │ │ └── test_pyo3_mixed_implicit.py │ ├── Cargo.toml │ ├── pyproject.toml │ └── README.md ├── pyo3-mixed-py-subdir │ ├── .gitignore │ ├── python │ │ └── pyo3_mixed_py_subdir │ │ │ ├── python_module │ │ │ ├── __init__.py │ │ │ └── double.py │ │ │ └── __init__.py │ ├── tox.ini │ ├── test_pyo3_mixed.py │ ├── check_installed │ │ └── check_installed.py │ ├── src │ │ └── lib.rs │ ├── Cargo.toml │ ├── README.md │ └── pyproject.toml ├── pyo3-mixed-submodule │ ├── .gitignore │ ├── pyo3_mixed_submodule │ │ ├── rust_module │ │ │ └── __init__.py │ │ ├── python_module │ │ │ ├── __init__.py │ │ │ └── double.py │ │ └── __init__.py │ ├── tox.ini │ ├── check_installed │ │ └── check_installed.py │ ├── tests │ │ └── test_pyo3_mixed_submodule.py │ ├── src │ │ └── lib.rs │ ├── Cargo.toml │ ├── pyproject.toml │ └── README.md ├── cffi-mixed │ ├── .gitignore │ ├── requirements_test.txt │ ├── cffi_mixed │ │ ├── __init__.py │ │ └── line.py │ ├── tox.ini │ ├── Cargo.lock │ ├── Cargo.toml │ ├── pyproject.toml │ ├── check_installed │ │ └── check_installed.py │ ├── test_cffi_mixed.py │ ├── src │ │ └── lib.rs │ └── Readme.md ├── pyo3-mixed-include-exclude │ ├── .gitignore │ ├── pyo3_mixed_include_exclude │ │ ├── exclude_this_file │ │ ├── include_this_file │ │ ├── python_module │ │ │ ├── __init__.py │ │ │ └── double.py │ │ ├── .gitignore │ │ └── __init__.py │ ├── tox.ini │ ├── check_installed │ │ └── check_installed.py │ ├── tests │ │ └── test_pyo3_mixed_include_exclude.py │ ├── src │ │ └── lib.rs │ ├── Cargo.toml │ ├── README.md │ └── pyproject.toml ├── pyo3-mixed-with-path-dep │ ├── .gitignore │ ├── tox.ini │ ├── tests │ │ └── test_pyo3_mixed_with_path_dep.py │ ├── pyo3_mixed_with_path_dep │ │ └── __init__.py │ ├── pyproject.toml │ ├── Cargo.toml │ ├── check_installed │ │ └── check_installed.py │ ├── README.md │ └── src │ │ └── lib.rs ├── uniffi-mixed │ ├── .gitignore │ ├── uniffi_mixed │ │ └── __init__.py │ ├── src │ │ ├── math.udl │ │ └── lib.rs │ ├── build.rs │ ├── pyproject.toml │ ├── test_uniffi_mixed.py │ ├── check_installed │ │ └── check_installed.py │ └── Cargo.toml ├── pyo3-mixed-workspace │ ├── src │ │ ├── pyo3_mixed_workspace │ │ │ ├── python_module │ │ │ │ ├── __init__.py │ │ │ │ └── double.py │ │ │ └── __init__.py │ │ └── tests │ │ │ └── test_pyo3_mixed.py │ ├── rust │ │ ├── pyo3-mixed-workspace │ │ │ ├── src │ │ │ │ └── lib.rs │ │ │ └── Cargo.toml │ │ ├── Cargo.toml │ │ └── python │ │ │ └── pyo3-mixed-workspace-py │ │ │ ├── src │ │ │ └── lib.rs │ │ │ └── Cargo.toml │ ├── tox.ini │ ├── check_installed │ │ └── check_installed.py │ ├── README.md │ └── pyproject.toml ├── workspace-inverted-order │ ├── path-dep-with-root │ │ ├── README.md │ │ ├── pyproject.toml │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── pyproject.toml │ ├── src │ │ └── lib.rs │ ├── README.md │ ├── check_installed │ │ └── check_installed.py │ └── Cargo.toml ├── workspace │ ├── Cargo.toml │ ├── py │ │ ├── src │ │ │ └── main.rs │ │ ├── pyproject.toml │ │ └── Cargo.toml │ ├── Cargo.lock │ └── Readme.md ├── uniffi-pure │ ├── uniffi.toml │ ├── src │ │ ├── math.udl │ │ └── lib.rs │ ├── build.rs │ ├── pyproject.toml │ ├── test_uniffi_pure.py │ ├── check_installed │ │ └── check_installed.py │ └── Cargo.toml ├── uniffi-pure-proc-macro │ ├── uniffi.toml │ ├── uniffi-bindgen.rs │ ├── pyproject.toml │ ├── src │ │ └── lib.rs │ ├── test_uniffi_pure.py │ ├── check_installed │ │ └── check_installed.py │ └── Cargo.toml ├── pyo3-ffi-pure │ ├── build.rs │ ├── test_pyo3_ffi_pure.py │ ├── check_installed │ │ └── check_installed.py │ ├── Cargo.toml │ ├── README.md │ ├── pyproject.toml │ ├── LICENSE │ ├── Cargo.lock │ └── src │ │ └── lib.rs ├── workspace-inheritance │ ├── generic_lib │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── python │ │ ├── pyproject.toml │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ └── Cargo.toml ├── workspace_with_path_dep │ ├── generic_lib │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── transitive_lib │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── dont_include_in_sdist │ │ ├── src │ │ │ └── main.rs │ │ └── Cargo.toml │ ├── python │ │ ├── pyproject.toml │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── Cargo.toml ├── uniffi-multiple-binding-files │ ├── uniffi.toml │ ├── src │ │ └── lib.rs │ ├── mylib │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── check_installed │ │ └── check_installed.py │ └── Cargo.toml ├── lib_with_path_dep │ ├── pyproject.toml │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── test.sh ├── sdist_with_path_dep │ ├── pyproject.toml │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── lib_with_disallowed_lib │ ├── pyproject.toml │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── pyo3-pure │ ├── pyo3_pure.pyi │ ├── tox.ini │ ├── local-test │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── tests │ │ └── test_pyo3_pure.py │ ├── src │ │ └── lib.rs │ ├── README.md │ ├── Cargo.toml │ ├── check_installed │ │ └── check_installed.py │ ├── LICENSE │ └── pyproject.toml ├── sdist_with_target_path_dep │ ├── pyproject.toml │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── cargo-update.sh ├── pyo3-no-extension-module │ ├── src │ │ └── lib.rs │ ├── Cargo.toml │ └── Cargo.lock ├── transitive_path_dep │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── some_path_dep │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── pyo3-bin │ ├── Cargo.toml │ ├── src │ │ └── main.rs │ └── check_installed │ │ └── check_installed.py ├── cargo-mock │ └── Cargo.toml └── update_readme.py ├── .github ├── FUNDING.yml ├── bors.toml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.yml ├── workflows │ ├── clear-cache.yml │ ├── downstream.yml │ ├── update-auditwheel.yml │ ├── copilot-setup-steps.yml │ └── docker.yml ├── copilot-instructions.md └── dependabot.yml ├── src ├── templates │ ├── main.rs.j2 │ ├── example.udl.j2 │ ├── build.rs.j2 │ ├── test_all.py.j2 │ ├── __init__.py.j2 │ ├── lib.rs.j2 │ ├── Cargo.toml.j2 │ ├── pyproject.toml.j2 │ └── .gitignore.j2 ├── auditwheel │ ├── mod.rs │ ├── repair.rs │ ├── musllinux.rs │ └── musllinux-policy.json ├── archive_source.rs ├── module_writer │ ├── util.rs │ └── path_writer.rs ├── python_interpreter │ └── get_interpreter_metadata.py ├── generate_json_schema.rs └── target │ └── legacy_py.rs ├── test-data ├── py.exe └── Readme.md ├── .codespellrc ├── netlify.toml ├── sysconfig ├── Readme.md ├── cpython-win-3.9-aarch64.txt ├── cpython-win-3.7.txt ├── pypy-linux-s390x-3.9-7.3.txt ├── pypy-linux-ppc64le-3.9-7.3.txt ├── pypy-linux-s390x-3.7-7.3.txt ├── pypy-linux-ppc64le-3.7-7.3.txt ├── pypy-linux-s390x-3.8-7.3.txt ├── pypy-linux-ppc64le-3.8-7.3.txt ├── pypy-linux-3.7-7.3.txt └── pypy-macos-3.7-7.3.txt ├── .dockerignore ├── MANIFEST.in ├── .gitignore ├── .config └── nextest.toml ├── .devcontainer ├── post_create.sh └── devcontainer.json ├── clippy.toml ├── .cirrus.yml ├── test-dockerfile.sh ├── license-mit ├── maturin ├── bootstrap.py └── __main__.py ├── pyproject.toml ├── .pre-commit-config.yaml ├── setup.py └── Dockerfile /tests/cmd/build.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cmd/init.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cmd/new.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cmd/sdist.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cmd/upload.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /guide/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /tests/cmd/develop.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cmd/generate-ci.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cmd/list-python.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cmd/maturin.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cmd/publish.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/pyo3-feature/src/lib.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: messense 2 | -------------------------------------------------------------------------------- /test-crates/wrong-python-source/src/lib.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/hello-world/LICENSE: -------------------------------------------------------------------------------- 1 | Test license 2 | -------------------------------------------------------------------------------- /test-crates/license-test/AUTHORS.txt: -------------------------------------------------------------------------------- 1 | AUTHORS 2 | -------------------------------------------------------------------------------- /test-crates/license-test/LICENCE.txt: -------------------------------------------------------------------------------- 1 | LICENCE 2 | -------------------------------------------------------------------------------- /test-crates/license-test/LICENSE: -------------------------------------------------------------------------------- 1 | LICENSE 2 | -------------------------------------------------------------------------------- /test-crates/license-test/NOTICE.md: -------------------------------------------------------------------------------- 1 | # NOTICE 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-abi3-without-version/src/lib.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cdo] 2 | -------------------------------------------------------------------------------- /test-crates/license-test/_vendor/LICENCE: -------------------------------------------------------------------------------- 1 | LICENCE 2 | -------------------------------------------------------------------------------- /test-crates/license-test/_vendor/NOTICE: -------------------------------------------------------------------------------- 1 | NOTICE 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cdo] 2 | -------------------------------------------------------------------------------- /test-crates/readme-duplication/readme-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/with-data/with_data.data/headers/empty.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /guide/src/changelog.md: -------------------------------------------------------------------------------- 1 | {{#include ../../Changelog.md}} 2 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/.gitignore: -------------------------------------------------------------------------------- 1 | cffi_pure/cffi_pure 2 | -------------------------------------------------------------------------------- /test-crates/license-test/_vendor/AUTHORS.txt: -------------------------------------------------------------------------------- 1 | AUTHORS 2 | -------------------------------------------------------------------------------- /test-crates/license-test/_vendor/COPYING.md: -------------------------------------------------------------------------------- 1 | # COPYING 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cdo] 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cdo] 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cdo] 2 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/.gitignore: -------------------------------------------------------------------------------- 1 | cffi_mixed/cffi_mixed 2 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/requirements_test.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | cffi 3 | -------------------------------------------------------------------------------- /test-crates/license-test/_vendor/nested/NOTICE.md: -------------------------------------------------------------------------------- 1 | # NOTICE 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cdo] 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-with-path-dep/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cdo] 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/pyo3_mixed/python_module/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/wrong-python-source/python/run_this/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/requirements_test.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | cffi 3 | -------------------------------------------------------------------------------- /test-crates/license-test/_vendor/nested/LICENSE-BSD: -------------------------------------------------------------------------------- 1 | LICENSE-BSD 2 | -------------------------------------------------------------------------------- /test-crates/uniffi-mixed/.gitignore: -------------------------------------------------------------------------------- 1 | uniffi_mixed/uniffi_mixed 2 | -------------------------------------------------------------------------------- /test-crates/wrong-python-source/src/bin/run_this.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /tests/cmd/maturin.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "--help" 3 | -------------------------------------------------------------------------------- /tests/cmd/new.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "new --help" 3 | -------------------------------------------------------------------------------- /test-crates/license-test/_vendor/LICENSE-APACHE.txt: -------------------------------------------------------------------------------- 1 | LICENSE-APACHE 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/src/pyo3_mixed_src/python_module/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/cmd/build.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "build --help" 3 | -------------------------------------------------------------------------------- /tests/cmd/init.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "init --help" 3 | -------------------------------------------------------------------------------- /tests/cmd/sdist.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "sdist --help" 3 | -------------------------------------------------------------------------------- /tests/cmd/upload.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "upload --help" 3 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/pyo3_mixed_submodule/rust_module/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/with-data/with_data.data/data/data_subdir/hello.txt: -------------------------------------------------------------------------------- 1 | Hi! 😊 2 | -------------------------------------------------------------------------------- /tests/cmd/develop.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "develop --help" 3 | -------------------------------------------------------------------------------- /tests/cmd/publish.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "publish --help" 3 | -------------------------------------------------------------------------------- /src/templates/main.rs.j2: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/pyo3-feature/Readme.md: -------------------------------------------------------------------------------- 1 | A crate where pyo3 is an optional feature 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/pyo3_mixed_include_exclude/exclude_this_file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/pyo3_mixed_include_exclude/include_this_file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/pyo3_mixed_submodule/python_module/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/src/pyo3_mixed_workspace/python_module/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/workspace-inverted-order/path-dep-with-root/README.md: -------------------------------------------------------------------------------- 1 | # Path readme 2 | -------------------------------------------------------------------------------- /test-crates/workspace-inverted-order/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.dummy] 2 | value = 1 3 | -------------------------------------------------------------------------------- /test-crates/workspace-inverted-order/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub const NUMBER: u32 = 42; 2 | -------------------------------------------------------------------------------- /test-crates/workspace/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "py" 4 | ] 5 | -------------------------------------------------------------------------------- /test-data/py.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/test-data/py.exe -------------------------------------------------------------------------------- /tests/cmd/generate-ci.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "generate-ci --help" 3 | -------------------------------------------------------------------------------- /tests/cmd/list-python.toml: -------------------------------------------------------------------------------- 1 | bin.name = "maturin" 2 | args = "list-python --help" 3 | -------------------------------------------------------------------------------- /src/templates/example.udl.j2: -------------------------------------------------------------------------------- 1 | namespace example { 2 | u32 add(u32 a, u32 b); 3 | }; 4 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/python_module/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/readme-duplication/README.md: -------------------------------------------------------------------------------- 1 | Readme duplication test case - Readme 1 2 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.python] 2 | cdylib_name = "example" 3 | -------------------------------------------------------------------------------- /test-crates/hello-world/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/license-test/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/pyo3_mixed_include_exclude/python_module/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-crates/uniffi-mixed/uniffi_mixed/__init__.py: -------------------------------------------------------------------------------- 1 | from .uniffi_mixed import * # NOQA 2 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure/src/math.udl: -------------------------------------------------------------------------------- 1 | namespace math { 2 | u32 add(u32 a, u32 b); 3 | }; 4 | -------------------------------------------------------------------------------- /test-crates/workspace/py/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/pyo3_mixed_include_exclude/.gitignore: -------------------------------------------------------------------------------- 1 | include_this_file 2 | -------------------------------------------------------------------------------- /test-crates/readme-duplication/readme-py/README.md: -------------------------------------------------------------------------------- 1 | Readme duplication test case - Readme 2 2 | -------------------------------------------------------------------------------- /test-crates/uniffi-mixed/src/math.udl: -------------------------------------------------------------------------------- 1 | namespace math { 2 | u32 add(u32 a, u32 b); 3 | }; 4 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure-proc-macro/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.python] 2 | cdylib_name = "example" 3 | -------------------------------------------------------------------------------- /test-data/Readme.md: -------------------------------------------------------------------------------- 1 | * `py.exe`: Mock for the windows python launcher we can insert in path 2 | -------------------------------------------------------------------------------- /test-crates/hello-world/src/bin/foo.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("🦀 Hello, world! 🦀"); 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/pyo3-ffi-pure/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pyo3_build_config::use_pyo3_cfgs(); 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure-proc-macro/uniffi-bindgen.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | uniffi::uniffi_bindgen_main() 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/workspace-inheritance/generic_lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn foo() -> &'static str { 2 | "foo" 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/generic_lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn foo() -> &'static str { 2 | "foo" 3 | } 4 | -------------------------------------------------------------------------------- /guide/src/assets/sponsors/rerun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/guide/src/assets/sponsors/rerun.png -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/transitive_lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn bar() -> &'static str { 2 | "bar" 3 | } 4 | -------------------------------------------------------------------------------- /guide/src/assets/sponsors/astral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/guide/src/assets/sponsors/astral.png -------------------------------------------------------------------------------- /guide/src/assets/sponsors/bytewax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/guide/src/assets/sponsors/bytewax.png -------------------------------------------------------------------------------- /guide/src/assets/sponsors/prefect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/guide/src/assets/sponsors/prefect.png -------------------------------------------------------------------------------- /guide/src/assets/sponsors/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/guide/src/assets/sponsors/sentry.png -------------------------------------------------------------------------------- /test-crates/hello-world/licenses/AUTHORS.txt: -------------------------------------------------------------------------------- 1 | Test Author 2 | Another Author 3 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/rust/pyo3-mixed-workspace/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn get_21_lib() -> usize { 2 | 21 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/uniffi-mixed/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | uniffi::generate_scaffolding("./src/math.udl").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | uniffi::generate_scaffolding("./src/math.udl").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /guide/src/assets/sponsors/pydantic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/guide/src/assets/sponsors/pydantic.png -------------------------------------------------------------------------------- /test-crates/uniffi-mixed/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /test-crates/uniffi-multiple-binding-files/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.python] 2 | cdylib_name = "uniffi_multiple_binding_files" 3 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /test-crates/workspace/py/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=0.15,<0.16"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/dont_include_in_sdist/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | ignore-words-list = crate,socio-economic 3 | skip = ./.git,./target,./test-crates/venvs,./guide/book 4 | -------------------------------------------------------------------------------- /guide/src/assets/sponsors/tsy-capital.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/guide/src/assets/sponsors/tsy-capital.png -------------------------------------------------------------------------------- /test-crates/lib_with_path_dep/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /test-crates/sdist_with_path_dep/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /guide/src/assets/sponsors/quansightlabs.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/guide/src/assets/sponsors/quansightlabs.jpeg -------------------------------------------------------------------------------- /test-crates/lib_with_disallowed_lib/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/pyo3_pure.pyi: -------------------------------------------------------------------------------- 1 | class DummyClass: 2 | @staticmethod 3 | def get_42() -> int: ... 4 | 5 | fourtytwo: int 6 | -------------------------------------------------------------------------------- /test-crates/uniffi-mixed/src/lib.rs: -------------------------------------------------------------------------------- 1 | fn add(a: u32, b: u32) -> u32 { 2 | a + b 3 | } 4 | 5 | uniffi::include_scaffolding!("math"); 6 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure-proc-macro/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure/src/lib.rs: -------------------------------------------------------------------------------- 1 | fn add(a: u32, b: u32) -> u32 { 2 | a + b 3 | } 4 | 5 | uniffi::include_scaffolding!("math"); 6 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure/test_uniffi_pure.py: -------------------------------------------------------------------------------- 1 | import uniffi_pure 2 | 3 | 4 | def test_add(): 5 | assert uniffi_pure.add(1, 2) == 3 6 | -------------------------------------------------------------------------------- /guide/src/assets/sponsors/frontend-masters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/maturin/HEAD/guide/src/assets/sponsors/frontend-masters.png -------------------------------------------------------------------------------- /test-crates/sdist_with_target_path_dep/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /test-crates/uniffi-mixed/test_uniffi_mixed.py: -------------------------------------------------------------------------------- 1 | import uniffi_mixed 2 | 3 | 4 | def test_add(): 5 | assert uniffi_mixed.add(1, 2) == 3 6 | -------------------------------------------------------------------------------- /test-crates/workspace-inheritance/python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=0.15,<0.16"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/cffi_mixed/__init__.py: -------------------------------------------------------------------------------- 1 | from .cffi_mixed import ffi, lib 2 | from .line import Line 3 | 4 | __all__ = ["ffi", "lib", "Line"] 5 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest 8 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest tests/ 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest tests/ 8 | -------------------------------------------------------------------------------- /test-crates/readme-duplication/readme-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "readme-rs" 3 | version = "0.2.0" 4 | edition = "2021" 5 | readme.workspace = true 6 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure-proc-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[uniffi::export] 2 | fn add(a: u32, b: u32) -> u32 { 3 | a + b 4 | } 5 | 6 | uniffi::setup_scaffolding!(); 7 | -------------------------------------------------------------------------------- /src/templates/build.rs.j2: -------------------------------------------------------------------------------- 1 | {%- if bindings == "uniffi" -%} 2 | fn main() { 3 | uniffi::generate_scaffolding("./src/example.udl").unwrap(); 4 | } 5 | {% endif -%} 6 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest 8 | -------------------------------------------------------------------------------- /src/templates/test_all.py.j2: -------------------------------------------------------------------------------- 1 | import pytest 2 | import {{ crate_name }} 3 | 4 | 5 | def test_sum_as_string(): 6 | assert {{ crate_name }}.sum_as_string(1, 1) == "2" 7 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest tests/ 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest src/tests/ 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest tests/ 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/pyo3_mixed/python_module/double.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | 4 | def double(fn: Callable[[], int]) -> int: 5 | return 2 * fn() 6 | -------------------------------------------------------------------------------- /test-crates/readme-duplication/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["readme-py", "readme-rs"] 4 | 5 | [workspace.package] 6 | readme = "README.md" 7 | -------------------------------------------------------------------------------- /test-crates/readme-duplication/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import readme 3 | 4 | assert readme.value == 1 5 | 6 | print("SUCCESS") 7 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest tests/ 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-with-path-dep/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest tests/ 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | deps = pytest 7 | commands = pytest src/tests/ 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/pyo3-config.txt: -------------------------------------------------------------------------------- 1 | implementation=CPython 2 | version=3.10 3 | shared=true 4 | abi3=false 5 | suppress_build_script_link_lines=false 6 | pointer_width=64 7 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/tests/test_pyo3_mixed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed 4 | 5 | 6 | def test_get_42(): 7 | assert pyo3_mixed.get_42() == 42 8 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure-proc-macro/test_uniffi_pure.py: -------------------------------------------------------------------------------- 1 | import uniffi_pure_proc_macro as uniffi_pure 2 | 3 | 4 | def test_add(): 5 | assert uniffi_pure.add(1, 2) == 3 6 | -------------------------------------------------------------------------------- /test-crates/workspace-inverted-order/README.md: -------------------------------------------------------------------------------- 1 | # Root readme 2 | 3 | In this workspace, the build root is in a subdirectory, while the workspace root also contains a crate. 4 | -------------------------------------------------------------------------------- /test-crates/pyo3-ffi-pure/test_pyo3_ffi_pure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_ffi_pure 4 | 5 | 6 | def test_static(): 7 | assert pyo3_ffi_pure.sum(2, 40) == 42 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/python/pyo3_mixed_implicit/some_rust/__init__.py: -------------------------------------------------------------------------------- 1 | from .rust import get_22 2 | from .double import double 3 | 4 | __all__ = ["get_22", "double"] 5 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/src/pyo3_mixed_src/python_module/double.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | 4 | def double(fn: Callable[[], int]) -> int: 5 | return 2 * fn() 6 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "pyo3-mixed-workspace", 5 | "python/pyo3-mixed-workspace-py", 6 | ] 7 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import uniffi_pure 4 | 5 | assert uniffi_pure.add(1, 2) == 3 6 | 7 | print("SUCCESS") 8 | -------------------------------------------------------------------------------- /test-crates/workspace/py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "py" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-ffi-pure/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import pyo3_ffi_pure 3 | 4 | assert pyo3_ffi_pure.sum(2, 40) == 42 5 | 6 | print("SUCCESS") 7 | -------------------------------------------------------------------------------- /test-crates/uniffi-mixed/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import uniffi_mixed 4 | 5 | assert uniffi_mixed.add(1, 2) == 3 6 | 7 | print("SUCCESS") 8 | -------------------------------------------------------------------------------- /test-crates/workspace/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "py" 5 | version = "0.1.0" 6 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "cffi-pure" 5 | version = "0.1.0" 6 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/python/pyo3_mixed_implicit/some_rust/double.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | 4 | def double(fn: Callable[[], int]) -> int: 5 | return 2 * fn() 6 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/pyo3_mixed_submodule/python_module/double.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | 4 | def double(fn: Callable[[], int]) -> int: 5 | return 2 * fn() 6 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/src/pyo3_mixed_workspace/python_module/double.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | 4 | def double(fn: Callable[[], int]) -> int: 5 | return 2 * fn() 6 | -------------------------------------------------------------------------------- /test-crates/uniffi-multiple-binding-files/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[uniffi::export] 2 | pub fn get_status() -> mylib::Status { 3 | mylib::get_status() 4 | } 5 | 6 | uniffi::setup_scaffolding!(); 7 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "cffi-mixed" 5 | version = "0.1.0" 6 | -------------------------------------------------------------------------------- /test-crates/hello-world/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "hello-world" 5 | version = "0.1.0" 6 | -------------------------------------------------------------------------------- /test-crates/license-test/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "license-test" 5 | version = "0.1.0" 6 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/python_module/double.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | 4 | def double(fn: Callable[[], int]) -> int: 5 | return 2 * fn() 6 | -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "generic_lib", 5 | "transitive_lib", 6 | "dont_include_in_sdist", 7 | "python" 8 | ] 9 | -------------------------------------------------------------------------------- /src/templates/__init__.py.j2: -------------------------------------------------------------------------------- 1 | from .{{ crate_name }} import * 2 | 3 | 4 | __doc__ = {{ crate_name }}.__doc__ 5 | if hasattr({{ crate_name }}, "__all__"): 6 | __all__ = {{ crate_name }}.__all__ 7 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/pyo3_mixed_include_exclude/python_module/double.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | 4 | def double(fn: Callable[[], int]) -> int: 5 | return 2 * fn() 6 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/test_pyo3_mixed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_py_subdir as pyo3_mixed 4 | 5 | 6 | def test_get_42(): 7 | assert pyo3_mixed.get_42() == 42 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_src as pyo3_mixed 4 | 5 | assert pyo3_mixed.get_42() == 42 6 | 7 | print("SUCCESS") 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/src/tests/test_pyo3_mixed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_src as pyo3_mixed 4 | 5 | 6 | def test_get_42(): 7 | assert pyo3_mixed.get_42() == 42 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/src/pyo3_mixed_src/__init__.py: -------------------------------------------------------------------------------- 1 | from .python_module.double import double 2 | from .pyo3_mixed_src import get_21 3 | 4 | 5 | def get_42() -> int: 6 | return double(get_21) 7 | -------------------------------------------------------------------------------- /test-crates/readme-duplication/readme-py/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin==1.6.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "readme-py" 7 | version = "0.2.0" 8 | -------------------------------------------------------------------------------- /test-crates/wrong-python-source/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wrong-python-source" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "run_this" 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /test-crates/wrong-python-source/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.maturin] 2 | bindings = "bin" 3 | python-source = "python" 4 | 5 | [build-system] 6 | requires = ["maturin>=1.0,<2.0"] 7 | build-backend = "maturin" 8 | -------------------------------------------------------------------------------- /test-crates/license-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "license-test" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_py_subdir as pyo3_mixed 4 | 5 | assert pyo3_mixed.get_42() == 42 6 | 7 | print("SUCCESS") 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_submodule 4 | 5 | assert pyo3_mixed_submodule.get_42() == 42 6 | 7 | print("SUCCESS") 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_workspace as pyo3_mixed 4 | 5 | assert pyo3_mixed.get_42() == 42 6 | 7 | print("SUCCESS") 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/src/tests/test_pyo3_mixed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_workspace as pyo3_mixed 4 | 5 | 6 | def test_get_42(): 7 | assert pyo3_mixed.get_42() == 42 8 | -------------------------------------------------------------------------------- /test-crates/with-data/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "with-data" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /test-crates/workspace-inverted-order/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import path_dep_with_root 3 | 4 | assert path_dep_with_root.add_number(2) == 44 5 | 6 | print("SUCCESS") 7 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/python/pyo3_mixed_py_subdir/__init__.py: -------------------------------------------------------------------------------- 1 | from .python_module.double import double 2 | from ._pyo3_mixed import get_21 3 | 4 | 5 | def get_42() -> int: 6 | return double(get_21) 7 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/pyo3_mixed_submodule/__init__.py: -------------------------------------------------------------------------------- 1 | from .python_module.double import double 2 | from .rust_module.rust import get_21 3 | 4 | 5 | def get_42() -> int: 6 | return double(get_21) 7 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/tests/test_pyo3_mixed_submodule.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_submodule 4 | 5 | 6 | def test_get_42(): 7 | assert pyo3_mixed_submodule.get_42() == 42 8 | -------------------------------------------------------------------------------- /test-crates/with-data/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "with-data" 7 | dependencies = ["cffi"] 8 | dynamic = ["version"] 9 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "guide/book/" 3 | command = "curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.43/mdbook-v0.4.43-x86_64-unknown-linux-musl.tar.gz | tar xvz && ./mdbook build guide" 4 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/pyo3_mixed/__init__.py: -------------------------------------------------------------------------------- 1 | from .pyo3_mixed import get_21, print_cli_args # noqa: F401 2 | from .python_module.double import double 3 | 4 | 5 | def get_42() -> int: 6 | return double(get_21) 7 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure-proc-macro/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import uniffi_pure_proc_macro as uniffi_pure 4 | 5 | assert uniffi_pure.add(1, 2) == 3 6 | 7 | print("SUCCESS") 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_include_exclude 4 | 5 | assert pyo3_mixed_include_exclude.get_42() == 42 6 | 7 | print("SUCCESS") 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/src/pyo3_mixed_workspace/__init__.py: -------------------------------------------------------------------------------- 1 | from .python_module.double import double 2 | from .pyo3_mixed_workspace_py import get_21 3 | 4 | 5 | def get_42() -> int: 6 | return double(get_21) 7 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-with-path-dep/tests/test_pyo3_mixed_with_path_dep.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_with_path_dep 4 | 5 | 6 | def test_get_42(): 7 | assert pyo3_mixed_with_path_dep.get_42() == 42 8 | -------------------------------------------------------------------------------- /test-crates/wrong-python-source/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "wrong-python-source" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /sysconfig/Readme.md: -------------------------------------------------------------------------------- 1 | This folder contains all the sysconfigs (`python -m sysconfig`) I came across. I collected those because they differ highly across versions and operating systems, but are essential for naming wheels and libraries. 2 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/pyo3_mixed_include_exclude/__init__.py: -------------------------------------------------------------------------------- 1 | from .python_module.double import double 2 | from .pyo3_mixed_include_exclude import get_21 3 | 4 | 5 | def get_42() -> int: 6 | return double(get_21) 7 | -------------------------------------------------------------------------------- /test-crates/with-data/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "with-data" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "with_data" 9 | crate-type = ["cdylib"] 10 | -------------------------------------------------------------------------------- /test-crates/pyo3-feature/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["konstin "] 3 | name = "pyo3-feature" 4 | version = "0.7.3" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | pyo3 = { version = "0.27.0", optional = true } 9 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/tests/test_pyo3_mixed_include_exclude.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_include_exclude 4 | 5 | 6 | def test_get_42(): 7 | assert pyo3_mixed_include_exclude.get_42() == 42 8 | -------------------------------------------------------------------------------- /tests/manylinux_compliant.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | for PYBIN in /opt/python/cp3[89]*/bin; do 5 | $1 build -m test-crates/pyo3-mixed/Cargo.toml --target-dir test-crates/targets -i "${PYBIN}/python" --manylinux $2 -o dist 6 | done 7 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cffi-pure" 3 | version = "0.1.0" 4 | authors = ["Armin Ronacher "] 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "cffi_pure" 9 | crate-type = ["cdylib"] 10 | -------------------------------------------------------------------------------- /test-crates/workspace-inverted-order/path-dep-with-root/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "mitmproxy_path" 7 | version = "0.1.0" 8 | requires-python = ">=3.8" 9 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cffi-mixed" 3 | version = "0.1.0" 4 | authors = ["Armin Ronacher "] 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "cffi_mixed" 9 | crate-type = ["cdylib"] 10 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/rust/pyo3-mixed-workspace/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-mixed-workspace" 3 | version = "2.1.3" 4 | description = "Implements a dummy function combining rust and python" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/local-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "local-test" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /test-crates/cargo-update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | for d in ./*; do 3 | if [ -d "$d" ]; then 4 | if [ -f "$d/Cargo.lock" ]; then 5 | cargo update --manifest-path "$d/Cargo.toml" 6 | echo "$d updated" 7 | fi 8 | fi 9 | done 10 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_implicit.some_rust as some_rust 4 | 5 | assert some_rust.get_22() == 22 6 | assert some_rust.double(lambda: 4) == 8 7 | 8 | print("SUCCESS") 9 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-with-path-dep/pyo3_mixed_with_path_dep/__init__.py: -------------------------------------------------------------------------------- 1 | from .pyo3_mixed_with_path_dep import get_21, add_21, is_half 2 | 3 | __all__ = ["get_21", "add_21", "is_half", "get_42"] 4 | 5 | 6 | def get_42() -> int: 7 | return add_21(get_21()) 8 | -------------------------------------------------------------------------------- /test-crates/readme-duplication/readme-py/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::{pymodule, Bound, PyResult}; 2 | use pyo3::types::{PyModule, PyModuleMethods}; 3 | 4 | #[pymodule] 5 | fn readme(m: &Bound) -> PyResult<()> { 6 | m.add("value", 1)?; 7 | Ok(()) 8 | } 9 | -------------------------------------------------------------------------------- /test-crates/workspace-inheritance/generic_lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generic_lib" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import cffi_pure 4 | 5 | point = cffi_pure.lib.get_origin() 6 | point.x = 10 7 | point.y = 10 8 | assert cffi_pure.lib.is_in_range(point, 15) 9 | 10 | print("SUCCESS") 11 | -------------------------------------------------------------------------------- /test-crates/pyo3-no-extension-module/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::ffi::{PyDict_New, PyObject}; 2 | 3 | #[no_mangle] 4 | #[allow(non_snake_case)] 5 | pub unsafe extern "C" fn PyInit_pyo3_pure() -> *mut PyObject { 6 | PyDict_New() // Make sure an ffi function is used 7 | } 8 | -------------------------------------------------------------------------------- /test-crates/transitive_path_dep/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn is_sum(x: usize, y: usize, sum: usize) -> bool { 2 | x + y == sum 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | #[test] 8 | fn it_works() { 9 | assert_eq!(2 + 2, 4); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test-crates/uniffi-multiple-binding-files/mylib/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(uniffi::Enum)] 2 | pub enum Status { 3 | Running, 4 | Complete, 5 | } 6 | 7 | pub fn get_status() -> Status { 8 | Status::Complete 9 | } 10 | 11 | uniffi::setup_scaffolding!(); 12 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.pytest_cache 2 | **/.tox 3 | **/__pycache__ 4 | **/venv* 5 | .dockerignore 6 | .git 7 | .idea 8 | .travis.yml 9 | .venv 10 | /target 11 | Dockerfile* 12 | appveyor.yml 13 | ci 14 | sysconfig 15 | test-crates 16 | test-data 17 | test-dockerfile.sh 18 | -------------------------------------------------------------------------------- /src/auditwheel/mod.rs: -------------------------------------------------------------------------------- 1 | mod audit; 2 | mod musllinux; 3 | pub mod patchelf; 4 | mod platform_tag; 5 | mod policy; 6 | mod repair; 7 | 8 | pub use audit::*; 9 | pub use platform_tag::PlatformTag; 10 | pub use policy::Policy; 11 | pub use repair::find_external_libs; 12 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "cffi-mixed" 7 | dependencies = ["cffi"] 8 | dynamic = ["version"] 9 | 10 | [tool.maturin] 11 | bindings = "cffi" 12 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "cffi-pure" 7 | dependencies = ["cffi"] 8 | dynamic = ["version"] 9 | 10 | [tool.maturin] 11 | bindings = "cffi" 12 | -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/transitive_lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "transitive_lib" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include Cargo.toml Cargo.lock 2 | include README.md 3 | include license-apache license-mit 4 | recursive-include src *.rs *.py 5 | recursive-include src/auditwheel *.json 6 | recursive-include src/python_interpreter *.py *.json 7 | recursive-include src/templates *.j2 8 | -------------------------------------------------------------------------------- /test-crates/workspace/Readme.md: -------------------------------------------------------------------------------- 1 | This tests that we ignore non-existent Cargo.lock file listed by `cargo package --list`, which seems to only occur with workspaces. See https://github.com/rust-lang/cargo/issues/7938#issuecomment-593280660 and https://github.com/PyO3/maturin/issues/449 2 | -------------------------------------------------------------------------------- /test-crates/uniffi-multiple-binding-files/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import uniffi_multiple_binding_files 4 | 5 | assert uniffi_multiple_binding_files.get_status() == uniffi_multiple_binding_files.mylib.Status.COMPLETE 6 | 7 | print("SUCCESS") 8 | -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/dont_include_in_sdist/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dont_include_in_sdist" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /test-crates/some_path_dep/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use transitive_path_dep::is_sum; 2 | 3 | pub fn add(x: usize, y: usize) -> usize { 4 | x + y 5 | } 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | #[test] 10 | fn it_works() { 11 | assert_eq!(add(2, 2), 4); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg 2 | *.egg-info/ 3 | *.o 4 | *.py[cdo] 5 | *.so 6 | .pytest_cache/ 7 | .tox/ 8 | .venv/ 9 | /target/ 10 | /test-crates/*/target 11 | __pycache__/ 12 | dist/ 13 | node_modules 14 | tags 15 | test-crates/targets/ 16 | test-crates/venvs/ 17 | test-crates/wheels/ 18 | venv*/ 19 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | fn get_22() -> usize { 5 | 22 6 | } 7 | 8 | #[pymodule] 9 | fn rust(m: &Bound<'_, PyModule>) -> PyResult<()> { 10 | m.add_wrapped(wrap_pyfunction!(get_22))?; 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | fn get_21() -> usize { 5 | 21 6 | } 7 | 8 | #[pymodule] 9 | fn rust(m: &Bound<'_, PyModule>) -> PyResult<()> { 10 | m.add_wrapped(wrap_pyfunction!(get_21))?; 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/pyo3_mixed/assets/asset.txt: -------------------------------------------------------------------------------- 1 | This file is included twice, once implicitly by in the python root and once explicitly in pyproject.toml 2 | and checks that it gets deduplicated, see https://github.com/PyO3/maturin/issues/2106 and 3 | https://github.com/PyO3/maturin/issues/2066. 4 | -------------------------------------------------------------------------------- /.config/nextest.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | # Terminate slow tests after 30 minutes 3 | slow-timeout = { period = "60s", terminate-after = 30 } 4 | 5 | [[profile.default.overrides]] 6 | # See https://nexte.st/book/threads-required.html 7 | filter = 'test(/_uniffi_/)' 8 | threads-required = "num-cpus" 9 | -------------------------------------------------------------------------------- /test-crates/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | # Test references to out-of-project files 7 | readme = "../../README.md" 8 | default-run = "hello-world" 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | fn get_21() -> usize { 5 | 21 6 | } 7 | 8 | #[pymodule] 9 | fn _pyo3_mixed(m: &Bound<'_, PyModule>) -> PyResult<()> { 10 | m.add_wrapped(wrap_pyfunction!(get_21))?; 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | fn get_21() -> usize { 5 | 21 6 | } 7 | 8 | #[pymodule] 9 | fn pyo3_mixed_src(m: &Bound<'_, PyModule>) -> PyResult<()> { 10 | m.add_wrapped(wrap_pyfunction!(get_21))?; 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /test-crates/uniffi-multiple-binding-files/mylib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mylib" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | uniffi = { version = "0.28.0" } 10 | -------------------------------------------------------------------------------- /test-crates/pyo3-bin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-bin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | pyo3 = { version = "0.27.0", features = ["auto-initialize"] } 10 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-mixed src layout 2 | 3 | A package for testing maturin with a src layout mixed pyo3/python project. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | pip install . 9 | ``` 10 | 11 | ```python 12 | import pyo3_mixed_src 13 | assert pyo3_mixed_src.get_42() == 42 14 | ``` 15 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-mixed src layout 2 | 3 | A package for testing maturin with a nested workspace. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | pip install . 9 | ``` 10 | 11 | ```python 12 | import pyo3_mixed_workspace 13 | assert pyo3_mixed_workspace.get_42() == 42 14 | ``` 15 | -------------------------------------------------------------------------------- /.github/bors.toml: -------------------------------------------------------------------------------- 1 | delete_merged_branches = true 2 | required_approvals = 0 3 | use_codeowners = false 4 | status = [ 5 | # GitHub Actions 6 | "conclusion", 7 | # Cirrus CI 8 | "Test (x86_64 FreeBSD)", 9 | "Test (arm64 macOS)", 10 | "Test (arm64 Linux)" 11 | ] 12 | timeout_sec = 21600 13 | -------------------------------------------------------------------------------- /test-crates/transitive_path_dep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "transitive_path_dep" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /test-crates/workspace-inverted-order/path-dep-with-root/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pymodule] 4 | mod path_dep_with_root { 5 | use pyo3::pyfunction; 6 | use top_level::NUMBER; 7 | 8 | #[pyfunction] 9 | fn add_number(x: u32) -> u32 { 10 | x + NUMBER 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test-crates/lib_with_path_dep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lib_with_path_dep" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | pyo3 = "0.27.0" 12 | some_path_dep = { path = "../some_path_dep" } 13 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/tests/test_pyo3_mixed_implicit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_mixed_implicit.some_rust as some_rust 4 | 5 | 6 | def test_get_rust_and_python(): 7 | assert some_rust.get_22() == 22 8 | assert some_rust.double(lambda: 4) == 8 9 | 10 | 11 | print("SUCCESS") 12 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | fn get_21() -> usize { 5 | 21 6 | } 7 | 8 | #[pymodule] 9 | fn pyo3_mixed_include_exclude(m: &Bound<'_, PyModule>) -> PyResult<()> { 10 | m.add_wrapped(wrap_pyfunction!(get_21))?; 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/local-test/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: usize, right: usize) -> usize { 2 | left + right 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | use super::*; 8 | 9 | #[test] 10 | fn it_works() { 11 | let result = add(2, 2); 12 | assert_eq!(result, 4); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test-crates/sdist_with_path_dep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sdist_with_path_dep" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | pyo3 = "0.27.0" 12 | some_path_dep = { path = "../some_path_dep" } 13 | -------------------------------------------------------------------------------- /test-crates/with-data/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | use std::os::raw::{c_char, c_int}; 3 | 4 | #[no_mangle] 5 | pub unsafe extern "C" fn say_hello() -> *const c_char { 6 | CString::new("hello").unwrap().into_raw() 7 | } 8 | 9 | #[no_mangle] 10 | pub unsafe extern "C" fn one() -> c_int { 11 | 1 12 | } 13 | -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/generic_lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generic_lib" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | transitive_lib = { path = "../transitive_lib" } 10 | -------------------------------------------------------------------------------- /test-crates/cargo-mock/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-mock" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | 7 | [[bin]] 8 | name = "cargo" 9 | path = "src/main.rs" 10 | 11 | [dependencies] 12 | anyhow = "1.0.38" 13 | cargo_metadata = "0.18.1" 14 | serde_json = "1.0.62" 15 | -------------------------------------------------------------------------------- /test-crates/readme-duplication/readme-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "readme_py" 3 | version = "0.2.0" 4 | edition = "2021" 5 | readme = "README.md" 6 | publish = false 7 | 8 | [lib] 9 | name = "readme" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | readme-rs = { path = "../readme-rs" } 14 | pyo3 = "0.27.0" 15 | -------------------------------------------------------------------------------- /test-crates/license-test/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "license-test" 7 | license = { file = "LICENCE.txt" } 8 | dynamic = ["version", "license"] # Allow license from Cargo.toml to be used 9 | 10 | [tool.maturin] 11 | bindings = "bin" 12 | -------------------------------------------------------------------------------- /test-crates/lib_with_disallowed_lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lib_with_disallowed_lib" 3 | version = "0.1.0" 4 | authors = ["messense "] 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | libz-sys = { version = "1.1.2", default-features = false } 12 | pyo3 = "0.27.0" 13 | -------------------------------------------------------------------------------- /guide/tweak.css: -------------------------------------------------------------------------------- 1 | .sponsors { 2 | display: flex; 3 | justify-content: left; 4 | flex-wrap: wrap; 5 | align-items: center; 6 | margin: 1rem 0; 7 | } 8 | 9 | .sponsors > div { 10 | text-align: center; 11 | width: 25%; 12 | padding-bottom: 20px; 13 | } 14 | 15 | .sponsors span { 16 | display: block; 17 | } 18 | -------------------------------------------------------------------------------- /test-crates/pyo3-ffi-pure/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-ffi-pure" 3 | version = "1.0.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | pyo3-ffi = { version = "0.27.0", features = ["abi3-py39"] } 8 | 9 | [build-dependencies] 10 | pyo3-build-config = "0.27.0" 11 | 12 | [lib] 13 | name = "pyo3_ffi_pure" 14 | crate-type = ["cdylib"] 15 | -------------------------------------------------------------------------------- /guide/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["PyO3 Project and Contributors"] 3 | language = "en" 4 | src = "src" 5 | title = "Maturin User Guide" 6 | 7 | [output.html] 8 | additional-css = ["tweak.css"] 9 | git-repository-url = "https://github.com/PyO3/maturin/tree/main/guide" 10 | edit-url-template = "https://github.com/PyO3/maturin/edit/main/guide/{path}" 11 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import cffi_mixed 4 | 5 | from cffi_mixed import Line 6 | 7 | point = cffi_mixed.lib.get_origin() 8 | point.x = 10 9 | point.y = 10 10 | assert cffi_mixed.lib.is_in_range(point, 15) 11 | 12 | assert Line(2, 5, 6, 8).length() == 5 13 | 14 | print("SUCCESS") 15 | -------------------------------------------------------------------------------- /test-crates/pyo3-abi3-without-version/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-abi3-without-version" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | pyo3 = { version = "0.27.0", features = ["abi3"] } 9 | 10 | [lib] 11 | name = "pyo3_abi3_without_version" 12 | crate-type = ["cdylib"] 13 | -------------------------------------------------------------------------------- /test-crates/pyo3-ffi-pure/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-ffi-pure 2 | 3 | A package with pyo3-ffi bindings for testing maturin. 4 | 5 | ## Usage 6 | 7 | ```python 8 | import pyo3_ffi_pure 9 | assert pyo3_ffi_pure.sum(2, 40) == 42 10 | ``` 11 | 12 | ## Testing 13 | 14 | Install `pytest` and run: 15 | 16 | ```bash 17 | pytest -v test_pyo3_ffi_pure.py 18 | ``` 19 | -------------------------------------------------------------------------------- /test-crates/license-test/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | from subprocess import check_output 2 | 3 | 4 | def main(): 5 | output = check_output(["license-test"]).decode("utf-8").strip() 6 | if not output == "Hello, world!": 7 | raise Exception(output) 8 | print("SUCCESS") 9 | 10 | 11 | if __name__ == "__main__": 12 | main() 13 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-with-path-dep/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "pyo3-mixed-with-path-dep" 7 | classifiers = [ 8 | "Programming Language :: Python", 9 | "Programming Language :: Rust" 10 | ] 11 | requires-python = ">=3.7" 12 | dynamic = ["version"] 13 | -------------------------------------------------------------------------------- /test-crates/some_path_dep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "some_path_dep" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | transitive_path_dep = { path = "../transitive_path_dep" } 11 | -------------------------------------------------------------------------------- /test-crates/workspace-inheritance/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "generic_lib", 5 | "python" 6 | ] 7 | 8 | [workspace.package] 9 | version = "0.1.0" 10 | 11 | [workspace.dependencies] 12 | cfg-if = "1.0.0" 13 | libc = { version = "0.2", features = ["std"] } 14 | rand = "0.8" 15 | generic_lib = { path = "generic_lib" } 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: ❓ Question 4 | url: https://github.com/PyO3/maturin/discussions 5 | about: Ask and answer questions about maturin on Discussions 6 | - name: 🔧 Troubleshooting 7 | url: https://github.com/PyO3/maturin/discussions 8 | about: For troubleshooting help, see the Discussions 9 | -------------------------------------------------------------------------------- /test-crates/pyo3-bin/src/main.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | fn main() -> PyResult<()> { 4 | Python::attach(|py| { 5 | let builtins = py.import("builtins")?; 6 | let total: i32 = builtins.getattr("sum")?.call1((vec![1, 2, 3],))?.extract()?; 7 | assert_eq!(total, 6); 8 | println!("Hello, world!"); 9 | Ok(()) 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/python/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "workspace_with_path_dep" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | pyo3 = "0.27.0" 12 | generic_lib = { path = "../generic_lib" } 13 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-with-path-dep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [] 3 | name = "pyo3-mixed-with-path-dep" 4 | version = "2.1.3" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | pyo3 = { version = "0.27.0", features = ["generate-import-lib", ] } 9 | some_path_dep = { path = "../some_path_dep" } 10 | 11 | [lib] 12 | name = "pyo3_mixed_with_path_dep" 13 | crate-type = ["cdylib"] 14 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-with-path-dep/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import pyo3_mixed_with_path_dep 3 | 4 | assert pyo3_mixed_with_path_dep.get_42() == 42, "get_42 did not return 42" 5 | 6 | assert pyo3_mixed_with_path_dep.is_half(21, 42), "21 is not half of 42" 7 | assert not pyo3_mixed_with_path_dep.is_half(21, 73), "21 is half of 63" 8 | 9 | print("SUCCESS") 10 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["konstin "] 3 | name = "pyo3-mixed" 4 | version = "2.1.5" 5 | description = "Implements a dummy function combining rust and python" 6 | edition = "2021" 7 | 8 | [dependencies] 9 | pyo3 = { version = "0.27.0", features = ["generate-import-lib"] } 10 | 11 | [lib] 12 | name = "pyo3_mixed" 13 | crate-type = ["cdylib"] 14 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["konstin "] 3 | name = "pyo3-mixed-py-subdir" 4 | version = "2.1.3" 5 | description = "Implements a dummy function combining rust and python" 6 | readme = "README.md" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | pyo3 = "0.27.0" 11 | 12 | [lib] 13 | name = "pyo3_mixed_py_subdir" 14 | crate-type = ["cdylib"] 15 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["konstin "] 3 | name = "pyo3-mixed-submodule" 4 | version = "2.1.3" 5 | description = "Implements a dummy function combining rust and python" 6 | readme = "README.md" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | pyo3 = "0.27.0" 11 | 12 | [lib] 13 | name = "pyo3_mixed_submodule" 14 | crate-type = ["cdylib"] 15 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "pyo3-mixed-submodule" 7 | classifiers = [ 8 | "Programming Language :: Python", 9 | "Programming Language :: Rust" 10 | ] 11 | dynamic = ["version"] 12 | 13 | [tool.maturin] 14 | module-name = "pyo3_mixed_submodule.rust_module.rust" 15 | -------------------------------------------------------------------------------- /test-crates/workspace-inverted-order/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | ".", 4 | "path-dep-with-root", 5 | ] 6 | 7 | [workspace.package] 8 | authors = [] 9 | version = "0.10.2-dev" 10 | publish = false 11 | repository = "https://github.com/mitmproxy/mitmproxy-rs" 12 | 13 | [package] 14 | name = "top_level" 15 | version = "0.1.0" 16 | description = "root dep" 17 | edition = "2021" 18 | -------------------------------------------------------------------------------- /test-crates/lib_with_path_dep/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | fn add(x: usize, y: usize) -> usize { 5 | let sum = some_path_dep::add(x, y); 6 | debug_assert!(some_path_dep::is_sum(x, y, sum)); 7 | sum 8 | } 9 | 10 | #[pymodule] 11 | fn lib_with_path_dep(m: &Bound<'_, PyModule>) -> PyResult<()> { 12 | m.add_wrapped(wrap_pyfunction!(add))?; 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/rust/python/pyo3-mixed-workspace-py/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | use pyo3_mixed_workspace::get_21_lib; 4 | 5 | #[pyfunction] 6 | fn get_21() -> usize { 7 | get_21_lib() 8 | } 9 | 10 | #[pymodule] 11 | fn pyo3_mixed_workspace_py(m: &Bound<'_, PyModule>) -> PyResult<()> { 12 | m.add_function(wrap_pyfunction!(get_21, m)?)?; 13 | 14 | Ok(()) 15 | } 16 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/tests/test_pyo3_pure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyo3_pure 4 | 5 | import pytest 6 | 7 | 8 | def test_static(): 9 | assert pyo3_pure.fourtytwo == 42 10 | 11 | 12 | def test_class(): 13 | assert pyo3_pure.DummyClass.get_42() == 42 14 | 15 | 16 | def test_function(): 17 | with pytest.raises(AssertionError): 18 | assert pyo3_pure.DummyClass == 42 19 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [] 3 | name = "pyo3-mixed-include-exclude" 4 | version = "2.1.3" 5 | description = "Implements a dummy function combining rust and python" 6 | edition = "2021" 7 | 8 | [dependencies] 9 | pyo3 = { version = "0.27.0", features = ["generate-import-lib"] } 10 | 11 | [lib] 12 | name = "pyo3_mixed_include_exclude" 13 | crate-type = ["cdylib"] 14 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["konstin "] 3 | name = "pyo3-mixed-src" 4 | version = "2.1.3" 5 | description = "Implements a dummy function combining rust and python" 6 | edition = "2021" 7 | 8 | [dependencies] 9 | pyo3 = { version = "0.27.0", features = ["generate-import-lib"] } 10 | 11 | [lib] 12 | name = "pyo3_mixed_src" 13 | crate-type = ["cdylib"] 14 | -------------------------------------------------------------------------------- /test-crates/sdist_with_path_dep/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | fn add(x: usize, y: usize) -> usize { 5 | let sum = some_path_dep::add(x, y); 6 | debug_assert!(some_path_dep::is_sum(x, y, sum)); 7 | sum 8 | } 9 | 10 | #[pymodule] 11 | fn sdist_with_path_dep(m: &Bound<'_, PyModule>) -> PyResult<()> { 12 | m.add_wrapped(wrap_pyfunction!(add))?; 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /test-crates/sdist_with_target_path_dep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sdist_with_target_path_dep" 3 | version = "0.1.0" 4 | authors = ["konstin "] 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | pyo3 = "0.27.0" 12 | 13 | [target.'cfg(not(target_endian = "all-over-the-place"))'.dependencies] 14 | some_path_dep = { path = "../some_path_dep" } 15 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Kevin Patterson "] 3 | name = "pyo3-mixed-implicit" 4 | version = "2.1.3" 5 | description = "Implements a dummy function combining rust and python" 6 | readme = "README.md" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | pyo3 = "0.27.0" 11 | 12 | [lib] 13 | name = "pyo3_mixed_implicit" 14 | crate-type = ["cdylib"] 15 | -------------------------------------------------------------------------------- /test-crates/workspace-inverted-order/path-dep-with-root/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "path_dep_with_root" 3 | version = "0.1.0" 4 | description = "path dep" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "path_dep_with_root" 9 | crate-type = ["lib", "cdylib"] 10 | 11 | [dependencies] 12 | top_level = { path = "../" } 13 | pyo3 = { version = "0.27.0", features = [ 14 | "abi3", 15 | "abi3-py38", 16 | ] } 17 | -------------------------------------------------------------------------------- /.devcontainer/post_create.sh: -------------------------------------------------------------------------------- 1 | set -euxo pipefail 2 | 3 | sudo apt-get update 4 | sudo apt-get install -y python3-dev python3-pip python3-venv libclang-dev 5 | sudo python3 -m pip install cffi virtualenv pipx 6 | 7 | pipx ensurepath 8 | pipx install uniffi-bindgen 9 | pipx install cargo-deny 10 | 11 | rustup target add wasm32-wasip1 12 | curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin 13 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "pyo3_mixed_implicit" 7 | classifiers = [ 8 | "Programming Language :: Python", 9 | "Programming Language :: Rust" 10 | ] 11 | dynamic = ["version"] 12 | 13 | [tool.maturin] 14 | module-name = "pyo3_mixed_implicit.some_rust.rust" 15 | python-source = "python" 16 | -------------------------------------------------------------------------------- /test-crates/sdist_with_target_path_dep/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | fn add(x: usize, y: usize) -> usize { 5 | let sum = some_path_dep::add(x, y); 6 | debug_assert!(some_path_dep::is_sum(x, y, sum)); 7 | sum 8 | } 9 | 10 | #[pymodule] 11 | fn sdist_with_target_path_dep(m: &Bound<'_, PyModule>) -> PyResult<()> { 12 | m.add_wrapped(wrap_pyfunction!(add))?; 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-pure" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | name = "uniffi_pure" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | uniffi = "0.28.0" 14 | 15 | [build-dependencies] 16 | uniffi = { version = "0.28.0", features = ["build"] } 17 | -------------------------------------------------------------------------------- /test-crates/uniffi-mixed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-mixed" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | name = "uniffi_mixed" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | uniffi = "0.28.0" 14 | 15 | [build-dependencies] 16 | uniffi = { version = "0.28.0", features = ["build"] } 17 | -------------------------------------------------------------------------------- /test-crates/pyo3-no-extension-module/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["konstin "] 3 | name = "pyo3-no-extension-module" 4 | version = "2.1.0" 5 | description = "Does not use the extension-module feature, thus erroneously linking libpython" 6 | edition = "2021" 7 | 8 | [dependencies] 9 | pyo3 = { version = "0.25.0", default-features = false } 10 | 11 | [lib] 12 | name = "pyo3_no_extension_module" 13 | crate-type = ["cdylib"] 14 | -------------------------------------------------------------------------------- /.github/workflows/clear-cache.yml: -------------------------------------------------------------------------------- 1 | name: Clear Actions Cache 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | actions: write 8 | 9 | jobs: 10 | clear-cache: 11 | name: Clean Cache 12 | runs-on: ubuntu-latest 13 | env: 14 | GH_TOKEN: ${{ github.token }} 15 | steps: 16 | - name: Clear cache 17 | run: | 18 | gh cache delete --all --repo "$GITHUB_REPOSITORY" 19 | echo "cache cleared" 20 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | struct DummyClass {} 5 | 6 | #[pymethods] 7 | impl DummyClass { 8 | #[staticmethod] 9 | fn get_42() -> PyResult { 10 | Ok(42) 11 | } 12 | } 13 | 14 | /// module level doc string 15 | #[pymodule] 16 | fn pyo3_pure(m: &Bound<'_, PyModule>) -> PyResult<()> { 17 | m.add_class::()?; 18 | m.add("fourtytwo", 42)?; 19 | 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/test_cffi_pure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import cffi_pure 4 | 5 | 6 | def test_range(): 7 | point = cffi_pure.lib.get_origin() 8 | point.x = 10 9 | point.y = 10 10 | 11 | assert not cffi_pure.lib.is_in_range(point, 14) 12 | assert cffi_pure.lib.is_in_range(point, 15) 13 | 14 | 15 | def test_ffi(): 16 | point = cffi_pure.ffi.new("Point *", (10, 20)) 17 | assert point.x == 10 18 | assert point.y == 20 19 | -------------------------------------------------------------------------------- /test-crates/workspace-inheritance/python/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | /// Formats the sum of two numbers as string. 4 | #[pyfunction] 5 | fn sum_as_string(a: usize, b: usize) -> PyResult { 6 | Ok((a + b).to_string()) 7 | } 8 | 9 | /// A Python module implemented in Rust. 10 | #[pymodule] 11 | fn workspace_with_path_dep(m: &Bound<'_, PyModule>) -> PyResult<()> { 12 | m.add_function(wrap_pyfunction!(sum_as_string, m)?)?; 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /test-crates/workspace_with_path_dep/python/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | /// Formats the sum of two numbers as string. 4 | #[pyfunction] 5 | fn sum_as_string(a: usize, b: usize) -> PyResult { 6 | Ok((a + b).to_string()) 7 | } 8 | 9 | /// A Python module implemented in Rust. 10 | #[pymodule] 11 | fn workspace_with_path_dep(m: &Bound<'_, PyModule>) -> PyResult<()> { 12 | m.add_function(wrap_pyfunction!(sum_as_string, m)?)?; 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-src/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "pyo3-mixed-src" 7 | classifiers = [ 8 | "Programming Language :: Python", 9 | "Programming Language :: Rust" 10 | ] 11 | requires-python = ">=3.7" 12 | dynamic = ["version"] 13 | 14 | [project.scripts] 15 | get_42 = "pyo3_mixed_src:get_42" 16 | 17 | [tool.maturin] 18 | python-packages = ["pyo3_mixed_src", "tests"] 19 | -------------------------------------------------------------------------------- /test-crates/pyo3-ffi-pure/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "pyo3-ffi-pure" 7 | classifiers = [ 8 | "Programming Language :: Rust" 9 | ] 10 | description = "Tests compilation of packages using pyo3-ffi bindings" 11 | readme = "README.md" 12 | maintainers = [ 13 | {name = "messense", email = "messense@icloud.com"} 14 | ] 15 | license = { file = "LICENSE" } 16 | dynamic = ["version"] 17 | -------------------------------------------------------------------------------- /test-crates/hello-world/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | from subprocess import check_output 2 | 3 | 4 | def main(): 5 | output = check_output(["hello-world"]).decode("utf-8").strip() 6 | if not output == "Hello, world!": 7 | raise Exception(output) 8 | 9 | output = check_output(["foo"]).decode("utf-8").strip() 10 | if not output == "🦀 Hello, world! 🦀": 11 | raise Exception(output) 12 | print("SUCCESS") 13 | 14 | 15 | if __name__ == "__main__": 16 | main() 17 | -------------------------------------------------------------------------------- /test-crates/hello-world/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | license-files = ["LICENSE", "licenses/*"] 5 | dynamic = ["authors", "readme"] # Allow authors and readme from Cargo.toml to be used 6 | 7 | [build-system] 8 | requires = ["maturin>=1.0,<2.0"] 9 | build-backend = "maturin" 10 | 11 | [tool.maturin] 12 | bindings = "bin" 13 | 14 | [[tool.maturin.targets]] 15 | name = "hello-world" 16 | bindings = "bin" 17 | 18 | [[tool.maturin.targets]] 19 | name = "foo" 20 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-mixed 2 | 3 | A package for testing maturin with a mixed pyo3/python project. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | pip install . 9 | ``` 10 | 11 | ```python 12 | import pyo3_mixed 13 | assert pyo3_mixed.get_42() == 42 14 | ``` 15 | 16 | ## Testing 17 | 18 | Install tox: 19 | 20 | ```bash 21 | pip install tox 22 | ``` 23 | 24 | Run it: 25 | 26 | ```bash 27 | tox 28 | ``` 29 | 30 | The tests are in `test_pyo3_mixed.py`, while the configuration is in tox.ini 31 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/README.md: -------------------------------------------------------------------------------- 1 | # Get fourtytwo 2 | 3 | A package with pyo3 bindings for testing maturin. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | pip install . 9 | ``` 10 | 11 | ```python 12 | import pyo3_pure 13 | assert pyo3_pure.DummyClass.get_42() == 42 14 | ``` 15 | 16 | ## Testing 17 | 18 | Install tox: 19 | 20 | ```bash 21 | pip install tox 22 | ``` 23 | 24 | Run it: 25 | 26 | ```bash 27 | tox 28 | ``` 29 | 30 | The tests are in `test_pyo3_pure.py`, while the configuration is in tox.ini 31 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["konstin "] 3 | name = "pyo3-pure" 4 | version = "2.1.2" 5 | edition = "2021" 6 | description = "Implements a dummy function (get_fortytwo.DummyClass.get_42()) in rust" 7 | license = "MIT" 8 | 9 | [dependencies] 10 | pyo3 = { version = "0.27.0", features = [ 11 | "abi3-py37", 12 | "generate-import-lib", 13 | ] } 14 | 15 | [lib] 16 | name = "pyo3_pure" 17 | crate-type = ["cdylib"] 18 | 19 | [workspace] 20 | members = [".", "local-test"] 21 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/rust/python/pyo3-mixed-workspace-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["konstin "] 3 | name = "pyo3-mixed-workspace-py" 4 | version = "2.1.3" 5 | description = "Implements a dummy function combining rust and python" 6 | edition = "2021" 7 | 8 | [dependencies] 9 | pyo3-mixed-workspace = { path = "../../pyo3-mixed-workspace" } 10 | pyo3 = { version = "0.27.0", features = ["generate-import-lib", ] } 11 | 12 | [lib] 13 | name = "pyo3_mixed_workspace_py" 14 | crate-type = ["cdylib"] 15 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import subprocess 4 | 5 | import pyo3_pure 6 | 7 | assert pyo3_pure.DummyClass.get_42() == 42 8 | 9 | # Check type stub 10 | install_path = os.path.join(os.path.dirname(pyo3_pure.__file__)) 11 | assert os.path.exists(os.path.join(install_path, "__init__.pyi")) 12 | assert os.path.exists(os.path.join(install_path, "py.typed")) 13 | 14 | # Check entrypoints 15 | assert subprocess.run(["get_42"]).returncode == 42 16 | 17 | print("SUCCESS") 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💡 Feature request 3 | about: Suggest an idea for this project 4 | labels: [enhancement] 5 | --- 6 | 7 | 15 | -------------------------------------------------------------------------------- /test-crates/uniffi-multiple-binding-files/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-multiple-binding-files" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | name = "uniffi_multiple_binding_files" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | uniffi = { version = "0.28.0", features = ["cli"] } 14 | mylib = { path = "mylib" } 15 | 16 | [build-dependencies] 17 | uniffi = { version = "0.28.0", features = ["build"] } 18 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "pyo3-mixed" 7 | classifiers = [ 8 | "Programming Language :: Python", 9 | "Programming Language :: Rust" 10 | ] 11 | requires-python = ">=3.7" 12 | dependencies = ["boltons"] 13 | license = "MIT" 14 | dynamic = ["version"] 15 | 16 | [project.scripts] 17 | get_42 = "pyo3_mixed:get_42" 18 | print_cli_args = "pyo3_mixed:print_cli_args" 19 | 20 | [tool.maturin] 21 | include = ["pyo3_mixed/assets/*"] 22 | -------------------------------------------------------------------------------- /src/templates/lib.rs.j2: -------------------------------------------------------------------------------- 1 | {%- if bindings == "pyo3" -%} 2 | use pyo3::prelude::*; 3 | 4 | /// A Python module implemented in Rust. 5 | #[pymodule] 6 | mod {{crate_name}} { 7 | use pyo3::prelude::*; 8 | 9 | /// Formats the sum of two numbers as string. 10 | #[pyfunction] 11 | fn sum_as_string(a: usize, b: usize) -> PyResult { 12 | Ok((a + b).to_string()) 13 | } 14 | } 15 | {%- elif bindings == "uniffi" -%} 16 | fn add(a: u32, b: u32) -> u32 { 17 | a + b 18 | } 19 | 20 | uniffi::include_scaffolding!("example"); 21 | {%- endif %} 22 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-mixed 2 | 3 | A package for testing maturin with a mixed pyo3/python project and a non-default package name. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | pip install . 9 | ``` 10 | 11 | ```python 12 | import pyo3_mixed_py_subdir 13 | assert pyo3_mixed_py_subdir.get_42() == 42 14 | ``` 15 | 16 | ## Testing 17 | 18 | Install tox: 19 | 20 | ```bash 21 | pip install tox 22 | ``` 23 | 24 | Run it: 25 | 26 | ```bash 27 | tox 28 | ``` 29 | 30 | The tests are in `test_pyo3_mixed.py`, while the configuration is in tox.ini 31 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-with-path-dep/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-mixed 2 | 3 | A package for testing maturin with a mixed pyo3/python project. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | pip install . 9 | ``` 10 | 11 | ```python 12 | import pyo3_mixed_with_path_dep 13 | assert pyo3_mixed_with_path_dep.get_42() == 42 14 | ``` 15 | 16 | ## Testing 17 | 18 | Install tox: 19 | 20 | ```bash 21 | pip install tox 22 | ``` 23 | 24 | Run it: 25 | 26 | ```bash 27 | tox 28 | ``` 29 | 30 | The tests are in `tests/test_pyo3_mixed_with_path_dep.py`, while the configuration is in tox.ini 31 | -------------------------------------------------------------------------------- /src/templates/Cargo.toml.j2: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{ name }}" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | {%- if bindings != "bin" %} 8 | [lib] 9 | name = "{{ crate_name }}" 10 | crate-type = ["cdylib"] 11 | {%- endif %} 12 | 13 | [dependencies] 14 | {% if bindings == "pyo3" -%} 15 | pyo3 = "0.27.0" 16 | {% elif bindings == "uniffi" -%} 17 | uniffi = "0.28.0" 18 | 19 | [build-dependencies] 20 | uniffi = { version = "0.28.0", features = ["build"] } 21 | {% endif -%} 22 | -------------------------------------------------------------------------------- /test-crates/lib_with_disallowed_lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_ulong; 2 | 3 | use pyo3::prelude::*; 4 | 5 | #[link(name = "z")] 6 | extern "C" { 7 | fn gzflags() -> c_ulong; 8 | } 9 | 10 | #[pyfunction] 11 | fn add(x: usize, y: usize) -> usize { 12 | let _version = unsafe { libz_sys::zlibVersion() }; 13 | let _flags = unsafe { gzflags() }; 14 | let sum = x + y; 15 | sum 16 | } 17 | 18 | #[pymodule] 19 | fn lib_with_disallowed_lib(m: &Bound<'_, PyModule>) -> PyResult<()> { 20 | m.add_wrapped(wrap_pyfunction!(add))?; 21 | 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-submodule/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-mixed-submodule 2 | 3 | A package for testing maturin with a mixed pyo3/python project with Rust submodule. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | pip install . 9 | ``` 10 | 11 | ```python 12 | import pyo3_mixed_submodule 13 | assert pyo3_mixed_submodule.get_42() == 42 14 | ``` 15 | 16 | ## Testing 17 | 18 | Install tox: 19 | 20 | ```bash 21 | pip install tox 22 | ``` 23 | 24 | Run it: 25 | 26 | ```bash 27 | tox 28 | ``` 29 | 30 | The tests are in `tests/test_pyo3_mixed_submodule.py`, while the configuration is in tox.ini 31 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-py-subdir/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "pyo3-mixed-py-subdir" 7 | classifiers = [ 8 | "Programming Language :: Python", 9 | "Programming Language :: Rust" 10 | ] 11 | requires-python = ">=3.6" 12 | dynamic = ["version", "description"] # Allow description from Cargo.toml to be used 13 | 14 | [project.scripts] 15 | get_42 = "pyo3_mixed_py_subdir:get_42" 16 | 17 | [tool.maturin] 18 | module-name = "pyo3_mixed_py_subdir._pyo3_mixed" 19 | python-source = "python" 20 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maturin", 3 | "image": "mcr.microsoft.com/devcontainers/rust:bullseye", 4 | "postCreateCommand": "bash .devcontainer/post_create.sh", 5 | "customizations": { 6 | "vscode": { 7 | "extensions": [ 8 | "ms-python.black-formatter", 9 | "rust-lang.rust-analyzer", 10 | "charliermarsh.ruff" 11 | ], 12 | "settings": { 13 | "editor.formatOnSave": true 14 | } 15 | } 16 | }, 17 | "features": { 18 | "ghcr.io/devcontainers/features/sshd:1": { 19 | "version": "latest" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-implicit/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-mixed-implicit 2 | 3 | A package for testing maturin with a mixed pyo3/python project with an implicit namespace package and Rust submodule. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | pip install . 9 | ``` 10 | 11 | ```python 12 | import pyo3_mixed_implicit 13 | assert pyo3_mixed_implicit.some_rust.get_22() == 22 14 | ``` 15 | 16 | ## Testing 17 | 18 | Install tox: 19 | 20 | ```bash 21 | pip install tox 22 | ``` 23 | 24 | Run it: 25 | 26 | ```bash 27 | tox 28 | ``` 29 | 30 | The tests are in `tests/test_pyo3_mixed_implicit.py`, while the configuration is in tox.ini 31 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-mixed-include-exclude 2 | 3 | A package for testing maturin with a mixed pyo3/python project with include and exclude configurations. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | pip install . 9 | ``` 10 | 11 | ```python 12 | import pyo3_mixed_include_exclude 13 | assert pyo3_mixed_include_exclude.get_42() == 42 14 | ``` 15 | 16 | ## Testing 17 | 18 | Install tox: 19 | 20 | ```bash 21 | pip install tox 22 | ``` 23 | 24 | Run it: 25 | 26 | ```bash 27 | tox 28 | ``` 29 | 30 | The tests are in `test_pyo3_mixed_include_exclude.py`, while the configuration is in tox.ini 31 | -------------------------------------------------------------------------------- /test-crates/uniffi-pure-proc-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-pure-proc-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | name = "uniffi_pure_proc_macro" 10 | crate-type = ["cdylib"] 11 | 12 | [[bin]] 13 | name = "uniffi-bindgen" 14 | path = "uniffi-bindgen.rs" 15 | required-features = ["bindgen"] 16 | 17 | [features] 18 | bindgen = ["uniffi/cli"] 19 | 20 | [dependencies] 21 | uniffi = "0.28.0" 22 | 23 | [build-dependencies] 24 | uniffi = { version = "0.28.0", features = ["build"] } 25 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-workspace/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "pyo3-mixed-workspace" 7 | classifiers = [ 8 | "Programming Language :: Python", 9 | "Programming Language :: Rust" 10 | ] 11 | requires-python = ">=3.7" 12 | dynamic = ["version"] 13 | 14 | [project.scripts] 15 | get_42 = "pyo3_mixed_workspace:get_42" 16 | 17 | [tool.maturin] 18 | python-packages = ["pyo3_mixed_workspace", "tests"] 19 | module-name = "pyo3_mixed_workspace.pyo3_mixed_workspace_py" 20 | manifest-path = "rust/python/pyo3-mixed-workspace-py/Cargo.toml" 21 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | ## Code Standards 2 | 3 | ### Required Before Each Commit 4 | 5 | - Run `cargo clippy --tests --all-features -- -D warnings` to ensure the code builds successfully and address any warnings. 6 | - Run `cargo fmt` to ensure the code is formatted correctly. 7 | - Run `cargo test` to ensure all tests pass. 8 | - Run `pre-commit` to ensure code quality and consistency. 9 | 10 | ## Key Guidelines 11 | 12 | - Follow Rust best practices and idiomatic patterns. 13 | - Maintain existing code structure and organization by default unless instructed to refactor or refactoring can improve maintainability or performance. 14 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-with-path-dep/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use some_path_dep::{add, is_sum}; 3 | 4 | #[pyfunction] 5 | fn get_21() -> usize { 6 | 21 7 | } 8 | 9 | #[pyfunction] 10 | fn add_21(num: usize) -> usize { 11 | add(num, get_21()) 12 | } 13 | 14 | #[pyfunction] 15 | fn is_half(a: usize, b: usize) -> bool { 16 | is_sum(a, a, b) 17 | } 18 | 19 | 20 | #[pymodule] 21 | fn pyo3_mixed_with_path_dep(m: &Bound<'_, PyModule>) -> PyResult<()> { 22 | m.add_wrapped(wrap_pyfunction!(get_21))?; 23 | m.add_wrapped(wrap_pyfunction!(add_21))?; 24 | m.add_wrapped(wrap_pyfunction!(is_half))?; 25 | 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/test_cffi_mixed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import cffi_mixed 4 | 5 | from cffi_mixed import Line 6 | 7 | 8 | def test_in_range(): 9 | point = cffi_mixed.lib.get_origin() 10 | point.x = 10 11 | point.y = 10 12 | 13 | assert not cffi_mixed.lib.is_in_range(point, 14) 14 | assert cffi_mixed.lib.is_in_range(point, 15) 15 | 16 | 17 | def test_ffi(): 18 | point = cffi_mixed.ffi.new("Point *", (10, 20)) 19 | assert point.x == 10 20 | assert point.y == 20 21 | 22 | 23 | def test_line(): 24 | line = Line(2, 5, 6, 8) 25 | assert line.length() == 5 26 | assert str(line) == "Line from (2.0,5.0) to (6.0,8.0)" 27 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed-include-exclude/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "pyo3-mixed-include-exclude" 7 | classifiers = ["Programming Language :: Python", "Programming Language :: Rust"] 8 | requires-python = ">=3.7" 9 | dynamic = ["version"] 10 | 11 | [project.scripts] 12 | get_42 = "pyo3_mixed_include_exclude:get_42" 13 | 14 | [tool.maturin] 15 | include = [ 16 | "pyo3_mixed_include_exclude/include_this_file", 17 | "missing", 18 | "README.md", 19 | ] 20 | exclude = [ 21 | "pyo3_mixed_include_exclude/exclude_this_file", 22 | "pyo3_mixed_include_exclude/.gitignore", 23 | "tests/**/*", 24 | "unused", 25 | ] 26 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/cffi_mixed/line.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | from .cffi_mixed import ffi 4 | 5 | 6 | class Line: 7 | def __init__(self, x1: float, y1: float, x2: float, y2: float): 8 | # You can pass a tuple/list or a dict as value for a public rust struct 9 | self.start = ffi.new("Point *", {"x": x1, "y": y1}) 10 | self.end = ffi.new("Point *", (x2, y2)) 11 | 12 | def length(self) -> float: 13 | """Returns the length of the line.""" 14 | return math.sqrt((self.end.x - self.start.x) ** 2 + (self.end.y - self.start.y) ** 2) 15 | 16 | def __str__(self) -> str: 17 | return "Line from ({},{}) to ({},{})".format(self.start.x, self.start.y, self.end.x, self.end.y) 18 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/Readme.md: -------------------------------------------------------------------------------- 1 | # cffi-pure 2 | 3 | A package for testing a crate with cffi bindings with maturin. 4 | 5 | Read the [cffi guide](https://cffi.readthedocs.io/en/latest/index.html) to learn how to use the generated `ffi` and `lib` objects. 6 | 7 | ## Usage 8 | 9 | ```bash 10 | pip install . 11 | ``` 12 | 13 | ```python 14 | import cffi_pure 15 | 16 | point = cffi_pure.lib.get_origin() 17 | point.x = 10 18 | point.y = 10 19 | assert cffi_pure.lib.is_in_range(point, 15) 20 | ``` 21 | 22 | ## Testing 23 | 24 | Install tox: 25 | 26 | ```bash 27 | pip install tox 28 | ``` 29 | 30 | Run it: 31 | 32 | ```bash 33 | tox 34 | ``` 35 | 36 | The tests are in `test_cffi_pure.py`, while the configuration is in tox.ini 37 | -------------------------------------------------------------------------------- /test-crates/pyo3-bin/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import sys 4 | from subprocess import check_output 5 | 6 | 7 | def main(): 8 | if platform.system().lower() == "windows": 9 | # Add sys.base_prefix which contains python3y.dll to PATH 10 | # otherwise running `pyo3-bin` might return exit code 3221225781 11 | path = os.environ["PATH"] 12 | path = path + os.pathsep + sys.base_prefix 13 | os.environ["PATH"] = path 14 | 15 | output = check_output(["pyo3-bin"]).decode("utf-8").strip() 16 | if not output == "Hello, world!": 17 | raise Exception(output) 18 | print("SUCCESS") 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /guide/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Introduction](index.md) 4 | 5 | --- 6 | 7 | - [Installation](./installation.md) 8 | - [Tutorial](./tutorial.md) 9 | - [Project Layout](./project_layout.md) 10 | - [Bindings](./bindings.md) 11 | - [Python Metadata](./metadata.md) 12 | - [Configuration](./config.md) 13 | - [Environment Variables](./environment-variables.md) 14 | - [Local Development](./local_development.md) 15 | - [Import Hook](./import_hook.md) 16 | - [Distribution](./distribution.md) 17 | - [Sphinx Integration](./sphinx.md) 18 | 19 | --- 20 | 21 | - [Migration Guide](./migration.md) 22 | - [Changelog](./changelog.md) 23 | 24 | --- 25 | 26 | - [Contributing](./contributing.md) 27 | - [Platform Support](./platform_support.md) 28 | -------------------------------------------------------------------------------- /test-crates/cffi-pure/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[repr(C)] 2 | pub struct Point { 3 | pub x: f32, 4 | pub y: f32, 5 | } 6 | 7 | #[repr(u32)] 8 | pub enum Foo { 9 | A = 1, 10 | B, 11 | C, 12 | } 13 | 14 | #[no_mangle] 15 | pub unsafe extern "C" fn get_origin() -> Point { 16 | Point { x: 0.0, y: 0.0 } 17 | } 18 | 19 | #[no_mangle] 20 | pub unsafe extern "C" fn is_in_range(point: Point, range: f32) -> bool { 21 | (point.x.powi(2) + point.y.powi(2)).sqrt() <= range 22 | } 23 | 24 | #[no_mangle] 25 | pub unsafe extern "C" fn print_foo(foo: *const Foo) { 26 | println!( 27 | "{}", 28 | match *foo { 29 | Foo::A => "a", 30 | Foo::B => "b", 31 | Foo::C => "c", 32 | } 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | 2 | disallowed-types = [ 3 | "std::fs::DirEntry", 4 | "std::fs::File", 5 | "std::fs::OpenOptions", 6 | "std::fs::ReadDir", 7 | ] 8 | 9 | disallowed-methods = [ 10 | "std::fs::canonicalize", 11 | "std::fs::copy", 12 | "std::fs::create_dir", 13 | "std::fs::create_dir_all", 14 | "std::fs::hard_link", 15 | "std::fs::metadata", 16 | "std::fs::read", 17 | "std::fs::read_dir", 18 | "std::fs::read_link", 19 | "std::fs::read_to_string", 20 | "std::fs::remove_dir", 21 | "std::fs::remove_dir_all", 22 | "std::fs::remove_file", 23 | "std::fs::rename", 24 | "std::fs::set_permissions", 25 | "std::fs::soft_link", 26 | "std::fs::symlink_metadata", 27 | "std::fs::write", 28 | ] 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | groups: 13 | crates-io: 14 | patterns: 15 | - "*" 16 | 17 | - package-ecosystem: "github-actions" 18 | directory: "/" 19 | schedule: 20 | interval: "monthly" 21 | -------------------------------------------------------------------------------- /tests/cmd/list-python.stdout: -------------------------------------------------------------------------------- 1 | Search and list the available python installations 2 | 3 | Usage: maturin[EXE] list-python [OPTIONS] 4 | 5 | Options: 6 | --target 7 | 8 | 9 | -v, --verbose... 10 | Use verbose output. 11 | 12 | * Default: Show build information and `cargo build` output. * `-v`: Use `cargo build -v`. 13 | * `-vv`: Show debug logging and use `cargo build -vv`. * `-vvv`: Show trace logging. 14 | 15 | You can configure fine-grained logging using the `RUST_LOG` environment variable. 16 | () 17 | 18 | -h, --help 19 | Print help (see a summary with '-h') 20 | -------------------------------------------------------------------------------- /test-crates/workspace-inheritance/python/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "workspace-inheritance" 3 | version.workspace = true 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [lib] 8 | name = "workspace_inheritance" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = "0.27.0" 13 | generic_lib.workspace = true 14 | 15 | [dependencies.libc] 16 | workspace = true 17 | optional = true 18 | features = ["extra_traits"] 19 | 20 | [build-dependencies] 21 | cfg-if.workspace = true 22 | 23 | [dev-dependencies] 24 | cfg-if.workspace = true 25 | 26 | [dependencies.cfg-if] 27 | workspace = true 28 | optional = true 29 | 30 | [dependencies.rand] 31 | workspace = true 32 | features = ["small_rng"] 33 | -------------------------------------------------------------------------------- /tests/cli.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn cli_tests() { 3 | let t = trycmd::TestCases::new(); 4 | t.default_bin_name("maturin"); 5 | t.case("tests/cmd/*.toml"); 6 | 7 | #[cfg(not(feature = "upload"))] 8 | { 9 | t.skip("tests/cmd/upload.toml"); 10 | t.skip("tests/cmd/publish.toml"); 11 | } 12 | 13 | #[cfg(not(feature = "zig"))] 14 | { 15 | t.skip("tests/cmd/build.toml"); 16 | } 17 | 18 | #[cfg(not(feature = "scaffolding"))] 19 | { 20 | t.skip("tests/cmd/new.toml"); 21 | t.skip("tests/cmd/init.toml"); 22 | t.skip("tests/cmd/generate-ci.toml"); 23 | } 24 | 25 | #[cfg(not(all(feature = "upload", feature = "zig", feature = "scaffolding")))] 26 | { 27 | t.skip("tests/cmd/maturin.toml"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[repr(C)] 2 | pub struct Point { 3 | pub x: f32, 4 | pub y: f32, 5 | } 6 | 7 | #[repr(u32)] 8 | pub enum Foo { 9 | A = 1, 10 | B, 11 | C, 12 | } 13 | 14 | #[no_mangle] 15 | pub unsafe extern "C" fn get_origin() -> Point { 16 | Point { x: 0.0, y: 0.0 } 17 | } 18 | 19 | #[no_mangle] 20 | pub unsafe extern "C" fn add_points(p1: Point, p2: Point) -> Point { 21 | Point { 22 | x: p1.x + p2.x, 23 | y: p1.y + p2.y, 24 | } 25 | } 26 | 27 | #[no_mangle] 28 | pub unsafe extern "C" fn is_in_range(point: Point, range: f32) -> bool { 29 | (point.x.powi(2) + point.y.powi(2)).sqrt() <= range 30 | } 31 | 32 | #[no_mangle] 33 | pub unsafe extern "C" fn print_foo(foo: *const Foo) { 34 | println!( 35 | "{}", 36 | match *foo { 37 | Foo::A => "a", 38 | Foo::B => "b", 39 | Foo::C => "c", 40 | } 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/templates/pyproject.toml.j2: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>={{ version_major }}.{{ version_minor }},<{{ version_major + 1 }}.0"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "{{ name }}" 7 | requires-python = ">=3.8" 8 | classifiers = [ 9 | "Programming Language :: Rust", 10 | "Programming Language :: Python :: Implementation :: CPython", 11 | "Programming Language :: Python :: Implementation :: PyPy", 12 | ] 13 | {% if bindings == "cffi" -%} 14 | dependencies = ["cffi"] 15 | {% endif -%} 16 | dynamic = ["version"] 17 | {% if mixed_non_src -%} 18 | [project.optional-dependencies] 19 | tests = [ 20 | "pytest", 21 | ] 22 | {% endif -%} 23 | 24 | {% if bindings in ["bin", "cffi"] or mixed_non_src -%} 25 | [tool.maturin] 26 | {% if bindings == "cffi" or bindings == "bin" -%} 27 | bindings = "{{ bindings }}" 28 | {% endif -%} 29 | {% if mixed_non_src -%} 30 | python-source = "python" 31 | {% endif -%} 32 | {% endif -%} 33 | -------------------------------------------------------------------------------- /test-crates/cffi-mixed/Readme.md: -------------------------------------------------------------------------------- 1 | # cffi-mixed 2 | 3 | A package for testing maturin with a cffi wrapper with a rust backend and a python wrapper. 4 | 5 | Read the [cffi guide](https://cffi.readthedocs.io/en/latest/index.html) to learn how to use the generated `ffi` and `lib` objects. 6 | 7 | The package contains a `Point` type implemented in rust and a `Line` class consisting of two points implemented in python 8 | 9 | ## Usage 10 | 11 | ```bash 12 | pip install . 13 | ``` 14 | 15 | ```python 16 | import cffi_mixed 17 | 18 | from cffi_mixed import Line 19 | 20 | point = cffi_mixed.lib.get_origin() 21 | point.x = 10 22 | point.y = 10 23 | assert cffi_mixed.lib.is_in_range(point, 15) 24 | 25 | assert Line(2, 5, 6, 8).length() == 5 26 | ``` 27 | 28 | ## Testing 29 | 30 | Install tox: 31 | 32 | ```bash 33 | pip install tox 34 | ``` 35 | 36 | Run it: 37 | 38 | ```bash 39 | tox 40 | ``` 41 | 42 | The tests are in `test_cffi_mixed.py`, while the configuration is in tox.ini 43 | -------------------------------------------------------------------------------- /test-crates/with-data/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | import locale 2 | import sys 3 | from pathlib import Path 4 | 5 | import with_data 6 | 7 | assert with_data.lib.one() == 1 8 | assert with_data.ffi.string(with_data.lib.say_hello()).decode() == "hello" 9 | 10 | venv_root = Path(sys.prefix) 11 | 12 | installed_data = ( 13 | venv_root.joinpath("data_subdir") 14 | .joinpath("hello.txt") 15 | # With the default encoding, python under windows fails to read the file correctly 16 | .read_text(encoding="utf-8") 17 | .strip() 18 | ) 19 | assert installed_data == "Hi! 😊", ( 20 | installed_data, 21 | "Hi! 😊", 22 | locale.getpreferredencoding(), 23 | ) 24 | header_file = ( 25 | venv_root.joinpath("include") 26 | .joinpath("site") 27 | .joinpath(f"python{sys.version_info.major}.{sys.version_info.minor}") 28 | .joinpath("with-data") 29 | .joinpath("empty.h") 30 | ) 31 | assert header_file.is_file(), header_file 32 | 33 | print("SUCCESS") 34 | -------------------------------------------------------------------------------- /src/archive_source.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub enum ArchiveSource { 5 | Generated(GeneratedSourceData), 6 | File(FileSourceData), 7 | } 8 | 9 | impl ArchiveSource { 10 | pub(crate) fn executable(&self) -> bool { 11 | match self { 12 | Self::Generated(source) => source.executable, 13 | Self::File(source) => source.executable, 14 | } 15 | } 16 | 17 | pub(crate) fn path(&self) -> Option<&Path> { 18 | match self { 19 | Self::Generated(source) => source.path.as_deref(), 20 | Self::File(source) => Some(&source.path), 21 | } 22 | } 23 | } 24 | 25 | #[derive(Debug, Clone)] 26 | pub struct GeneratedSourceData { 27 | pub(crate) data: Vec, 28 | pub(crate) path: Option, 29 | pub(crate) executable: bool, 30 | } 31 | 32 | #[derive(Debug, Clone)] 33 | pub struct FileSourceData { 34 | pub(crate) path: PathBuf, 35 | pub(crate) executable: bool, 36 | } 37 | -------------------------------------------------------------------------------- /tests/manylinux_incompliant.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Fail because we're running in manylinux2_28, which can't build for manylinux 2010 5 | for PYBIN in /opt/python/cp3[9]*/bin; do 6 | if $1 build -m test-crates/pyo3-mixed/Cargo.toml --target-dir test-crates/targets -i "${PYBIN}/python" --manylinux 2010 -o dist; then 7 | echo "maturin build unexpectedly succeeded" 8 | exit 1 9 | else 10 | echo "maturin build failed as expected" 11 | fi 12 | done 13 | 14 | # Fail because we're linking zlib with black-listed symbols(gzflags), which is not allowed in manylinux 15 | apt-get -v &> /dev/null && apt-get install -y zlib1g-dev || yum install -y zlib-devel 16 | 17 | for PYBIN in /opt/python/cp3[9]*/bin; do 18 | if $1 build -m test-crates/lib_with_disallowed_lib/Cargo.toml --target-dir test-crates/targets -i "${PYBIN}/python" --manylinux 2014 -o dist; then 19 | echo "maturin build unexpectedly succeeded" 20 | exit 1 21 | else 22 | echo "maturin build failed as expected" 23 | fi 24 | done 25 | -------------------------------------------------------------------------------- /src/templates/.gitignore.j2: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | .pytest_cache/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | .venv/ 14 | env/ 15 | bin/ 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | include/ 26 | man/ 27 | venv/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | pip-selfcheck.json 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | 45 | # Translations 46 | *.mo 47 | 48 | # Mr Developer 49 | .mr.developer.cfg 50 | .project 51 | .pydevproject 52 | 53 | # Rope 54 | .ropeproject 55 | 56 | # Django stuff: 57 | *.log 58 | *.pot 59 | 60 | .DS_Store 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyCharm 66 | .idea/ 67 | 68 | # VSCode 69 | .vscode/ 70 | 71 | # Pyenv 72 | .python-version 73 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | env: 2 | RUST_BACKTRACE: 1 3 | CARGO_INCREMENTAL: 0 4 | CARGO_TERM_COLOR: always 5 | CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse 6 | 7 | build_and_test: 8 | &BUILD_AND_TEST # only run tasks on pull request or github merge queue branches 9 | only_if: $CIRRUS_BRANCH =~ 'gh-readonly-queue/.*' || $CIRRUS_PR != "" 10 | setup_script: 11 | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable 12 | - rustup target add wasm32-wasip1 13 | - python3 -m pip install --upgrade cffi virtualenv 14 | build_script: 15 | - cargo build 16 | test_script: 17 | - cargo test 18 | 19 | freebsd_task: 20 | name: Test (x86_64 FreeBSD) 21 | freebsd_instance: 22 | image_family: freebsd-14-3 23 | env: 24 | PATH: $HOME/.cargo/bin:$PATH 25 | target_cache: 26 | folder: target 27 | fingerprint_script: 28 | - echo $CIRRUS_OS 29 | - cat Cargo.lock 30 | install_script: 31 | - pkg install -y bash git python 32 | - python3 -m ensurepip 33 | <<: *BUILD_AND_TEST 34 | -------------------------------------------------------------------------------- /test-dockerfile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Builds all 6 test crates using the docker container, 3 | # installs the wheel and checks that the installed package is functional 4 | 5 | set -e 6 | 7 | rm -rf venv-docker 8 | python3.11 -m venv venv-docker 9 | venv-docker/bin/pip install -U pip cffi 10 | 11 | # FIXME: Can we run the tests without activate? Currently hello-world fails because then the binary is not in PATH 12 | source venv-docker/bin/activate 13 | 14 | for test_crate in hello-world cffi-pure cffi-mixed pyo3-pure pyo3-mixed pyo3-mixed-submodule pyo3-mixed-implicit 15 | do 16 | echo "Testing $test_crate" 17 | docker run -e RUST_BACKTRACE=1 --rm -v "$(pwd):/io" -w /io/test-crates/$test_crate maturin build -i python3.11 18 | # --only-binary=:all: stops pip from picking a local already compiled sdist 19 | venv-docker/bin/pip install $test_crate --only-binary=:all: --find-links test-crates/$test_crate/target/wheels/ 20 | if [[ $(venv-docker/bin/python test-crates/$test_crate/check_installed/check_installed.py) != 'SUCCESS' ]]; then 21 | exit 1 22 | fi 23 | done 24 | 25 | deactivate 26 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use std::env; 3 | 4 | #[pyfunction] 5 | fn get_21() -> usize { 6 | 21 7 | } 8 | 9 | /// Prints the CLI arguments, once from Rust's point of view and once from Python's point of view. 10 | #[pyfunction] 11 | fn print_cli_args(py: Python) -> PyResult<()> { 12 | // This one includes Python and the name of the wrapper script itself, e.g. 13 | // `["/home/ferris/.venv/bin/python", "/home/ferris/.venv/bin/print_cli_args", "a", "b", "c"]` 14 | println!("{:?}", env::args().collect::>()); 15 | // This one includes only the name of the wrapper script itself, e.g. 16 | // `["/home/ferris/.venv/bin/print_cli_args", "a", "b", "c"])` 17 | println!( 18 | "{:?}", 19 | py.import("sys")? 20 | .getattr("argv")? 21 | .extract::>()? 22 | ); 23 | Ok(()) 24 | } 25 | 26 | #[pymodule] 27 | fn pyo3_mixed(m: &Bound<'_, PyModule>) -> PyResult<()> { 28 | m.add_wrapped(wrap_pyfunction!(get_21))?; 29 | m.add_wrapped(wrap_pyfunction!(print_cli_args))?; 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /license-mit: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 konstin 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /tests/cmd/sdist.stdout: -------------------------------------------------------------------------------- 1 | Build only a source distribution (sdist) without compiling. 2 | 3 | Building a source distribution requires a pyproject.toml with a `[build-system]` table. 4 | 5 | This command is a workaround for [pypa/pip#6041](https://github.com/pypa/pip/issues/6041) 6 | 7 | Usage: maturin[EXE] sdist [OPTIONS] 8 | 9 | Options: 10 | -m, --manifest-path 11 | The path to the Cargo.toml 12 | 13 | -v, --verbose... 14 | Use verbose output. 15 | 16 | * Default: Show build information and `cargo build` output. * `-v`: Use `cargo build -v`. 17 | * `-vv`: Show debug logging and use `cargo build -vv`. * `-vvv`: Show trace logging. 18 | 19 | You can configure fine-grained logging using the `RUST_LOG` environment variable. 20 | () 21 | 22 | -o, --out 23 | The directory to store the built wheels in. Defaults to a new "wheels" directory in the 24 | project's target directory 25 | 26 | -h, --help 27 | Print help (see a summary with '-h') 28 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-present konstin 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /tests/cmd/new.stdout: -------------------------------------------------------------------------------- 1 | Create a new cargo project 2 | 3 | Usage: maturin[EXE] new [OPTIONS] 4 | 5 | Arguments: 6 | 7 | Project path 8 | 9 | Options: 10 | --name 11 | Set the resulting package name, defaults to the directory name 12 | 13 | -v, --verbose... 14 | Use verbose output. 15 | 16 | * Default: Show build information and `cargo build` output. * `-v`: Use `cargo build -v`. 17 | * `-vv`: Show debug logging and use `cargo build -vv`. * `-vvv`: Show trace logging. 18 | 19 | You can configure fine-grained logging using the `RUST_LOG` environment variable. 20 | () 21 | 22 | --mixed 23 | Use mixed Rust/Python project layout 24 | 25 | --src 26 | Use Python first src layout for mixed Rust/Python project 27 | 28 | -b, --bindings 29 | Which kind of bindings to use 30 | 31 | [possible values: pyo3, cffi, uniffi, bin] 32 | 33 | -h, --help 34 | Print help (see a summary with '-h') 35 | -------------------------------------------------------------------------------- /test-crates/pyo3-pure/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [tool.maturin] 6 | bindings = "pyo3" 7 | editable-profile = "dev" 8 | 9 | [tool.maturin.target."x86_64-apple-darwin"] 10 | macos-deployment-target = "10.12" 11 | 12 | [tool.maturin.target."aarch64-apple-darwin"] 13 | macos-deployment-target = "11.0" 14 | 15 | [project] 16 | # The name pyo3_pure instead of pyo3-pure is intentional, 17 | # it's used as a crate name resolution regression test 18 | name = "pyo3_pure" 19 | version = "0.1.0+abc123de" 20 | classifiers = [ 21 | "Programming Language :: Rust" 22 | ] 23 | description = "Implements a dummy function in Rust" 24 | readme = "README.md" 25 | maintainers = [ 26 | {name = "messense", email = "messense@icloud.com"} 27 | ] 28 | license = { file = "LICENSE" } 29 | dynamic = ["license"] # Allow license from Cargo.toml to be used 30 | 31 | [project.optional-dependencies] 32 | test = [ 33 | "attrs", 34 | "boltons; sys_platform == 'win32'" 35 | ] 36 | 37 | [project.scripts] 38 | get_42 = "pyo3_pure:DummyClass.get_42" 39 | 40 | [project.gui-scripts] 41 | get_42_gui = "pyo3_pure:DummyClass.get_42" 42 | -------------------------------------------------------------------------------- /test-crates/pyo3-ffi-pure/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present maturin contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /tests/cmd/init.stdout: -------------------------------------------------------------------------------- 1 | Create a new cargo project in an existing directory 2 | 3 | Usage: maturin[EXE] init [OPTIONS] [PATH] 4 | 5 | Arguments: 6 | [PATH] 7 | Project path 8 | 9 | Options: 10 | --name 11 | Set the resulting package name, defaults to the directory name 12 | 13 | -v, --verbose... 14 | Use verbose output. 15 | 16 | * Default: Show build information and `cargo build` output. * `-v`: Use `cargo build -v`. 17 | * `-vv`: Show debug logging and use `cargo build -vv`. * `-vvv`: Show trace logging. 18 | 19 | You can configure fine-grained logging using the `RUST_LOG` environment variable. 20 | () 21 | 22 | --mixed 23 | Use mixed Rust/Python project layout 24 | 25 | --src 26 | Use Python first src layout for mixed Rust/Python project 27 | 28 | -b, --bindings 29 | Which kind of bindings to use 30 | 31 | [possible values: pyo3, cffi, uniffi, bin] 32 | 33 | -h, --help 34 | Print help (see a summary with '-h') 35 | -------------------------------------------------------------------------------- /test-crates/lib_with_path_dep/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | # The problem with testing this is that we need to go through the PEP 517 so we need a wheel of maturin, 6 | # which makes everything complex and slow 7 | cd "$(git rev-parse --show-toplevel)" # Go to project root 8 | 9 | pip uninstall -y lib_with_path_dep 2> /dev/null 10 | # Make sure it's actually removed 11 | python -c "from lib_with_path_dep import add; assert add(2,2)==4" 2> /dev/null && exit 1 || true 12 | 13 | # Build maturin wheel 14 | cargo run -- build -b bin --strip --manylinux off 15 | cargo run -- pep517 write-sdist --manifest-path test-crates/lib_with_path_dep/Cargo.toml --sdist-directory test-crates/lib_with_path_dep/target/wheels 16 | # Slower alternative: cargo run -- build -m test-crates/lib_with_path_dep/Cargo.toml -i python 17 | # See https://github.com/pypa/pip/issues/6041 18 | 19 | # First, use the maturin wheel we just build 20 | # Then install lib_with_path_dep from the sdist we build 21 | pip install \ 22 | --find-links target/wheels/ \ 23 | --force-reinstall --no-binary lib_with_path_dep --find-links test-crates/lib_with_path_dep/target/wheels lib_with_path_dep 24 | python -c "from lib_with_path_dep import add; assert add(2,2)==4" 25 | -------------------------------------------------------------------------------- /.github/workflows/downstream.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | repository: 5 | required: true 6 | type: string 7 | manifest-dir: 8 | required: true 9 | type: string 10 | 11 | jobs: 12 | sdist: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v6 16 | - name: Sccache Setup 17 | uses: mozilla-actions/sccache-action@v0.0.9 18 | with: 19 | version: "v0.10.0" 20 | - name: Build maturin 21 | env: 22 | RUST_BACKTRACE: "1" 23 | SCCACHE_GHA_ENABLED: "true" 24 | RUSTC_WRAPPER: "sccache" 25 | run: cargo build 26 | - uses: actions/checkout@v6 27 | with: 28 | repository: ${{ inputs.repository }} 29 | submodules: "recursive" 30 | path: downstream 31 | - name: maturin sdist 32 | working-directory: downstream 33 | run: | 34 | ../target/debug/maturin sdist --manifest-path ${{ inputs.manifest-dir }}/Cargo.toml -o target/sdist 35 | - name: Build from sdist 36 | working-directory: downstream 37 | run: | 38 | ../target/debug/maturin build --manifest-path ${{ inputs.manifest-dir }}/Cargo.toml 39 | -------------------------------------------------------------------------------- /test-crates/pyo3-ffi-pure/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "libc" 7 | version = "0.2.147" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" 10 | 11 | [[package]] 12 | name = "pyo3-build-config" 13 | version = "0.27.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "f77d387774f6f6eec64a004eac0ed525aab7fa1966d94b42f743797b3e395afb" 16 | dependencies = [ 17 | "target-lexicon", 18 | ] 19 | 20 | [[package]] 21 | name = "pyo3-ffi" 22 | version = "0.27.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "2dd13844a4242793e02df3e2ec093f540d948299a6a77ea9ce7afd8623f542be" 25 | dependencies = [ 26 | "libc", 27 | "pyo3-build-config", 28 | ] 29 | 30 | [[package]] 31 | name = "pyo3-ffi-pure" 32 | version = "1.0.0" 33 | dependencies = [ 34 | "pyo3-build-config", 35 | "pyo3-ffi", 36 | ] 37 | 38 | [[package]] 39 | name = "target-lexicon" 40 | version = "0.13.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" 43 | -------------------------------------------------------------------------------- /test-crates/update_readme.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import re 3 | import subprocess 4 | from pathlib import Path 5 | 6 | 7 | FILES = [ 8 | "README.md", 9 | "guide/src/local_development.md", 10 | "guide/src/tutorial.md", 11 | "guide/src/distribution.md", 12 | ] 13 | 14 | 15 | def main(): 16 | root = Path(subprocess.check_output(["git", "rev-parse", "--show-toplevel"], text=True).strip()) 17 | 18 | for path in FILES: 19 | content = root.joinpath(path).read_text() 20 | 21 | matcher = re.compile(r"```\nUsage: maturin (\w+) (.*?)```", re.MULTILINE | re.DOTALL) 22 | 23 | replaces = {} 24 | for command, old in matcher.findall(content): 25 | command_output = subprocess.check_output(["cargo", "run", "--", command.lower(), "--help"], text=True) 26 | new = "Usage:" + command_output.strip().split("Usage:")[1] + "\n" 27 | # Remove trailing whitespace 28 | new = re.sub(" +\n", "\n", new) 29 | old = "Usage: maturin " + command + " " + old 30 | replaces[old] = new 31 | 32 | for old, new in replaces.items(): 33 | content = content.replace(old, new) 34 | root.joinpath(path).write_text(content) 35 | 36 | 37 | if __name__ == "__main__": 38 | main() 39 | -------------------------------------------------------------------------------- /maturin/bootstrap.py: -------------------------------------------------------------------------------- 1 | """Support installing rust before compiling (bootstrapping) maturin. 2 | 3 | Installing a package that uses maturin as build backend on a platform without maturin 4 | binaries, we install rust in a cache directory if the user doesn't have a rust 5 | installation already. Since this bootstrapping requires more dependencies but is only 6 | required if rust is missing, we check if cargo is present before requesting those 7 | dependencies. 8 | 9 | https://setuptools.pypa.io/en/stable/build_meta.html#dynamic-build-dependencies-and-other-build-meta-tweaks 10 | """ 11 | 12 | from __future__ import annotations 13 | 14 | import os 15 | import shutil 16 | from typing import Any 17 | 18 | # noinspection PyUnresolvedReferences 19 | from setuptools.build_meta import * # noqa:F403 20 | 21 | 22 | def get_requires_for_build_wheel(config_settings: dict[str, Any] | None = None) -> list[str]: 23 | if not os.environ.get("MATURIN_NO_INSTALL_RUST") and not shutil.which("cargo"): 24 | return ["puccinialin>=0.1,<0.2"] 25 | return [] 26 | 27 | 28 | def get_requires_for_build_sdist(config_settings: dict[str, Any] | None = None) -> list[str]: 29 | if not os.environ.get("MATURIN_NO_INSTALL_RUST") and not shutil.which("cargo"): 30 | return ["puccinialin>=0.1,<0.2"] 31 | return [] 32 | -------------------------------------------------------------------------------- /src/module_writer/util.rs: -------------------------------------------------------------------------------- 1 | use std::io::Error as IoError; 2 | use std::io::Write; 3 | 4 | use anyhow::Result; 5 | use base64::Engine as _; 6 | use base64::engine::general_purpose::URL_SAFE_NO_PAD; 7 | use sha2::Digest as _; 8 | use sha2::Sha256; 9 | 10 | pub(super) struct StreamSha256<'a, W> { 11 | hasher: Sha256, 12 | inner: &'a mut W, 13 | bytes_written: usize, 14 | } 15 | 16 | impl<'a, W> StreamSha256<'a, W> 17 | where 18 | W: Write, 19 | { 20 | pub(super) fn new(inner: &'a mut W) -> Self { 21 | Self { 22 | hasher: Sha256::new(), 23 | inner, 24 | bytes_written: 0, 25 | } 26 | } 27 | 28 | pub(super) fn finalize(self) -> Result<(String, usize)> { 29 | self.inner.flush()?; 30 | let hash = URL_SAFE_NO_PAD.encode(self.hasher.finalize()); 31 | Ok((hash, self.bytes_written)) 32 | } 33 | } 34 | 35 | impl<'a, W> Write for StreamSha256<'a, W> 36 | where 37 | W: Write, 38 | { 39 | fn write(&mut self, buf: &[u8]) -> Result { 40 | let written = self.inner.write(buf)?; 41 | self.hasher.update(&buf[..written]); 42 | self.bytes_written += written; 43 | Ok(written) 44 | } 45 | 46 | fn flush(&mut self) -> Result<(), IoError> { 47 | self.inner.flush() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/auditwheel/repair.rs: -------------------------------------------------------------------------------- 1 | use super::audit::AuditWheelError; 2 | use crate::auditwheel::Policy; 3 | use anyhow::Result; 4 | use lddtree::DependencyAnalyzer; 5 | use std::path::{Path, PathBuf}; 6 | 7 | /// Find external shared library dependencies 8 | #[allow(clippy::result_large_err)] 9 | pub fn find_external_libs( 10 | artifact: impl AsRef, 11 | policy: &Policy, 12 | sysroot: PathBuf, 13 | ld_paths: Vec, 14 | ) -> Result, AuditWheelError> { 15 | let dep_analyzer = DependencyAnalyzer::new(sysroot).library_paths(ld_paths); 16 | let deps = dep_analyzer 17 | .analyze(artifact) 18 | .map_err(AuditWheelError::DependencyAnalysisError)?; 19 | let mut ext_libs = Vec::new(); 20 | for (_, lib) in deps.libraries { 21 | let name = &lib.name; 22 | // Skip dynamic linker/loader and white-listed libs 23 | if name.starts_with("ld-linux") 24 | || name == "ld64.so.2" 25 | || name == "ld64.so.1" 26 | // musl libc, eg: libc.musl-aarch64.so.1 27 | || name.starts_with("ld-musl") 28 | || name.starts_with("libc.") 29 | || policy.lib_whitelist.contains(name) 30 | { 31 | continue; 32 | } 33 | ext_libs.push(lib); 34 | } 35 | Ok(ext_libs) 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/update-auditwheel.yml: -------------------------------------------------------------------------------- 1 | name: Update auditwheel policies 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | # Run every week 7 | - cron: '0 0 * * 0' 8 | 9 | permissions: # added using https://github.com/step-security/secure-workflows 10 | contents: read 11 | 12 | jobs: 13 | update: 14 | permissions: 15 | contents: write # for peter-evans/create-pull-request to create branch 16 | pull-requests: write # for peter-evans/create-pull-request to create a PR 17 | name: Update auditwheel policies 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v6 21 | - name: Fetch latest policy files 22 | run: | 23 | curl https://raw.githubusercontent.com/pypa/auditwheel/main/src/auditwheel/policy/manylinux-policy.json > src/auditwheel/manylinux-policy.json 24 | curl https://raw.githubusercontent.com/pypa/auditwheel/main/src/auditwheel/policy/musllinux-policy.json > src/auditwheel/musllinux-policy.json 25 | - name: Create Pull Request 26 | uses: peter-evans/create-pull-request@v7 27 | with: 28 | delete-branch: true 29 | add-paths: | 30 | src/auditwheel/*.json 31 | title: 'Update manylinux/musllinux policies to the latest main' 32 | commit-message: 'Update manylinux/musllinux policies to the latest main' 33 | -------------------------------------------------------------------------------- /maturin/__main__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | import sys 5 | from pathlib import Path 6 | import sysconfig 7 | from typing import Optional 8 | 9 | 10 | def get_maturin_path() -> Optional[Path]: 11 | SCRIPT_NAME = "maturin" 12 | 13 | def script_dir(scheme: str) -> str: 14 | return sysconfig.get_path("scripts", scheme) 15 | 16 | def script_exists(dir: str) -> bool: 17 | for _, _, files in os.walk(dir): 18 | for f in files: 19 | name, *_ = os.path.splitext(f) 20 | if name == SCRIPT_NAME: 21 | return True 22 | 23 | return False 24 | 25 | paths = list( 26 | filter( 27 | script_exists, 28 | filter(os.path.exists, map(script_dir, sysconfig.get_scheme_names())), 29 | ) 30 | ) 31 | 32 | if paths: 33 | return Path(paths[0]) / SCRIPT_NAME 34 | 35 | return None 36 | 37 | 38 | if __name__ == "__main__": 39 | maturin = get_maturin_path() 40 | if maturin is None: 41 | print("Unable to find `maturin` script") 42 | exit(1) 43 | 44 | if sys.platform == "win32": 45 | import subprocess 46 | 47 | code = subprocess.call([str(maturin)] + sys.argv[1:]) 48 | sys.exit(code) 49 | else: 50 | os.execv(maturin, [str(maturin)] + sys.argv[1:]) 51 | -------------------------------------------------------------------------------- /tests/cmd/maturin.stdout: -------------------------------------------------------------------------------- 1 | Build and publish crates with pyo3, cffi and uniffi bindings as well as rust binaries as python 2 | packages 3 | 4 | Usage: maturin[EXE] [OPTIONS] 5 | 6 | Commands: 7 | build Build the crate into python packages 8 | publish Build and publish the crate as python packages to pypi 9 | list-python Search and list the available python installations 10 | develop Install the crate as module in the current virtualenv 11 | sdist Build only a source distribution (sdist) without compiling 12 | init Create a new cargo project in an existing directory 13 | new Create a new cargo project 14 | generate-ci Generate CI configuration 15 | upload Upload python packages to pypi 16 | help Print this message or the help of the given subcommand(s) 17 | 18 | Options: 19 | -v, --verbose... 20 | Use verbose output. 21 | 22 | * Default: Show build information and `cargo build` output. * `-v`: Use `cargo build -v`. 23 | * `-vv`: Show debug logging and use `cargo build -vv`. * `-vvv`: Show trace logging. 24 | 25 | You can configure fine-grained logging using the `RUST_LOG` environment variable. 26 | () 27 | 28 | -h, --help 29 | Print help (see a summary with '-h') 30 | 31 | -V, --version 32 | Print version 33 | 34 | Visit https://maturin.rs to learn more about maturin. 35 | -------------------------------------------------------------------------------- /tests/cmd/generate-ci.stdout: -------------------------------------------------------------------------------- 1 | Generate CI configuration 2 | 3 | Usage: maturin[EXE] generate-ci [OPTIONS] 4 | 5 | Arguments: 6 | 7 | CI provider 8 | 9 | Possible values: 10 | - github: GitHub 11 | 12 | Options: 13 | -m, --manifest-path 14 | Path to Cargo.toml 15 | 16 | -v, --verbose... 17 | Use verbose output. 18 | 19 | * Default: Show build information and `cargo build` output. * `-v`: Use `cargo build -v`. 20 | * `-vv`: Show debug logging and use `cargo build -vv`. * `-vvv`: Show trace logging. 21 | 22 | You can configure fine-grained logging using the `RUST_LOG` environment variable. 23 | () 24 | 25 | -o, --output 26 | Output path 27 | 28 | [default: -] 29 | 30 | --platform ... 31 | Platform support 32 | 33 | Possible values: 34 | - all: All 35 | - manylinux: Manylinux 36 | - musllinux: Musllinux 37 | - windows: Windows 38 | - macos: macOS 39 | - emscripten: Emscripten 40 | 41 | [default: linux musllinux windows macos] 42 | 43 | --pytest 44 | Enable pytest 45 | 46 | --zig 47 | Use zig to do cross compilation 48 | 49 | --skip-attestation 50 | Skip artifact attestation 51 | 52 | -h, --help 53 | Print help (see a summary with '-h') 54 | -------------------------------------------------------------------------------- /src/auditwheel/musllinux.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use fs_err as fs; 3 | use goblin::elf::Elf; 4 | use regex::Regex; 5 | use std::path::{Path, PathBuf}; 6 | use std::process::{Command, Stdio}; 7 | 8 | /// Find musl libc path from executable's ELF header 9 | pub fn find_musl_libc() -> Result> { 10 | let buffer = fs::read("/bin/ls")?; 11 | let elf = Elf::parse(&buffer)?; 12 | Ok(elf.interpreter.map(PathBuf::from)) 13 | } 14 | 15 | /// Read the musl version from libc library's output 16 | /// 17 | /// The libc library should output something like this to stderr:: 18 | /// 19 | /// musl libc (x86_64) 20 | /// Version 1.2.2 21 | /// Dynamic Program Loader 22 | pub fn get_musl_version(ld_path: impl AsRef) -> Result> { 23 | let ld_path = ld_path.as_ref(); 24 | let output = Command::new(ld_path) 25 | .stdout(Stdio::null()) 26 | .stderr(Stdio::piped()) 27 | .output()?; 28 | let stderr = std::str::from_utf8(&output.stderr)?; 29 | let expr = Regex::new(r"Version (\d+)\.(\d+)")?; 30 | if let Some(capture) = expr.captures(stderr) { 31 | let context = "Expected a digit"; 32 | let major = capture 33 | .get(1) 34 | .unwrap() 35 | .as_str() 36 | .parse::() 37 | .context(context)?; 38 | let minor = capture 39 | .get(2) 40 | .unwrap() 41 | .as_str() 42 | .parse::() 43 | .context(context)?; 44 | return Ok(Some((major, minor))); 45 | } 46 | Ok(None) 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/copilot-setup-steps.yml: -------------------------------------------------------------------------------- 1 | name: "Copilot Setup Steps" 2 | 3 | # Automatically run the setup steps when they are changed to allow for easy validation, and 4 | # allow manual testing through the repository's "Actions" tab 5 | on: 6 | workflow_dispatch: 7 | push: 8 | paths: 9 | - .github/workflows/copilot-setup-steps.yml 10 | pull_request: 11 | paths: 12 | - .github/workflows/copilot-setup-steps.yml 13 | 14 | jobs: 15 | # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. 16 | copilot-setup-steps: 17 | runs-on: ubuntu-latest 18 | 19 | # Set the permissions to the lowest permissions possible needed for your steps. 20 | # Copilot will be given its own token for its operations. 21 | permissions: 22 | # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. 23 | contents: read 24 | 25 | # You can define any steps you want, and they will run before the agent starts. 26 | # If you do not check out your code, Copilot will do this for you. 27 | steps: 28 | - name: Checkout code 29 | uses: actions/checkout@v6 30 | - uses: astral-sh/setup-uv@v7 31 | - run: uv tool install pre-commit 32 | - uses: actions/cache@v4 33 | with: 34 | path: ~/.cache/pre-commit 35 | key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} 36 | - run: pre-commit install-hooks 37 | -------------------------------------------------------------------------------- /src/python_interpreter/get_interpreter_metadata.py: -------------------------------------------------------------------------------- 1 | import json 2 | import platform 3 | import struct 4 | import sys 5 | import sysconfig 6 | 7 | if platform.python_implementation() == "PyPy": 8 | # Workaround for PyPy 3.6 on windows: 9 | # - sysconfig.get_config_var("EXT_SUFFIX") differs to importlib until 10 | # Python 3.8 11 | # - PyPy does not load the plain ".pyd" suffix because it expects that's 12 | # for a CPython extension module 13 | # 14 | # This workaround can probably be removed once PyPy for Python 3.8 is the 15 | # main PyPy version. 16 | import importlib.machinery 17 | 18 | ext_suffix = importlib.machinery.EXTENSION_SUFFIXES[0] 19 | else: 20 | ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") 21 | 22 | metadata = { 23 | # sys.implementation.name can differ from platform.python_implementation(), for example 24 | # Pyston has sys.implementation.name == "pyston" while platform.python_implementation() == cpython 25 | "implementation_name": sys.implementation.name, 26 | "executable": sys.executable or None, 27 | "major": sys.version_info.major, 28 | "minor": sys.version_info.minor, 29 | "abiflags": sysconfig.get_config_var("ABIFLAGS"), 30 | "interpreter": platform.python_implementation().lower(), 31 | "ext_suffix": ext_suffix, 32 | "soabi": sysconfig.get_config_var("SOABI") or None, 33 | "platform": sysconfig.get_platform(), 34 | # This one isn't technically necessary, but still very useful for sanity checks 35 | "system": platform.system().lower(), 36 | # This one is for generating a config file for pyo3 37 | "pointer_width": struct.calcsize("P") * 8, 38 | "gil_disabled": sysconfig.get_config_var("Py_GIL_DISABLED") == 1, 39 | } 40 | 41 | print(json.dumps(metadata)) 42 | -------------------------------------------------------------------------------- /sysconfig/cpython-win-3.9-aarch64.txt: -------------------------------------------------------------------------------- 1 | Platform: "win-arm64" 2 | Python version: "3.9" 3 | Current installation scheme: "nt" 4 | 5 | Paths: 6 | data = "C:\Program Files (Arm)\Python-3.9.1" 7 | include = "C:\Program Files (Arm)\Python-3.9.1\Include" 8 | platinclude = "C:\Program Files (Arm)\Python-3.9.1\Include" 9 | platlib = "C:\Program Files (Arm)\Python-3.9.1\Lib\site-packages" 10 | platstdlib = "C:\Program Files (Arm)\Python-3.9.1\Lib" 11 | purelib = "C:\Program Files (Arm)\Python-3.9.1\Lib\site-packages" 12 | scripts = "C:\Program Files (Arm)\Python-3.9.1\Scripts" 13 | stdlib = "C:\Program Files (Arm)\Python-3.9.1\Lib" 14 | 15 | Variables: 16 | BINDIR = "C:\Program Files (Arm)\Python-3.9.1" 17 | BINLIBDEST = "C:\Program Files (Arm)\Python-3.9.1\Lib" 18 | EXE = ".exe" 19 | EXT_SUFFIX = ".pyd" 20 | INCLUDEPY = "C:\Program Files (Arm)\Python-3.9.1\Include" 21 | LIBDEST = "C:\Program Files (Arm)\Python-3.9.1\Lib" 22 | SO = ".pyd" 23 | TZPATH = "" 24 | VERSION = "39" 25 | abiflags = "" 26 | base = "C:\Program Files (Arm)\Python-3.9.1" 27 | exec_prefix = "C:\Program Files (Arm)\Python-3.9.1" 28 | installed_base = "C:\Program Files (Arm)\Python-3.9.1" 29 | installed_platbase = "C:\Program Files (Arm)\Python-3.9.1" 30 | platbase = "C:\Program Files (Arm)\Python-3.9.1" 31 | platlibdir = "lib" 32 | prefix = "C:\Program Files (Arm)\Python-3.9.1" 33 | projectbase = "C:\Program Files (Arm)\Python-3.9.1" 34 | py_version = "3.9.1" 35 | py_version_nodot = "39" 36 | py_version_short = "3.9" 37 | srcdir = "C:\Program Files (Arm)\Python-3.9.1" 38 | userbase = "C:\Users\messense\AppData\Roaming\Python" 39 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | # Workaround to bootstrap maturin on non-manylinux platforms 2 | [build-system] 3 | requires = [ 4 | "setuptools>=77.0.0", 5 | "tomli>=1.1.0 ; python_version<'3.11'", 6 | "setuptools-rust>=1.11.0", 7 | ] 8 | backend-path = ["maturin"] 9 | build-backend = "bootstrap" 10 | 11 | [project] 12 | name = "maturin" 13 | description = "Build and publish crates with pyo3, cffi and uniffi bindings as well as rust binaries as python packages" 14 | authors = [{ name = "konstin", email = "konstin@mailbox.org" }] 15 | readme = { file = "README.md", content-type = "text/markdown" } 16 | requires-python = ">=3.7" 17 | license = "MIT OR Apache-2.0" 18 | license-files = [ 19 | "license-mit", 20 | "license-apache", 21 | ] 22 | classifiers = [ 23 | "Topic :: Software Development :: Build Tools", 24 | "Programming Language :: Rust", 25 | "Programming Language :: Python :: Implementation :: CPython", 26 | "Programming Language :: Python :: Implementation :: PyPy", 27 | "Programming Language :: Python :: Implementation :: GraalPy", 28 | ] 29 | dependencies = ["tomli>=1.1.0 ; python_version<'3.11'"] 30 | dynamic = ["version"] 31 | 32 | [project.optional-dependencies] 33 | zig = ["ziglang>=0.10.0,<0.13.0"] 34 | patchelf = ["patchelf"] 35 | 36 | [project.urls] 37 | "Source Code" = "https://github.com/PyO3/maturin" 38 | Issues = "https://github.com/PyO3/maturin/issues" 39 | Documentation = "https://maturin.rs" 40 | Changelog = "https://maturin.rs/changelog.html" 41 | 42 | [tool.setuptools] 43 | packages = ["maturin"] 44 | 45 | [tool.maturin] 46 | bindings = "bin" 47 | 48 | [tool.black] 49 | target-version = ['py37'] 50 | extend-exclude = ''' 51 | # Ignore cargo-generate templates 52 | ^/src/templates 53 | ''' 54 | 55 | [tool.ruff] 56 | line-length = 120 57 | target-version = "py37" 58 | 59 | [tool.mypy] 60 | disallow_untyped_defs = true 61 | disallow_incomplete_defs = true 62 | warn_no_return = true 63 | ignore_missing_imports = true 64 | -------------------------------------------------------------------------------- /sysconfig/cpython-win-3.7.txt: -------------------------------------------------------------------------------- 1 | Platform: "win-amd64" 2 | Python version: "3.7" 3 | Current installation scheme: "nt" 4 | 5 | Paths: 6 | data = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 7 | include = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Include" 8 | platinclude = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Include" 9 | platlib = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Lib\site-packages" 10 | platstdlib = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Lib" 11 | purelib = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Lib\site-packages" 12 | scripts = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Scripts" 13 | stdlib = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Lib" 14 | 15 | Variables: 16 | BINDIR = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 17 | BINLIBDEST = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Lib" 18 | EXE = ".exe" 19 | EXT_SUFFIX = ".pyd" 20 | INCLUDEPY = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Include" 21 | LIBDEST = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37\Lib" 22 | SO = ".pyd" 23 | VERSION = "37" 24 | abiflags = "" 25 | base = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 26 | exec_prefix = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 27 | installed_base = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 28 | installed_platbase = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 29 | platbase = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 30 | prefix = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 31 | projectbase = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 32 | py_version = "3.7.0" 33 | py_version_nodot = "37" 34 | py_version_short = "3.7" 35 | srcdir = "C:\Users\Konstantin\AppData\Local\Programs\Python\Python37" 36 | userbase = "C:\Users\Konstantin\AppData\Roaming\Python" 37 | -------------------------------------------------------------------------------- /sysconfig/pypy-linux-s390x-3.9-7.3.txt: -------------------------------------------------------------------------------- 1 | Platform: "linux-s390" 2 | Python version: "3.9" 3 | Current installation scheme: "posix_prefix" 4 | 5 | Paths: 6 | data = "/usr" 7 | include = "/usr/include/pypy3.9" 8 | platinclude = "/usr/include/pypy3.9" 9 | platlib = "/usr/lib64/pypy3.9/site-packages" 10 | platstdlib = "/usr/lib64/pypy3.9" 11 | purelib = "/usr/lib/pypy3.9/site-packages" 12 | scripts = "/usr/bin" 13 | stdlib = "/usr/lib64/pypy3.9" 14 | 15 | Variables: 16 | ABIFLAGS = "" 17 | AR = "ar" 18 | ARFLAGS = "rc" 19 | CC = "cc -pthread" 20 | CCSHARED = "-fPIC" 21 | CFLAGS = "-DNDEBUG -O2" 22 | CXX = "c++ -pthread" 23 | EXE = "" 24 | EXT_SUFFIX = ".pypy39-pp73-s390x-linux-gnu.so" 25 | INCLUDEPY = "/usr/include/pypy3.9" 26 | LDFLAGS = "-Wl,-Bsymbolic-functions" 27 | LDLIBRARY = "libpypy3.9-c.so" 28 | LDSHARED = "cc -pthread -shared -Wl,-Bsymbolic-functions" 29 | LDVERSION = "3.9" 30 | LIBDIR = "/usr/bin" 31 | LIBRARY = "" 32 | MULTIARCH = "s390x-linux-gnu" 33 | OPT = "-DNDEBUG -O2" 34 | Py_DEBUG = "0" 35 | Py_ENABLE_SHARED = "0" 36 | SHLIB_SUFFIX = ".so" 37 | SIZEOF_VOID_P = "8" 38 | SO = ".pypy39-pp73-s390x-linux-gnu.so" 39 | SOABI = "pypy39-pp73" 40 | TZPATH = "/usr/share/zoneinfo:/usr/lib/zoneinfo:/usr/share/lib/zoneinfo:/usr/../etc/zoneinfo" 41 | VERSION = "3.9" 42 | abiflags = "" 43 | base = "/usr" 44 | exec_prefix = "/usr" 45 | implementation = "PyPy" 46 | implementation_lower = "pypy" 47 | installed_base = "/usr" 48 | installed_platbase = "/usr" 49 | platbase = "/usr" 50 | platlibdir = "lib64" 51 | prefix = "/usr" 52 | projectbase = "/usr/bin" 53 | py_version = "3.9.12" 54 | py_version_nodot = "39" 55 | py_version_short = "3.9" 56 | srcdir = "/usr/bin" 57 | userbase = "/root/.local" 58 | User: 59 | data = "/root/.local" 60 | include = "/root/.local/include/pypy3.9" 61 | platlib = "/root/.local/lib64/pypy3.9/site-packages" 62 | platstdlib = "/root/.local/lib64/pypy3.9" 63 | purelib = "/root/.local/lib/pypy3.9/site-packages" 64 | scripts = "/root/.local/bin" 65 | stdlib = "/root/.local/lib64/pypy3.9" 66 | -------------------------------------------------------------------------------- /sysconfig/pypy-linux-ppc64le-3.9-7.3.txt: -------------------------------------------------------------------------------- 1 | Platform: "linux-ppc64le" 2 | Python version: "3.9" 3 | Current installation scheme: "posix_prefix" 4 | 5 | Paths: 6 | data = "/usr" 7 | include = "/usr/include/pypy3.9" 8 | platinclude = "/usr/include/pypy3.9" 9 | platlib = "/usr/lib64/pypy3.9/site-packages" 10 | platstdlib = "/usr/lib64/pypy3.9" 11 | purelib = "/usr/lib/pypy3.9/site-packages" 12 | scripts = "/usr/bin" 13 | stdlib = "/usr/lib64/pypy3.9" 14 | 15 | Variables: 16 | ABIFLAGS = "" 17 | AR = "ar" 18 | ARFLAGS = "rc" 19 | CC = "cc -pthread" 20 | CCSHARED = "-fPIC" 21 | CFLAGS = "-DNDEBUG -O2" 22 | CXX = "c++ -pthread" 23 | EXE = "" 24 | EXT_SUFFIX = ".pypy39-pp73-ppc_64-linux-gnu.so" 25 | INCLUDEPY = "/usr/include/pypy3.9" 26 | LDFLAGS = "-Wl,-Bsymbolic-functions" 27 | LDLIBRARY = "libpypy3.9-c.so" 28 | LDSHARED = "cc -pthread -shared -Wl,-Bsymbolic-functions" 29 | LDVERSION = "3.9" 30 | LIBDIR = "/usr/bin" 31 | LIBRARY = "" 32 | MULTIARCH = "ppc_64-linux-gnu" 33 | OPT = "-DNDEBUG -O2" 34 | Py_DEBUG = "0" 35 | Py_ENABLE_SHARED = "0" 36 | SHLIB_SUFFIX = ".so" 37 | SIZEOF_VOID_P = "8" 38 | SO = ".pypy39-pp73-ppc_64-linux-gnu.so" 39 | SOABI = "pypy39-pp73" 40 | TZPATH = "/usr/share/zoneinfo:/usr/lib/zoneinfo:/usr/share/lib/zoneinfo:/usr/../etc/zoneinfo" 41 | VERSION = "3.9" 42 | abiflags = "" 43 | base = "/usr" 44 | exec_prefix = "/usr" 45 | implementation = "PyPy" 46 | implementation_lower = "pypy" 47 | installed_base = "/usr" 48 | installed_platbase = "/usr" 49 | platbase = "/usr" 50 | platlibdir = "lib64" 51 | prefix = "/usr" 52 | projectbase = "/usr/bin" 53 | py_version = "3.9.12" 54 | py_version_nodot = "39" 55 | py_version_short = "3.9" 56 | srcdir = "/usr/bin" 57 | userbase = "/root/.local" 58 | User: 59 | data = "/root/.local" 60 | include = "/root/.local/include/pypy3.9" 61 | platlib = "/root/.local/lib64/pypy3.9/site-packages" 62 | platstdlib = "/root/.local/lib64/pypy3.9" 63 | purelib = "/root/.local/lib/pypy3.9/site-packages" 64 | scripts = "/root/.local/bin" 65 | stdlib = "/root/.local/lib64/pypy3.9" 66 | -------------------------------------------------------------------------------- /sysconfig/pypy-linux-s390x-3.7-7.3.txt: -------------------------------------------------------------------------------- 1 | Platform: "linux-s390x" 2 | Python version: "3.7" 3 | Current installation scheme: "pypy" 4 | 5 | Paths: 6 | data = "/usr/lib64/pypy3.7" 7 | include = "/usr/lib64/pypy3.7/include" 8 | platinclude = "/usr/lib64/pypy3.7/include" 9 | platlib = "/usr/lib64/pypy3.7/site-packages" 10 | platstdlib = "/usr/lib64/pypy3.7/lib_pypy" 11 | purelib = "/usr/lib64/pypy3.7/site-packages" 12 | scripts = "/usr/lib64/pypy3.7/bin" 13 | stdlib = "/usr/lib64/pypy3.7/lib_pypy" 14 | 15 | Variables: 16 | ABIFLAGS = "" 17 | AR = "ar" 18 | ARFLAGS = "rc" 19 | CC = "cc -pthread" 20 | CCSHARED = "-fPIC" 21 | CFLAGS = "-DNDEBUG -O2" 22 | CXX = "c++ -pthread" 23 | EXE = "" 24 | EXT_SUFFIX = ".pypy37-pp73-s390x-linux-gnu.so" 25 | INCLUDEPY = "/usr/lib64/pypy3.7/include" 26 | LDLIBRARY = "libpypy3-c.so" 27 | LDSHARED = "cc -pthread -shared" 28 | LDVERSION = "3.7" 29 | LIBDIR = "/usr/lib64/pypy3.7/bin" 30 | LIBRARY = "" 31 | MULTIARCH = "s390x-linux-gnu" 32 | OPT = "-DNDEBUG -O2" 33 | Py_DEBUG = "0" 34 | Py_ENABLE_SHARED = "0" 35 | SHLIB_SUFFIX = ".so" 36 | SIZEOF_VOID_P = "8" 37 | SO = ".pypy37-pp73-s390x-linux-gnu.so" 38 | SOABI = "pypy37-pp73" 39 | VERSION = "3.7" 40 | abiflags = "" 41 | base = "/usr/lib64/pypy3.7" 42 | exec_prefix = "/usr/lib64/pypy3.7" 43 | implementation = "PyPy" 44 | implementation_lower = "pypy" 45 | installed_base = "/usr/lib64/pypy3.7" 46 | installed_platbase = "/usr/lib64/pypy3.7" 47 | platbase = "/usr/lib64/pypy3.7" 48 | prefix = "/usr/lib64/pypy3.7" 49 | projectbase = "/usr/lib64/pypy3.7/bin" 50 | py_version = "3.7.13" 51 | py_version_nodot = "37" 52 | py_version_short = "3.7" 53 | srcdir = "/usr/lib64/pypy3.7/lib_pypy/config-3.7-s390x-linux-gnu" 54 | userbase = "/root/.local" 55 | User: 56 | data = "/root/.local" 57 | include = "/root/.local/include/pypy3.7" 58 | platlib = "/root/.local/lib/pypy3.7/site-packages" 59 | platstdlib = "/root/.local/lib/pypy3.7" 60 | purelib = "/root/.local/lib/pypy3.7/site-packages" 61 | scripts = "/root/.local/bin" 62 | stdlib = "/root/.local/lib/pypy3.7" 63 | -------------------------------------------------------------------------------- /sysconfig/pypy-linux-ppc64le-3.7-7.3.txt: -------------------------------------------------------------------------------- 1 | Platform: "linux-ppc64le" 2 | Python version: "3.7" 3 | Current installation scheme: "pypy" 4 | 5 | Paths: 6 | data = "/usr/lib64/pypy3.7" 7 | include = "/usr/lib64/pypy3.7/include" 8 | platinclude = "/usr/lib64/pypy3.7/include" 9 | platlib = "/usr/lib64/pypy3.7/site-packages" 10 | platstdlib = "/usr/lib64/pypy3.7/lib_pypy" 11 | purelib = "/usr/lib64/pypy3.7/site-packages" 12 | scripts = "/usr/lib64/pypy3.7/bin" 13 | stdlib = "/usr/lib64/pypy3.7/lib_pypy" 14 | 15 | Variables: 16 | ABIFLAGS = "" 17 | AR = "ar" 18 | ARFLAGS = "rc" 19 | CC = "cc -pthread" 20 | CCSHARED = "-fPIC" 21 | CFLAGS = "-DNDEBUG -O2" 22 | CXX = "c++ -pthread" 23 | EXE = "" 24 | EXT_SUFFIX = ".pypy37-pp73-ppc_64-linux-gnu.so" 25 | INCLUDEPY = "/usr/lib64/pypy3.7/include" 26 | LDLIBRARY = "libpypy3-c.so" 27 | LDSHARED = "cc -pthread -shared" 28 | LDVERSION = "3.7" 29 | LIBDIR = "/usr/lib64/pypy3.7/bin" 30 | LIBRARY = "" 31 | MULTIARCH = "ppc_64-linux-gnu" 32 | OPT = "-DNDEBUG -O2" 33 | Py_DEBUG = "0" 34 | Py_ENABLE_SHARED = "0" 35 | SHLIB_SUFFIX = ".so" 36 | SIZEOF_VOID_P = "8" 37 | SO = ".pypy37-pp73-ppc_64-linux-gnu.so" 38 | SOABI = "pypy37-pp73" 39 | VERSION = "3.7" 40 | abiflags = "" 41 | base = "/usr/lib64/pypy3.7" 42 | exec_prefix = "/usr/lib64/pypy3.7" 43 | implementation = "PyPy" 44 | implementation_lower = "pypy" 45 | installed_base = "/usr/lib64/pypy3.7" 46 | installed_platbase = "/usr/lib64/pypy3.7" 47 | platbase = "/usr/lib64/pypy3.7" 48 | prefix = "/usr/lib64/pypy3.7" 49 | projectbase = "/usr/lib64/pypy3.7/bin" 50 | py_version = "3.7.13" 51 | py_version_nodot = "37" 52 | py_version_short = "3.7" 53 | srcdir = "/usr/lib64/pypy3.7/lib_pypy/config-3.7-ppc_64-linux-gnu" 54 | userbase = "/root/.local" 55 | User: 56 | data = "/root/.local" 57 | include = "/root/.local/include/pypy3.7" 58 | platlib = "/root/.local/lib/pypy3.7/site-packages" 59 | platstdlib = "/root/.local/lib/pypy3.7" 60 | purelib = "/root/.local/lib/pypy3.7/site-packages" 61 | scripts = "/root/.local/bin" 62 | stdlib = "/root/.local/lib/pypy3.7" 63 | -------------------------------------------------------------------------------- /sysconfig/pypy-linux-s390x-3.8-7.3.txt: -------------------------------------------------------------------------------- 1 | Platform: "linux-s390x" 2 | Python version: "3.8" 3 | Current installation scheme: "posix_prefix" 4 | 5 | Paths: 6 | data = "/usr/lib64/pypy3.8" 7 | include = "/usr/lib64/pypy3.8/include/pypy3.8" 8 | platinclude = "/usr/lib64/pypy3.8/include/pypy3.8" 9 | platlib = "/usr/lib64/pypy3.8/lib/pypy3.8/site-packages" 10 | platstdlib = "/usr/lib64/pypy3.8/lib/pypy3.8" 11 | purelib = "/usr/lib64/pypy3.8/lib/pypy3.8/site-packages" 12 | scripts = "/usr/lib64/pypy3.8/bin" 13 | stdlib = "/usr/lib64/pypy3.8/lib/pypy3.8" 14 | 15 | Variables: 16 | ABIFLAGS = "" 17 | AR = "ar" 18 | ARFLAGS = "rc" 19 | CC = "cc -pthread" 20 | CCSHARED = "-fPIC" 21 | CFLAGS = "-DNDEBUG -O2" 22 | CXX = "c++ -pthread" 23 | EXE = "" 24 | EXT_SUFFIX = ".pypy38-pp73-s390x-linux-gnu.so" 25 | INCLUDEPY = "/usr/lib64/pypy3.8/include/pypy3.8" 26 | LDFLAGS = "-Wl,-Bsymbolic-functions" 27 | LDLIBRARY = "libpypy3-c.so" 28 | LDSHARED = "cc -pthread -shared -Wl,-Bsymbolic-functions" 29 | LDVERSION = "3.8" 30 | LIBDIR = "/usr/lib64/pypy3.8/bin" 31 | LIBRARY = "" 32 | MULTIARCH = "s390x-linux-gnu" 33 | OPT = "-DNDEBUG -O2" 34 | Py_DEBUG = "0" 35 | Py_ENABLE_SHARED = "0" 36 | SHLIB_SUFFIX = ".so" 37 | SIZEOF_VOID_P = "8" 38 | SO = ".pypy38-pp73-s390x-linux-gnu.so" 39 | SOABI = "pypy38-pp73" 40 | VERSION = "3.8" 41 | abiflags = "" 42 | base = "/usr/lib64/pypy3.8" 43 | exec_prefix = "/usr/lib64/pypy3.8" 44 | implementation = "PyPy" 45 | implementation_lower = "pypy" 46 | installed_base = "/usr/lib64/pypy3.8" 47 | installed_platbase = "/usr/lib64/pypy3.8" 48 | platbase = "/usr/lib64/pypy3.8" 49 | prefix = "/usr/lib64/pypy3.8" 50 | projectbase = "/usr/lib64/pypy3.8/bin" 51 | py_version = "3.8.13" 52 | py_version_nodot = "38" 53 | py_version_short = "3.8" 54 | srcdir = "/usr/lib64/pypy3.8/bin" 55 | userbase = "/root/.local" 56 | User: 57 | data = "/root/.local" 58 | include = "/root/.local/include/pypy3.8" 59 | platlib = "/root/.local/lib/pypy3.8/site-packages" 60 | platstdlib = "/root/.local/lib/pypy3.8" 61 | purelib = "/root/.local/lib/pypy3.8/site-packages" 62 | scripts = "/root/.local/bin" 63 | stdlib = "/root/.local/lib/pypy3.8" 64 | -------------------------------------------------------------------------------- /sysconfig/pypy-linux-ppc64le-3.8-7.3.txt: -------------------------------------------------------------------------------- 1 | Platform: "linux-ppc64le" 2 | Python version: "3.8" 3 | Current installation scheme: "posix_prefix" 4 | 5 | Paths: 6 | data = "/usr/lib64/pypy3.8" 7 | include = "/usr/lib64/pypy3.8/include/pypy3.8" 8 | platinclude = "/usr/lib64/pypy3.8/include/pypy3.8" 9 | platlib = "/usr/lib64/pypy3.8/lib/pypy3.8/site-packages" 10 | platstdlib = "/usr/lib64/pypy3.8/lib/pypy3.8" 11 | purelib = "/usr/lib64/pypy3.8/lib/pypy3.8/site-packages" 12 | scripts = "/usr/lib64/pypy3.8/bin" 13 | stdlib = "/usr/lib64/pypy3.8/lib/pypy3.8" 14 | 15 | Variables: 16 | ABIFLAGS = "" 17 | AR = "ar" 18 | ARFLAGS = "rc" 19 | CC = "cc -pthread" 20 | CCSHARED = "-fPIC" 21 | CFLAGS = "-DNDEBUG -O2" 22 | CXX = "c++ -pthread" 23 | EXE = "" 24 | EXT_SUFFIX = ".pypy38-pp73-ppc_64-linux-gnu.so" 25 | INCLUDEPY = "/usr/lib64/pypy3.8/include/pypy3.8" 26 | LDFLAGS = "-Wl,-Bsymbolic-functions" 27 | LDLIBRARY = "libpypy3-c.so" 28 | LDSHARED = "cc -pthread -shared -Wl,-Bsymbolic-functions" 29 | LDVERSION = "3.8" 30 | LIBDIR = "/usr/lib64/pypy3.8/bin" 31 | LIBRARY = "" 32 | MULTIARCH = "ppc_64-linux-gnu" 33 | OPT = "-DNDEBUG -O2" 34 | Py_DEBUG = "0" 35 | Py_ENABLE_SHARED = "0" 36 | SHLIB_SUFFIX = ".so" 37 | SIZEOF_VOID_P = "8" 38 | SO = ".pypy38-pp73-ppc_64-linux-gnu.so" 39 | SOABI = "pypy38-pp73" 40 | VERSION = "3.8" 41 | abiflags = "" 42 | base = "/usr/lib64/pypy3.8" 43 | exec_prefix = "/usr/lib64/pypy3.8" 44 | implementation = "PyPy" 45 | implementation_lower = "pypy" 46 | installed_base = "/usr/lib64/pypy3.8" 47 | installed_platbase = "/usr/lib64/pypy3.8" 48 | platbase = "/usr/lib64/pypy3.8" 49 | prefix = "/usr/lib64/pypy3.8" 50 | projectbase = "/usr/lib64/pypy3.8/bin" 51 | py_version = "3.8.13" 52 | py_version_nodot = "38" 53 | py_version_short = "3.8" 54 | srcdir = "/usr/lib64/pypy3.8/bin" 55 | userbase = "/root/.local" 56 | User: 57 | data = "/root/.local" 58 | include = "/root/.local/include/pypy3.8" 59 | platlib = "/root/.local/lib/pypy3.8/site-packages" 60 | platstdlib = "/root/.local/lib/pypy3.8" 61 | purelib = "/root/.local/lib/pypy3.8/site-packages" 62 | scripts = "/root/.local/bin" 63 | stdlib = "/root/.local/lib/pypy3.8" 64 | -------------------------------------------------------------------------------- /src/auditwheel/musllinux-policy.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"name": "linux", 3 | "aliases": [], 4 | "priority": 0, 5 | "symbol_versions": {}, 6 | "lib_whitelist": [], 7 | "blacklist": {} 8 | }, 9 | {"name": "musllinux_1_1", 10 | "aliases": [], 11 | "priority": 100, 12 | "symbol_versions": { 13 | "i686": { 14 | }, 15 | "x86_64": { 16 | }, 17 | "aarch64": { 18 | }, 19 | "ppc64le": { 20 | }, 21 | "s390x": { 22 | }, 23 | "armv7l": { 24 | }, 25 | "riscv64": { 26 | } 27 | }, 28 | "lib_whitelist": ["libc.so", "libz.so.1"], 29 | "blacklist": { 30 | "libz.so.1": ["_dist_code", "_length_code", "_tr_align", "_tr_flush_block", "_tr_init", "_tr_stored_block", "_tr_tally", "bi_windup", "crc32_vpmsum", "crc_fold_512to32", "crc_fold_copy", "crc_fold_init", "deflate_copyright", "deflate_medium", "fill_window", "flush_pending", "gzflags", "inflate_copyright", "inflate_fast", "inflate_table", "longest_match", "slide_hash_sse", "static_ltree", "uncompress2", "x86_check_features", "x86_cpu_has_pclmul", "x86_cpu_has_sse2", "x86_cpu_has_sse42", "z_errmsg", "zcalloc", "zcfree"] 31 | }}, 32 | {"name": "musllinux_1_2", 33 | "aliases": [], 34 | "priority": 90, 35 | "symbol_versions": { 36 | "i686": { 37 | }, 38 | "x86_64": { 39 | }, 40 | "aarch64": { 41 | }, 42 | "ppc64le": { 43 | }, 44 | "s390x": { 45 | }, 46 | "armv7l": { 47 | }, 48 | "riscv64": { 49 | }, 50 | "loongarch64": { 51 | } 52 | }, 53 | "lib_whitelist": ["libc.so", "libz.so.1"], 54 | "blacklist": { 55 | "libz.so.1": ["_dist_code", "_length_code", "_tr_align", "_tr_flush_block", "_tr_init", "_tr_stored_block", "_tr_tally", "bi_windup", "crc32_vpmsum", "crc_fold_512to32", "crc_fold_copy", "crc_fold_init", "deflate_copyright", "deflate_medium", "fill_window", "flush_pending", "gzflags", "inflate_copyright", "inflate_fast", "inflate_table", "longest_match", "slide_hash_sse", "static_ltree", "uncompress2", "x86_check_features", "x86_cpu_has_pclmul", "x86_cpu_has_sse2", "x86_cpu_has_sse42", "z_errmsg", "zcalloc", "zcfree"] 56 | }} 57 | ] 58 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # run all hooks with: 2 | # pre-commit run --hook-stage manual --all 3 | ci: 4 | skip: 5 | # pre-commit.ci doesn't have Rust installed 6 | - cargo-fmt 7 | - cargo-deny 8 | 9 | repos: 10 | - repo: local 11 | hooks: 12 | - id: cargo-fmt # rustup component add rustfmt 13 | name: cargo fmt 14 | entry: cargo fmt --all -- 15 | language: system 16 | types: [rust] 17 | pass_filenames: false 18 | 19 | - id: cargo-deny # cargo install --locked cargo-deny 20 | name: cargo deny 21 | entry: cargo deny --all-features check -- 22 | language: system 23 | pass_filenames: false 24 | 25 | - id: cargo-check 26 | name: cargo check 27 | entry: cargo check --all-features --all-targets -- 28 | language: system 29 | pass_filenames: false 30 | types: [rust] 31 | stages: [manual] # because it's slow 32 | 33 | - id: cargo-clippy # rustup component add clippy 34 | name: cargo clippy 35 | entry: cargo clippy --tests --all-features -- -D warnings 36 | language: system 37 | pass_filenames: false 38 | types: [rust] 39 | stages: [manual] # because it's slow 40 | - repo: https://github.com/pre-commit/pre-commit-hooks 41 | rev: v6.0.0 42 | hooks: 43 | - id: check-yaml 44 | - id: check-toml 45 | - id: end-of-file-fixer 46 | exclude: | 47 | (?x)( 48 | (^sysconfig/)| 49 | (.*\.stdout) 50 | ) 51 | - id: trailing-whitespace 52 | exclude: | 53 | (?x)( 54 | (^sysconfig/)| 55 | (.*\.stdout) 56 | ) 57 | - id: mixed-line-ending 58 | - repo: https://github.com/astral-sh/ruff-pre-commit 59 | rev: v0.14.9 60 | hooks: 61 | - id: ruff-format 62 | - id: ruff 63 | - repo: https://github.com/pre-commit/mirrors-mypy 64 | rev: v1.19.1 65 | hooks: 66 | - id: mypy 67 | entry: mypy maturin/ 68 | pass_filenames: false 69 | - repo: https://github.com/codespell-project/codespell 70 | rev: v2.4.1 71 | hooks: 72 | - id: codespell 73 | -------------------------------------------------------------------------------- /guide/src/platform_support.md: -------------------------------------------------------------------------------- 1 | # Platform Support 2 | 3 | Being built on cargo and rustc, maturin is limited by [rust's platform support](https://doc.rust-lang.org/nightly/rustc/platform-support.html). 4 | 5 | ## Automated tests 6 | 7 | On GitHub actions, windows, macOS and linux are tested, all 8 | on 64-bit x86. FreeBSD is also tested though Cirrus CI, but might get removed at 9 | some point. Since CI is very time intensive to maintain, I'd like to stick to 10 | GitHub action and these three platforms. 11 | 12 | ## Releases 13 | 14 | The following targets are built into wheels and downloadable binaries: 15 | 16 | * Windows: 32-bit and 64-bit x86 as well as arm64 17 | * Linux: x86, x86_64, armv7, aarch64 and ppc64le (musl), as well as s390x (gnu) 18 | * macOS: x86_64 and aarch64 19 | 20 | ## Other Operating Systems 21 | 22 | It should be possible to build maturin and for maturin to build wheels on other platforms supported by rust. 23 | To add a new os, add it in target.rs and, if it doesn't behave like the other unixes, in 24 | `PythonInterpreter::get_tag`. Please also submit the output of `python -m sysconfig` as a file in the `sysconfig` folder. 25 | It's ok to edit setup.py to deactivate default features so `pip install` works, but new platforms should not 26 | require complex workaround in `compile.rs`. 27 | 28 | ## Architectures 29 | 30 | All architectures included in manylinux (aarch64, armv7l, ppc64le, ppc64, i686, x86_64, s390x) are supported. 31 | I'm not sure whether it makes sense to allow architectures that aren't even 32 | supported by [manylinux](https://github.com/pypa/manylinux). 33 | 34 | ## Python Support 35 | 36 | CPython 3.8 to 3.14 are supported and tested on CI, though the entire 3.x series should work. 37 | This will be changed as new python versions are released and others have their end of life. 38 | 39 | PyPy 3.8 and later also works, as does GraalPy 23.0 and later. 40 | 41 | ## Manylinux/Musllinux 42 | 43 | `manylinux2014` and its newer versions as well as `musllinux_1_1` and its newer versions 44 | are supported. 45 | 46 | Since Rust and the manylinux project drop support for old manylinux/musllinux versions sometimes, 47 | after maturin 1.0 manylinux version bumps will be minor versions rather than major versions. 48 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: [ 'v*' ] 8 | 9 | jobs: 10 | publish-docker: 11 | name: Publish Docker Images 12 | runs-on: ubuntu-latest 13 | environment: 14 | name: Docker Hub 15 | url: https://ghcr.io/pyo3/maturin 16 | steps: 17 | - uses: actions/checkout@v6 18 | - uses: dorny/paths-filter@v3 19 | id: changes 20 | with: 21 | filters: | 22 | changed: 23 | - 'Cargo.toml' 24 | - 'Cargo.lock' 25 | - 'src/**' 26 | - 'Dockerfile' 27 | - '.github/workflows/docker.yml' 28 | - '.dockerignore' 29 | - name: Setup QEMU 30 | if: ${{ steps.changes.outputs.changed == 'true' || startsWith(github.ref, 'refs/tags/') }} 31 | uses: dbhi/qus/action@main 32 | - uses: docker/setup-buildx-action@v3 33 | if: ${{ steps.changes.outputs.changed == 'true' || startsWith(github.ref, 'refs/tags/') }} 34 | - uses: docker/metadata-action@v5 35 | if: ${{ steps.changes.outputs.changed == 'true' || startsWith(github.ref, 'refs/tags/') }} 36 | id: meta 37 | with: 38 | images: ghcr.io/pyo3/maturin 39 | - name: Login to GitHub Container Registry 40 | if: ${{ steps.changes.outputs.changed == 'true' || startsWith(github.ref, 'refs/tags/') }} 41 | uses: docker/login-action@v3 42 | with: 43 | registry: ghcr.io 44 | username: ${{ github.actor }} 45 | password: ${{ secrets.GITHUB_TOKEN }} 46 | - name: Build and push 47 | if: ${{ steps.changes.outputs.changed == 'true' || startsWith(github.ref, 'refs/tags/') }} 48 | uses: docker/build-push-action@v6 49 | with: 50 | context: . 51 | platforms: linux/amd64,linux/arm64 52 | push: true 53 | tags: ${{ steps.meta.outputs.tags }} 54 | labels: ${{ steps.meta.outputs.labels }} 55 | # https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#registry-cache 56 | cache-from: type=registry,ref=ghcr.io/pyo3/maturin:buildcache 57 | cache-to: type=registry,ref=ghcr.io/pyo3/maturin:buildcache,mode=max 58 | -------------------------------------------------------------------------------- /sysconfig/pypy-linux-3.7-7.3.txt: -------------------------------------------------------------------------------- 1 | Platform: "linux-x86_64" 2 | Python version: "3.7" 3 | Current installation scheme: "pypy" 4 | 5 | Paths: 6 | data = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3" 7 | include = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/include" 8 | platinclude = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/include" 9 | platlib = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/site-packages" 10 | platstdlib = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/lib-pypy" 11 | purelib = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/site-packages" 12 | scripts = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/bin" 13 | stdlib = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/lib-pypy" 14 | 15 | Variables: 16 | AR = "ar" 17 | ARFLAGS = "rc" 18 | CC = "gcc -pthread" 19 | CCSHARED = "-fPIC" 20 | CFLAGS = "-DNDEBUG -O2" 21 | CXX = "g++ -pthread" 22 | EXE = "" 23 | EXT_SUFFIX = ".pypy37-pp73-x86_64-linux-gnu.so" 24 | GNULD = "yes" 25 | INCLUDEPY = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/include" 26 | LDSHARED = "gcc -pthread -shared" 27 | LIBDIR = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/bin" 28 | LIBRARY = "" 29 | OPT = "-DNDEBUG -O2" 30 | SHLIB_SUFFIX = ".so" 31 | SO = ".pypy37-pp73-x86_64-linux-gnu.so" 32 | SOABI = "pypy37-pp73" 33 | abiflags = "" 34 | base = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3" 35 | exec_prefix = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3" 36 | implementation = "PyPy" 37 | implementation_lower = "pypy" 38 | installed_base = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3" 39 | installed_platbase = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3" 40 | platbase = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3" 41 | prefix = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3" 42 | projectbase = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/bin" 43 | py_version = "3.7.9" 44 | py_version_nodot = "37" 45 | py_version_short = "3.7" 46 | srcdir = "/home/konsti/.pyenv/versions/pypy3.7-7.3.3/lib-pypy/config-3.7" 47 | userbase = "/home/konsti/.local" 48 | User: 49 | data = "/home/konsti/.local" 50 | include = "/home/konsti/.local/include/pypy3.7" 51 | platlib = "/home/konsti/.local/lib/pypy3.7/site-packages" 52 | platstdlib = "/home/konsti/.local/lib/pypy3.7" 53 | purelib = "/home/konsti/.local/lib/pypy3.7/site-packages" 54 | scripts = "/home/konsti/.local/bin" 55 | stdlib = "/home/konsti/.local/lib/pypy3.7" 56 | -------------------------------------------------------------------------------- /src/generate_json_schema.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "schemars")] 2 | 3 | use fs_err as fs; 4 | use std::path::PathBuf; 5 | 6 | use anyhow::{Result, bail}; 7 | use pretty_assertions::StrComparison; 8 | use schemars::schema_for; 9 | 10 | use crate::pyproject_toml::ToolMaturin; 11 | 12 | #[derive(Debug, Copy, Clone, PartialEq, Eq, clap::ValueEnum, Default)] 13 | /// The mode to use when generating the JSON schema. 14 | pub enum Mode { 15 | /// Write the JSON schema to the file. 16 | #[default] 17 | Write, 18 | /// Check if the JSON schema is up-to-date. 19 | Check, 20 | /// Print the JSON schema to stdout. 21 | DryRun, 22 | } 23 | 24 | /// Generate the JSON schema for the `pyproject.toml` file. 25 | #[derive(Debug, clap::Parser)] 26 | pub struct GenerateJsonSchemaOptions { 27 | /// The mode to use when generating the JSON schema. 28 | #[arg(long, default_value_t, value_enum)] 29 | pub mode: Mode, 30 | } 31 | 32 | /// Generate the JSON schema for the `pyproject.toml` file. 33 | pub fn generate_json_schema(args: GenerateJsonSchemaOptions) -> Result<()> { 34 | let schema = schema_for!(ToolMaturin); 35 | let schema_string = serde_json::to_string_pretty(&schema).unwrap(); 36 | let filename = "maturin.schema.json"; 37 | let schema_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(filename); 38 | 39 | match args.mode { 40 | Mode::DryRun => { 41 | println!("{schema_string}"); 42 | } 43 | Mode::Check => { 44 | let current = fs::read_to_string(schema_path)?; 45 | if current == schema_string { 46 | println!("Up-to-date: {filename}"); 47 | } else { 48 | let comparison = StrComparison::new(¤t, &schema_string); 49 | bail!( 50 | "{filename} changed, please run `cargo run --features schemars -- generate-json-schema`:\n{comparison}", 51 | ); 52 | } 53 | } 54 | Mode::Write => { 55 | let current = fs::read_to_string(&schema_path)?; 56 | if current == schema_string { 57 | println!("Up-to-date: {filename}"); 58 | } else { 59 | println!("Updating: {filename}"); 60 | fs::write(schema_path, schema_string.as_bytes())?; 61 | } 62 | } 63 | } 64 | 65 | Ok(()) 66 | } 67 | -------------------------------------------------------------------------------- /test-crates/pyo3-mixed/check_installed/check_installed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import json 3 | import os.path 4 | import platform 5 | import sys 6 | from pathlib import Path 7 | from subprocess import check_output 8 | 9 | from boltons.strutils import slugify 10 | 11 | import pyo3_mixed 12 | 13 | assert pyo3_mixed.get_42() == 42 14 | assert slugify("First post! Hi!!!!~1 ") == "first_post_hi_1" 15 | 16 | script_name = "print_cli_args" 17 | args = ["a", "b", "c"] 18 | [rust_args, python_args] = check_output([script_name, *args], text=True).splitlines() 19 | # The rust vec debug format is also valid json 20 | rust_args = json.loads(rust_args) 21 | python_args = json.loads(python_args) 22 | 23 | # On alpine/musl, rust_args is empty so we skip all tests on musl 24 | if len(rust_args) > 0: 25 | # On linux we get sys.executable, windows resolve the path and mac os gives us a third 26 | # path ( 27 | # {prefix}/Python.framework/Versions/3.10/Resources/Python.app/Contents/MacOS/Python 28 | # vs 29 | # {prefix}/Python.framework/Versions/3.10/bin/python3.10 30 | # on cirrus ci) 31 | # On windows, cpython resolves while pypy doesn't. 32 | # The script for cpython is actually a distinct file from the system interpreter for 33 | # windows and mac 34 | if platform.system() == "Linux": 35 | assert os.path.samefile(rust_args[0], sys.executable), ( 36 | rust_args, 37 | sys.executable, 38 | os.path.realpath(rust_args[0]), 39 | os.path.realpath(sys.executable), 40 | ) 41 | 42 | # Windows can't decide if it's with or without .exe, FreeBSB just doesn't work for some reason 43 | if platform.system() in ["Darwin", "Linux"]: 44 | # Unix venv layout (and hopefully also on more exotic platforms) 45 | print_cli_args = str(Path(sys.prefix).joinpath("bin").joinpath(script_name)) 46 | assert rust_args[1] == print_cli_args, (rust_args, print_cli_args) 47 | assert python_args[0] == print_cli_args, (python_args, print_cli_args) 48 | 49 | # FreeBSB just doesn't work for some reason 50 | if platform.system() in ["Darwin", "Linux", "Windows"]: 51 | # Rust contains the python executable as first argument but python does not 52 | assert rust_args[2:] == args, rust_args 53 | assert python_args[1:] == args, python_args 54 | 55 | print("SUCCESS") 56 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug Report 2 | description: Create a bug report 3 | labels: [bug] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking the time to fill out this bug report! 9 | Please fill out the form below... 10 | - type: textarea 11 | id: description 12 | attributes: 13 | label: Bug Description 14 | description: Please provide a clear and concise description of what the bug is. 15 | placeholder: The bug is... 16 | validations: 17 | required: true 18 | - type: input 19 | id: maturin_version 20 | attributes: 21 | label: Your maturin version (`maturin --version`) 22 | placeholder: ex. 0.14.1 23 | validations: 24 | required: true 25 | - type: input 26 | id: py_version 27 | attributes: 28 | label: Your Python version (`python -V`) 29 | placeholder: ex. Python 3.10.0 30 | validations: 31 | required: true 32 | - type: input 33 | id: pip_version 34 | attributes: 35 | label: Your pip version (`pip -V`) 36 | placeholder: ex. pip 21.1.3 37 | validations: 38 | required: true 39 | - type: dropdown 40 | id: bindings 41 | attributes: 42 | label: What bindings you're using 43 | options: 44 | - "pyo3" 45 | - "cffi" 46 | - "uniffi" 47 | - "bin" 48 | validations: 49 | required: false 50 | - type: checkboxes 51 | id: cargo-build 52 | attributes: 53 | label: Does `cargo build` work? 54 | options: 55 | - label: Yes, it works 56 | required: false 57 | - type: checkboxes 58 | id: windows-unix-path 59 | attributes: 60 | label: If on windows, have you checked that you aren't accidentally using unix path (those with the forward slash `/`)? 61 | options: 62 | - label: "Yes" 63 | required: false 64 | - type: textarea 65 | id: reproduce 66 | attributes: 67 | label: Steps to Reproduce 68 | description: Please list the exact steps required to reproduce your error with all command output and if possible with a repository. Please run maturin with `RUST_LOG=maturin=debug` being set, e.g. `RUST_LOG=maturin=debug maturin build` and share the output, either in a codeblock or as a [gist](https://gist.github.com/) 69 | placeholder: | 70 | 1. 71 | 2. 72 | 3. 73 | validations: 74 | required: true 75 | -------------------------------------------------------------------------------- /test-crates/pyo3-ffi-pure/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use pyo3_ffi::*; 4 | 5 | static mut MODULE_DEF: PyModuleDef = PyModuleDef { 6 | m_base: PyModuleDef_HEAD_INIT, 7 | m_name: c"string_sum".as_ptr(), 8 | m_doc: c"A Python module written in Rust.".as_ptr(), 9 | m_size: 0, 10 | m_methods: std::ptr::addr_of_mut!(METHODS).cast(), 11 | m_slots: std::ptr::addr_of_mut!(SLOTS).cast(), 12 | m_traverse: None, 13 | m_clear: None, 14 | m_free: None, 15 | }; 16 | 17 | static mut METHODS: [PyMethodDef; 2] = [ 18 | PyMethodDef { 19 | ml_name: c"sum".as_ptr(), 20 | ml_meth: PyMethodDefPointer { 21 | PyCFunctionWithKeywords: sum, 22 | }, 23 | ml_flags: METH_VARARGS | METH_KEYWORDS, 24 | ml_doc: c"returns the sum of two integers".as_ptr(), 25 | }, 26 | // A zeroed PyMethodDef to mark the end of the array. 27 | PyMethodDef::zeroed(), 28 | ]; 29 | 30 | const SLOTS_LEN: usize = 31 | 1 + if cfg!(Py_3_12) { 1 } else { 0 } + if cfg!(Py_GIL_DISABLED) { 1 } else { 0 }; 32 | 33 | static mut SLOTS: [PyModuleDef_Slot; SLOTS_LEN] = [ 34 | // NB: only include this slot if the module does not store any global state in `static` variables 35 | // or other data which could cross between subinterpreters 36 | #[cfg(Py_3_12)] 37 | PyModuleDef_Slot { 38 | slot: Py_mod_multiple_interpreters, 39 | value: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED, 40 | }, 41 | // NB: only include this slot if the module does not depend on the GIL for thread safety 42 | #[cfg(Py_GIL_DISABLED)] 43 | PyModuleDef_Slot { 44 | slot: Py_mod_gil, 45 | value: Py_MOD_GIL_NOT_USED, 46 | }, 47 | PyModuleDef_Slot { 48 | slot: 0, 49 | value: ptr::null_mut(), 50 | }, 51 | ]; 52 | 53 | // The module initialization function, which must be named `PyInit_`. 54 | #[allow(non_snake_case)] 55 | #[no_mangle] 56 | pub unsafe extern "C" fn PyInit_pyo3_ffi_pure() -> *mut PyObject { 57 | PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF)) 58 | } 59 | 60 | #[no_mangle] 61 | pub unsafe extern "C" fn sum( 62 | _self: *mut PyObject, 63 | args: *mut PyObject, 64 | _kwds: *mut PyObject, 65 | ) -> *mut PyObject { 66 | // this is a minimal test of compilation, not good example code 67 | let val_a = PyTuple_GetItem(args, 0); 68 | let val_b = PyTuple_GetItem(args, 1); 69 | let res: i64 = PyLong_AsLongLong(val_a) + PyLong_AsLongLong(val_b); 70 | PyLong_FromLongLong(res) 71 | } 72 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # maturin is self bootstrapping, however on platforms like FreeBSD that aren't 2 | # manylinux/musllinux, pip will try installing maturin from the source distribution. 3 | # That source distribution obviously can't depend on maturin, so we're using 4 | # the always available setuptools. 5 | # 6 | # Note that this is really only a workaround for bootstrapping and not suited 7 | # for general purpose packaging, i.e. only building a wheel (as in 8 | # `python setup.py bdist_wheel`) and installing (as in 9 | # `pip install `) are supported. For creating a source distribution 10 | # for maturin itself use `maturin sdist`. 11 | 12 | import os 13 | import shlex 14 | import shutil 15 | 16 | try: 17 | import tomllib 18 | except ModuleNotFoundError: 19 | import tomli as tomllib 20 | from setuptools import setup 21 | 22 | from setuptools_rust import RustBin 23 | 24 | # Force the wheel to be platform specific 25 | # https://stackoverflow.com/a/45150383/3549270 26 | # There's also the much more concise solution in 27 | # https://stackoverflow.com/a/53463910/3549270, 28 | # but that would require python-dev 29 | try: 30 | # noinspection PyPackageRequirements,PyUnresolvedReferences 31 | from wheel.bdist_wheel import bdist_wheel as _bdist_wheel 32 | 33 | # noinspection PyPep8Naming,PyAttributeOutsideInit 34 | class bdist_wheel(_bdist_wheel): 35 | def finalize_options(self): 36 | _bdist_wheel.finalize_options(self) 37 | self.root_is_pure = False 38 | 39 | except ImportError: 40 | bdist_wheel = None 41 | 42 | with open("Cargo.toml", "rb") as fp: 43 | version = tomllib.load(fp)["package"]["version"] 44 | 45 | # Use `--no-default-features` by default for a minimal build to support PEP 517. 46 | # `MATURIN_SETUP_ARGS` env var can be used to pass customized arguments to cargo. 47 | cargo_args = ["--no-default-features"] 48 | if os.getenv("MATURIN_SETUP_ARGS"): 49 | cargo_args = shlex.split(os.getenv("MATURIN_SETUP_ARGS", "")) 50 | 51 | if not os.environ.get("MATURIN_NO_INSTALL_RUST") and not shutil.which("cargo"): 52 | from puccinialin import setup_rust 53 | 54 | print("Rust not found, installing into a temporary directory") 55 | extra_env = setup_rust() 56 | env = {**os.environ, **extra_env} 57 | else: 58 | env = None 59 | 60 | setup( 61 | version=version, 62 | cmdclass={"bdist_wheel": bdist_wheel}, 63 | rust_extensions=[RustBin("maturin", args=cargo_args, cargo_manifest_args=["--locked"], env=env)], 64 | zip_safe=False, 65 | ) 66 | -------------------------------------------------------------------------------- /sysconfig/pypy-macos-3.7-7.3.txt: -------------------------------------------------------------------------------- 1 | Platform: "macosx-10.7-x86_64" 2 | Python version: "3.7" 3 | Current installation scheme: "pypy" 4 | 5 | Paths: 6 | data = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5" 7 | include = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/include" 8 | platinclude = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/include" 9 | platlib = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/site-packages" 10 | platstdlib = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/lib-pypy" 11 | purelib = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/site-packages" 12 | scripts = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/bin" 13 | stdlib = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/lib-pypy" 14 | 15 | Variables: 16 | AR = "ar" 17 | ARFLAGS = "rc" 18 | CC = "gcc -pthread -arch x86_64" 19 | CCSHARED = "-fPIC" 20 | CFLAGS = "-DNDEBUG -O2" 21 | CXX = "g++ -pthread -arch x86_64" 22 | EXE = "" 23 | EXT_SUFFIX = ".pypy37-pp73-darwin.so" 24 | GNULD = "yes" 25 | INCLUDEPY = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/include" 26 | LDLIBRARY = "libpypy3-c.so" 27 | LDSHARED = "gcc -pthread -arch x86_64 -shared -undefined dynamic_lookup" 28 | LIBDIR = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/bin" 29 | LIBRARY = "" 30 | MACOSX_DEPLOYMENT_TARGET = "10.7" 31 | OPT = "-DNDEBUG -O2" 32 | SHLIB_SUFFIX = ".so" 33 | SO = ".pypy37-pp73-darwin.so" 34 | SOABI = "pypy37-pp73" 35 | abiflags = "" 36 | base = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5" 37 | exec_prefix = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5" 38 | implementation = "PyPy" 39 | implementation_lower = "pypy" 40 | installed_base = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5" 41 | installed_platbase = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5" 42 | platbase = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5" 43 | prefix = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5" 44 | projectbase = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/bin" 45 | py_version = "3.7.10" 46 | py_version_nodot = "37" 47 | py_version_short = "3.7" 48 | srcdir = "/Users/messense/.pyenv/versions/pypy3.7-7.3.5/lib-pypy/config-3.7" 49 | userbase = "/Users/messense/.local" 50 | User: 51 | data = "/Users/messense/.local" 52 | include = "/Users/messense/.local/include/pypy3.7" 53 | platlib = "/Users/messense/.local/lib/pypy3.7/site-packages" 54 | platstdlib = "/Users/messense/.local/lib/pypy3.7" 55 | purelib = "/Users/messense/.local/lib/pypy3.7/site-packages" 56 | scripts = "/Users/messense/.local/bin" 57 | stdlib = "/Users/messense/.local/lib/pypy3.7" -------------------------------------------------------------------------------- /src/target/legacy_py.rs: -------------------------------------------------------------------------------- 1 | //! This module is taken mostly verbatim from PyPI's `legacy.py` validation logic. 2 | //! 3 | //! 4 | 5 | use once_cell::sync::Lazy; 6 | use regex::Regex; 7 | 8 | pub(super) static MACOS_PLATFORM_RE: Lazy = 9 | Lazy::new(|| Regex::new(r"^macosx_(?P\d+)_(?:\d+)_(?P.+)$").unwrap()); 10 | 11 | pub(super) static IOS_PLATFORM_RE: Lazy = Lazy::new(|| { 12 | Regex::new(r"^ios_(?:\d+)_(?:\d+)_(?P.+)_(?:iphoneos|iphonesimulator)$").unwrap() 13 | }); 14 | 15 | pub(super) static ANDROID_PLATFORM_RE: Lazy = 16 | Lazy::new(|| Regex::new(r"^android_(?:\d+)_(?P.+)$").unwrap()); 17 | 18 | pub(super) static LINUX_PLATFORM_RE: Lazy = Lazy::new(|| { 19 | Regex::new(r"^(?P(?:many|musl))linux_(?:\d+)_(?:\d+)_(?P.+)$").unwrap() 20 | }); 21 | 22 | /// Contains also non-Rust platforms to match `legacy.py` verbatim. 23 | pub(super) static ALLOWED_PLATFORMS: &[&str] = &[ 24 | "any", 25 | "win32", 26 | "win_arm64", 27 | "win_amd64", 28 | "win_ia64", 29 | "manylinux1_x86_64", 30 | "manylinux1_i686", 31 | "manylinux2010_x86_64", 32 | "manylinux2010_i686", 33 | "manylinux2014_x86_64", 34 | "manylinux2014_i686", 35 | "manylinux2014_aarch64", 36 | "manylinux2014_armv7l", 37 | "manylinux2014_ppc64", 38 | "manylinux2014_ppc64le", 39 | "manylinux2014_s390x", 40 | "linux_armv6l", 41 | "linux_armv7l", 42 | ]; 43 | 44 | /// Windows platforms: win32 (i686), win_amd64 (x86_64), win_arm64 (aarch64). 45 | /// 46 | /// PyPI allows win_ia64 but Rust doesn't support IA64/Itanium. 47 | pub(super) static WINDOWS_ARCHES: &[&str] = &["x86_64", "i686", "aarch64"]; 48 | 49 | /// Reduced list only containing targets support by both Rust/maturin and PyPI. 50 | pub(super) static MACOS_ARCHES: &[&str] = &["x86_64", "arm64", "i686", "universal2"]; 51 | 52 | /// Those are actually hardcoded in warehouse in the same way. 53 | pub(super) static MACOS_MAJOR_VERSIONS: &[&str] = &["10", "11", "12", "13", "14", "15"]; 54 | 55 | pub(super) static IOS_ARCHES: &[&str] = &["arm64", "x86_64"]; 56 | 57 | pub(super) static ANDROID_ARCHES: &[&str] = &["armeabi_v7a", "arm64_v8a", "x86", "x86_64"]; 58 | 59 | pub(super) static MANYLINUX_ARCHES: &[&str] = &[ 60 | "x86_64", "i686", "aarch64", "armv7l", "ppc64le", "s390x", "ppc64", "riscv64", 61 | ]; 62 | 63 | pub(super) static MUSLLINUX_ARCHES: &[&str] = 64 | &["x86_64", "i686", "aarch64", "armv7l", "ppc64le", "s390x"]; 65 | -------------------------------------------------------------------------------- /test-crates/pyo3-no-extension-module/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.3.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 10 | 11 | [[package]] 12 | name = "libc" 13 | version = "0.2.155" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 16 | 17 | [[package]] 18 | name = "memoffset" 19 | version = "0.9.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 22 | dependencies = [ 23 | "autocfg", 24 | ] 25 | 26 | [[package]] 27 | name = "once_cell" 28 | version = "1.21.3" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 31 | 32 | [[package]] 33 | name = "portable-atomic" 34 | version = "1.6.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" 37 | 38 | [[package]] 39 | name = "pyo3" 40 | version = "0.25.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a" 43 | dependencies = [ 44 | "libc", 45 | "memoffset", 46 | "once_cell", 47 | "portable-atomic", 48 | "pyo3-build-config", 49 | "pyo3-ffi", 50 | ] 51 | 52 | [[package]] 53 | name = "pyo3-build-config" 54 | version = "0.25.1" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598" 57 | dependencies = [ 58 | "once_cell", 59 | "target-lexicon", 60 | ] 61 | 62 | [[package]] 63 | name = "pyo3-ffi" 64 | version = "0.25.1" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c" 67 | dependencies = [ 68 | "libc", 69 | "pyo3-build-config", 70 | ] 71 | 72 | [[package]] 73 | name = "pyo3-no-extension-module" 74 | version = "2.1.0" 75 | dependencies = [ 76 | "pyo3", 77 | ] 78 | 79 | [[package]] 80 | name = "target-lexicon" 81 | version = "0.13.2" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" 84 | -------------------------------------------------------------------------------- /tests/cmd/upload.stdout: -------------------------------------------------------------------------------- 1 | Upload python packages to pypi 2 | 3 | It is mostly similar to `twine upload`, but can only upload python wheels and source distributions. 4 | 5 | Usage: maturin[EXE] upload [OPTIONS] [FILE]... 6 | 7 | Arguments: 8 | [FILE]... 9 | The python packages to upload 10 | 11 | Options: 12 | -r, --repository 13 | The repository (package index) to upload the package to. Should be a section in the config 14 | file. 15 | 16 | Can also be set via MATURIN_REPOSITORY environment variable. 17 | 18 | [env: MATURIN_REPOSITORY=] 19 | [default: pypi] 20 | 21 | -v, --verbose... 22 | Use verbose output. 23 | 24 | * Default: Show build information and `cargo build` output. * `-v`: Use `cargo build -v`. 25 | * `-vv`: Show debug logging and use `cargo build -vv`. * `-vvv`: Show trace logging. 26 | 27 | You can configure fine-grained logging using the `RUST_LOG` environment variable. 28 | () 29 | 30 | --repository-url 31 | The URL of the registry where the wheels are uploaded to. This overrides --repository. 32 | 33 | Can also be set via MATURIN_REPOSITORY_URL environment variable. 34 | 35 | [env: MATURIN_REPOSITORY_URL=] 36 | 37 | -u, --username 38 | Username for pypi or your custom registry. 39 | 40 | Can also be set via MATURIN_USERNAME environment variable. 41 | 42 | Set MATURIN_PYPI_TOKEN variable to use token-based authentication instead 43 | 44 | [env: MATURIN_USERNAME=] 45 | 46 | -p, --password 47 | Password for pypi or your custom registry. 48 | 49 | Can also be set via MATURIN_PASSWORD environment variable. 50 | 51 | [env: MATURIN_PASSWORD] 52 | 53 | --skip-existing 54 | Continue uploading files if one already exists. (Only valid when uploading to PyPI. Other 55 | implementations may not support this.) 56 | 57 | --non-interactive 58 | Do not interactively prompt for username/password if the required credentials are missing. 59 | 60 | Can also be set via MATURIN_NON_INTERACTIVE environment variable. 61 | 62 | [env: MATURIN_NON_INTERACTIVE=] 63 | 64 | -h, --help 65 | Print help (see a summary with '-h') 66 | -------------------------------------------------------------------------------- /guide/src/environment-variables.md: -------------------------------------------------------------------------------- 1 | # Environment Variables 2 | 3 | Maturin reads a number of environment variables which you can use to configure the build process. 4 | Here is a list of all environment variables that are read by maturin: 5 | 6 | ## Cargo environment variables 7 | 8 | See [environment variables Cargo reads](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads) 9 | 10 | ## Python environment variables 11 | 12 | * `VIRTUAL_ENV`: Path to a Python virtual environment 13 | * `CONDA_PREFIX`: Path to a conda environment 14 | * `_PYTHON_SYSCONFIGDATA_NAME`: Name of a `sysconfigdata*.py` file 15 | * `MATURIN_PYPI_TOKEN`: PyPI token for uploading wheels 16 | * `MATURIN_PASSWORD`: PyPI password for uploading wheels 17 | * `MATURIN_PEP517_USE_BASE_PYTHON`: Use base Python executable instead of venv Python executable in PEP 517 build to avoid unnecessary rebuilds, should not be set when the sdist build requires packages installed in venv. 18 | 19 | ## `pyo3` environment variables 20 | 21 | * `PYO3_CROSS_PYTHON_VERSION`: Python version to use for cross compilation 22 | * `PYO3_CROSS_LIB_DIR`: This variable can be set to the directory containing the target's libpython DSO and the associated `_sysconfigdata*.py` file for Unix-like targets, or the Python DLL import libraries for the Windows target.This variable can be set to the directory containing the target's libpython DSO and the associated _sysconfigdata*.py file for Unix-like targets, or the Python DLL import libraries for the Windows target. 23 | * `PYO3_CONFIG_FILE`: Path to a [pyo3 config file](https://pyo3.rs/latest/building-and-distribution.html#advanced-config-files) 24 | 25 | ## Networking environment variables 26 | 27 | * `HTTP_PROXY` / `HTTPS_PROXY`: Proxy to use for HTTP/HTTPS requests 28 | * `REQUESTS_CA_BUNDLE` / `CURL_CA_BUNDLE`: Path to a CA bundle to use for HTTPS requests 29 | 30 | ## Other environment variables 31 | 32 | * `MACOSX_DEPLOYMENT_TARGET`: The minimum macOS version to target 33 | * `SOURCE_DATE_EPOCH`: The time to use for the timestamp in the wheel metadata 34 | * `MATURIN_EMSCRIPTEN_VERSION`: The version of emscripten to use for emscripten builds 35 | * `MATURIN_NO_MISSING_BUILD_BACKEND_WARNING`: Suppress missing build backend warning 36 | * `MATURIN_USE_XWIN`: Set to `1` to force to use `xwin` for cross compiling even on Windows that supports native compilation 37 | * `TARGET_SYSROOT`: The sysroot to use for auditwheel wheel when cross compiling 38 | * `ARCHFLAGS`: Flags to control the architecture of the build on macOS, for example you can use `ARCHFLAGS="-arch x86_64 -arch arm64"` to build universal2 wheels 39 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # x86_64 base 2 | FROM quay.io/pypa/manylinux2014_x86_64 AS base-amd64 3 | # x86_64 builder 4 | FROM --platform=$BUILDPLATFORM ghcr.io/rust-cross/rust-musl-cross:x86_64-musl AS builder-amd64 5 | 6 | # aarch64 base 7 | FROM quay.io/pypa/manylinux2014_aarch64 AS base-arm64 8 | # aarch64 cross compile builder 9 | FROM --platform=$BUILDPLATFORM ghcr.io/rust-cross/rust-musl-cross:aarch64-musl AS builder-arm64 10 | 11 | ARG TARGETARCH 12 | FROM builder-$TARGETARCH AS builder 13 | 14 | ENV PATH=/root/.cargo/bin:$PATH 15 | 16 | # Compile dependencies only for build caching 17 | ADD Cargo.toml /maturin/Cargo.toml 18 | ADD Cargo.lock /maturin/Cargo.lock 19 | RUN --mount=type=cache,target=/root/.cargo/git \ 20 | --mount=type=cache,target=/root/.cargo/registry \ 21 | --mount=type=cache,target=/maturin/target,sharing=locked \ 22 | mkdir /maturin/src && \ 23 | touch /maturin/src/lib.rs && \ 24 | echo 'fn main() { println!("Dummy") }' > /maturin/src/main.rs && \ 25 | cargo rustc --target $CARGO_BUILD_TARGET --bin maturin --manifest-path /maturin/Cargo.toml --release --features password-storage -- -C link-arg=-s 26 | 27 | ADD . /maturin/ 28 | 29 | # Manually update the timestamps as ADD keeps the local timestamps and cargo would then believe the cache is fresh 30 | RUN touch /maturin/src/lib.rs /maturin/src/main.rs 31 | 32 | RUN --mount=type=cache,target=/root/.cargo/git \ 33 | --mount=type=cache,target=/root/.cargo/registry \ 34 | --mount=type=cache,target=/maturin/target,sharing=locked \ 35 | cargo rustc --target $CARGO_BUILD_TARGET --bin maturin --manifest-path /maturin/Cargo.toml --release --features password-storage -- -C link-arg=-s \ 36 | && mv /maturin/target/$CARGO_BUILD_TARGET/release/maturin /usr/bin/maturin 37 | 38 | FROM base-$TARGETARCH 39 | 40 | ENV PATH=/root/.cargo/bin:$PATH 41 | # Add all supported python versions 42 | ENV PATH=/opt/python/cp39-cp39/bin:/opt/python/cp310-cp310/bin:/opt/python/cp311-cp311/bin:/opt/python/cp312-cp312/bin:/opt/python/cp313-cp313/bin/:/opt/python/cp313-cp313t/bin/:$PATH 43 | # Otherwise `cargo new` errors 44 | ENV USER=root 45 | 46 | RUN curl --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ 47 | && yum install -y libffi-devel openssh-clients \ 48 | && python3.8 -m pip install --no-cache-dir cffi \ 49 | && python3.9 -m pip install --no-cache-dir cffi \ 50 | && python3.10 -m pip install --no-cache-dir cffi \ 51 | && python3.11 -m pip install --no-cache-dir cffi \ 52 | && python3.12 -m pip install --no-cache-dir cffi \ 53 | && mkdir /io 54 | 55 | COPY --from=builder /usr/bin/maturin /usr/bin/maturin 56 | 57 | WORKDIR /io 58 | 59 | ENTRYPOINT ["/usr/bin/maturin"] 60 | -------------------------------------------------------------------------------- /src/module_writer/path_writer.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::Path; 3 | use std::path::PathBuf; 4 | 5 | use anyhow::Context as _; 6 | use anyhow::Result; 7 | use fs_err as fs; 8 | use fs_err::File; 9 | #[cfg(unix)] 10 | use fs_err::OpenOptions; 11 | #[cfg(unix)] 12 | use fs_err::os::unix::fs::OpenOptionsExt as _; 13 | 14 | use crate::archive_source::ArchiveSource; 15 | 16 | use super::ModuleWriterInternal; 17 | #[cfg(target_family = "unix")] 18 | use super::default_permission; 19 | 20 | /// A [ModuleWriter] that adds the module somewhere in the filesystem, e.g. in a virtualenv 21 | pub struct PathWriter { 22 | base_path: PathBuf, 23 | } 24 | 25 | impl super::private::Sealed for PathWriter {} 26 | 27 | impl ModuleWriterInternal for PathWriter { 28 | fn add_entry(&mut self, target: impl AsRef, source: ArchiveSource) -> Result<()> { 29 | let target = self.base_path.join(target); 30 | if let Some(parent_dir) = target.parent() { 31 | fs::create_dir_all(parent_dir) 32 | .with_context(|| format!("Failed to create directory {parent_dir:?}"))?; 33 | } 34 | 35 | // We only need to set the executable bit on unix 36 | let mut file = { 37 | #[cfg(target_family = "unix")] 38 | { 39 | OpenOptions::new() 40 | .create(true) 41 | .write(true) 42 | .truncate(true) 43 | .mode(default_permission(source.executable())) 44 | .open(&target) 45 | } 46 | #[cfg(target_os = "windows")] 47 | { 48 | File::create(&target) 49 | } 50 | } 51 | .with_context(|| format!("Failed to create a file at {target:?}"))?; 52 | 53 | match source { 54 | ArchiveSource::Generated(source) => io::copy(&mut source.data.as_slice(), &mut file), 55 | ArchiveSource::File(source) => { 56 | let mut source_file = 57 | File::options() 58 | .read(true) 59 | .open(&source.path) 60 | .with_context(|| { 61 | format!("Failed to open file at {:?} for reading", source.path) 62 | })?; 63 | 64 | io::copy(&mut source_file, &mut file) 65 | } 66 | } 67 | .context("Failed to copy entry to target")?; 68 | 69 | Ok(()) 70 | } 71 | } 72 | 73 | impl PathWriter { 74 | /// Writes the module to the given path 75 | pub fn from_path(path: impl AsRef) -> Self { 76 | Self { 77 | base_path: path.as_ref().to_path_buf(), 78 | } 79 | } 80 | } 81 | --------------------------------------------------------------------------------