├── .github ├── ISSUE_TEMPLATE │ ├── blank_issue.md │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── benches.yml │ ├── build.yml │ ├── cache-cleanup.yml │ ├── changelog.yml │ ├── ci.yml │ ├── coverage-pr-base.yml │ ├── gh-pages.yml │ └── release.yaml ├── .gitignore ├── .netlify ├── build.sh └── internal_banner.html ├── .towncrier.template.md ├── Architecture.md ├── CHANGELOG.md ├── CITATION.cff ├── Cargo.toml ├── Code-of-Conduct.md ├── Contributing.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── Releasing.md ├── assets └── script.py ├── branding ├── favicon │ ├── pyo3_16x16.png │ └── pyo3_32x32.png ├── pyo3logo.png ├── pyo3logo.svg ├── pyotr.png └── pyotr.svg ├── build.rs ├── codecov.yml ├── emscripten ├── .gitignore ├── Makefile ├── emscripten_patches │ └── 0001-Add-_gxx_personality_v0-stub-to-library.js.patch ├── env.sh ├── pybuilddir.txt └── runner.py ├── examples ├── Cargo.toml ├── README.md ├── decorator │ ├── .template │ │ ├── Cargo.toml │ │ ├── pre-script.rhai │ │ └── pyproject.toml │ ├── Cargo.toml │ ├── MANIFEST.in │ ├── README.md │ ├── cargo-generate.toml │ ├── noxfile.py │ ├── pyproject.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ ├── example.py │ │ └── test_.py ├── getitem │ ├── .template │ │ ├── Cargo.toml │ │ ├── pre-script.rhai │ │ └── pyproject.toml │ ├── Cargo.toml │ ├── MANIFEST.in │ ├── README.md │ ├── cargo-generate.toml │ ├── noxfile.py │ ├── pyproject.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ └── test_getitem.py ├── maturin-starter │ ├── .template │ │ ├── Cargo.toml │ │ ├── pre-script.rhai │ │ └── pyproject.toml │ ├── Cargo.toml │ ├── MANIFEST.in │ ├── README.md │ ├── cargo-generate.toml │ ├── maturin_starter │ │ └── __init__.py │ ├── noxfile.py │ ├── pyproject.toml │ ├── src │ │ ├── lib.rs │ │ └── submodule.rs │ └── tests │ │ ├── test_maturin_starter.py │ │ └── test_submodule.py ├── plugin │ ├── .DS_Store │ ├── .template │ │ ├── Cargo.toml │ │ ├── plugin_api │ │ │ └── Cargo.toml │ │ └── pre-script.rhai │ ├── Cargo.toml │ ├── README.md │ ├── cargo-generate.toml │ ├── plugin_api │ │ ├── Cargo.toml │ │ ├── noxfile.py │ │ ├── pyproject.toml │ │ ├── src │ │ │ └── lib.rs │ │ └── tests │ │ │ ├── test_Gadget.py │ │ │ └── test_import.py │ ├── python_plugin │ │ ├── gadget_init_plugin.py │ │ └── rng.py │ └── src │ │ └── main.rs ├── setuptools-rust-starter │ ├── .template │ │ ├── Cargo.toml │ │ ├── pre-script.rhai │ │ └── setup.cfg │ ├── Cargo.toml │ ├── MANIFEST.in │ ├── README.md │ ├── cargo-generate.toml │ ├── noxfile.py │ ├── pyproject.toml │ ├── requirements-dev.txt │ ├── setuptools_rust_starter │ │ └── __init__.py │ ├── src │ │ ├── lib.rs │ │ └── submodule.rs │ └── tests │ │ ├── test_setuptools_rust_starter.py │ │ └── test_submodule.py └── word-count │ ├── .template │ ├── Cargo.toml │ ├── pre-script.rhai │ └── pyproject.toml │ ├── Cargo.toml │ ├── MANIFEST.in │ ├── README.md │ ├── cargo-generate.toml │ ├── noxfile.py │ ├── pyproject.toml │ ├── src │ └── lib.rs │ ├── tests │ └── test_word_count.py │ └── word_count │ └── __init__.py ├── guide ├── book.toml ├── pyclass-parameters.md ├── pyo3_version.py ├── src │ ├── SUMMARY.md │ ├── advanced.md │ ├── async-await.md │ ├── building-and-distribution.md │ ├── building-and-distribution │ │ └── multiple-python-versions.md │ ├── changelog.md │ ├── class.md │ ├── class │ │ ├── call.md │ │ ├── numeric.md │ │ ├── object.md │ │ ├── protocols.md │ │ └── thread-safety.md │ ├── contributing.md │ ├── conversions.md │ ├── conversions │ │ ├── tables.md │ │ └── traits.md │ ├── debugging.md │ ├── ecosystem.md │ ├── ecosystem │ │ ├── async-await.md │ │ ├── logging.md │ │ └── tracing.md │ ├── exception.md │ ├── faq.md │ ├── features.md │ ├── free-threading.md │ ├── function-calls.md │ ├── function.md │ ├── function │ │ ├── error-handling.md │ │ └── signature.md │ ├── getting-started.md │ ├── index.md │ ├── migration.md │ ├── module.md │ ├── parallelism.md │ ├── performance.md │ ├── python-from-rust.md │ ├── python-from-rust │ │ ├── calling-existing-code.md │ │ └── function-calls.md │ ├── python-typing-hints.md │ ├── rust-from-python.md │ ├── trait-bounds.md │ └── types.md └── theme │ ├── tabs.css │ └── tabs.js ├── netlify.toml ├── newsfragments ├── .gitignore ├── 4364.added.md ├── 5121.added.md ├── 5121.changed.md ├── 5144.changed.md ├── 5145.packaging.md ├── 5146.changed.md ├── 5150.added.md ├── 5152.packaging.md ├── 5154.added.md ├── 5154.removed.md ├── 5156.fixed.md └── 5161.fixed.md ├── noxfile.py ├── pyo3-benches ├── Cargo.toml ├── benches │ ├── bench_any.rs │ ├── bench_bigint.rs │ ├── bench_call.rs │ ├── bench_comparisons.rs │ ├── bench_decimal.rs │ ├── bench_dict.rs │ ├── bench_err.rs │ ├── bench_extract.rs │ ├── bench_frompyobject.rs │ ├── bench_gil.rs │ ├── bench_intern.rs │ ├── bench_intopyobject.rs │ ├── bench_list.rs │ ├── bench_pyclass.rs │ ├── bench_pyobject.rs │ ├── bench_set.rs │ └── bench_tuple.rs └── build.rs ├── pyo3-build-config ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── build.rs └── src │ ├── errors.rs │ ├── impl_.rs │ ├── import_lib.rs │ └── lib.rs ├── pyo3-ffi-check ├── Cargo.toml ├── README.md ├── build.rs ├── macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── src │ └── main.rs └── wrapper.h ├── pyo3-ffi ├── ACKNOWLEDGEMENTS ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── examples │ ├── README.md │ ├── sequential │ │ ├── .template │ │ │ ├── Cargo.toml │ │ │ ├── pre-script.rhai │ │ │ └── pyproject.toml │ │ ├── Cargo.toml │ │ ├── MANIFEST.in │ │ ├── README.md │ │ ├── build.rs │ │ ├── cargo-generate.toml │ │ ├── noxfile.py │ │ ├── pyproject.toml │ │ ├── src │ │ │ ├── id.rs │ │ │ ├── lib.rs │ │ │ └── module.rs │ │ └── tests │ │ │ ├── test.rs │ │ │ └── test_.py │ └── string-sum │ │ ├── .template │ │ ├── Cargo.toml │ │ ├── pre-script.rhai │ │ └── pyproject.toml │ │ ├── Cargo.toml │ │ ├── MANIFEST.in │ │ ├── README.md │ │ ├── build.rs │ │ ├── cargo-generate.toml │ │ ├── noxfile.py │ │ ├── pyproject.toml │ │ ├── src │ │ └── lib.rs │ │ └── tests │ │ └── test_.py └── src │ ├── abstract_.rs │ ├── bltinmodule.rs │ ├── boolobject.rs │ ├── bytearrayobject.rs │ ├── bytesobject.rs │ ├── ceval.rs │ ├── code.rs │ ├── codecs.rs │ ├── compat │ ├── mod.rs │ ├── py_3_10.rs │ ├── py_3_13.rs │ ├── py_3_14.rs │ └── py_3_9.rs │ ├── compile.rs │ ├── complexobject.rs │ ├── context.rs │ ├── cpython │ ├── abstract_.rs │ ├── bytesobject.rs │ ├── ceval.rs │ ├── code.rs │ ├── compile.rs │ ├── complexobject.rs │ ├── critical_section.rs │ ├── descrobject.rs │ ├── dictobject.rs │ ├── floatobject.rs │ ├── frameobject.rs │ ├── funcobject.rs │ ├── genobject.rs │ ├── import.rs │ ├── initconfig.rs │ ├── listobject.rs │ ├── lock.rs │ ├── longobject.rs │ ├── methodobject.rs │ ├── mod.rs │ ├── object.rs │ ├── objimpl.rs │ ├── pydebug.rs │ ├── pyerrors.rs │ ├── pyframe.rs │ ├── pyhash.rs │ ├── pylifecycle.rs │ ├── pymem.rs │ ├── pystate.rs │ ├── pythonrun.rs │ ├── tupleobject.rs │ ├── unicodeobject.rs │ └── weakrefobject.rs │ ├── datetime.rs │ ├── descrobject.rs │ ├── dictobject.rs │ ├── enumobject.rs │ ├── fileobject.rs │ ├── fileutils.rs │ ├── floatobject.rs │ ├── genericaliasobject.rs │ ├── impl_ │ └── mod.rs │ ├── import.rs │ ├── intrcheck.rs │ ├── iterobject.rs │ ├── lib.rs │ ├── listobject.rs │ ├── longobject.rs │ ├── marshal.rs │ ├── memoryobject.rs │ ├── methodobject.rs │ ├── modsupport.rs │ ├── moduleobject.rs │ ├── object.rs │ ├── objimpl.rs │ ├── osmodule.rs │ ├── pyarena.rs │ ├── pybuffer.rs │ ├── pycapsule.rs │ ├── pyerrors.rs │ ├── pyframe.rs │ ├── pyhash.rs │ ├── pylifecycle.rs │ ├── pymem.rs │ ├── pyport.rs │ ├── pystate.rs │ ├── pystrtod.rs │ ├── pythonrun.rs │ ├── pytypedefs.rs │ ├── rangeobject.rs │ ├── refcount.rs │ ├── setobject.rs │ ├── sliceobject.rs │ ├── structmember.rs │ ├── structseq.rs │ ├── sysmodule.rs │ ├── traceback.rs │ ├── tupleobject.rs │ ├── typeslots.rs │ ├── unicodeobject.rs │ ├── warnings.rs │ └── weakrefobject.rs ├── pyo3-introspection ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── src │ ├── introspection.rs │ ├── lib.rs │ ├── model.rs │ └── stubs.rs └── tests │ └── test.rs ├── pyo3-macros-backend ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── build.rs └── src │ ├── attributes.rs │ ├── derive_attributes.rs │ ├── frompyobject.rs │ ├── intopyobject.rs │ ├── introspection.rs │ ├── konst.rs │ ├── lib.rs │ ├── method.rs │ ├── module.rs │ ├── params.rs │ ├── pyclass.rs │ ├── pyfunction.rs │ ├── pyfunction │ └── signature.rs │ ├── pyimpl.rs │ ├── pymethod.rs │ ├── pyversions.rs │ ├── quotes.rs │ └── utils.rs ├── pyo3-macros ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ └── lib.rs ├── pyo3-runtime ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── pyproject.toml ├── src │ └── pyo3_runtime │ │ └── __init__.py └── tests │ └── __init__.py ├── pyproject.toml ├── pytests ├── Cargo.toml ├── MANIFEST.in ├── README.md ├── build.rs ├── conftest.py ├── noxfile.py ├── pyproject.toml ├── src │ ├── awaitable.rs │ ├── buf_and_str.rs │ ├── comparisons.rs │ ├── consts.rs │ ├── datetime.rs │ ├── dict_iter.rs │ ├── enums.rs │ ├── lib.rs │ ├── misc.rs │ ├── objstore.rs │ ├── othermod.rs │ ├── path.rs │ ├── pyclasses.rs │ ├── pyfunctions.rs │ ├── sequence.rs │ └── subclassing.rs ├── stubs │ ├── __init__.pyi │ ├── consts.pyi │ ├── pyclasses.pyi │ └── pyfunctions.pyi └── tests │ ├── test_awaitable.py │ ├── test_buf_and_str.py │ ├── test_comparisons.py │ ├── test_datetime.py │ ├── test_dict_iter.py │ ├── test_enums.py │ ├── test_enums_match.py │ ├── test_hammer_gil_in_thread.py │ ├── test_misc.py │ ├── test_objstore.py │ ├── test_othermod.py │ ├── test_path.py │ ├── test_pyclasses.py │ ├── test_pyfunctions.py │ ├── test_sequence.py │ └── test_subclassing.py ├── src ├── buffer.rs ├── call.rs ├── conversion.rs ├── conversions │ ├── anyhow.rs │ ├── bigdecimal.rs │ ├── chrono.rs │ ├── chrono_tz.rs │ ├── either.rs │ ├── eyre.rs │ ├── hashbrown.rs │ ├── indexmap.rs │ ├── jiff.rs │ ├── mod.rs │ ├── num_bigint.rs │ ├── num_complex.rs │ ├── num_rational.rs │ ├── ordered_float.rs │ ├── rust_decimal.rs │ ├── serde.rs │ ├── smallvec.rs │ ├── std │ │ ├── array.rs │ │ ├── cell.rs │ │ ├── ipaddr.rs │ │ ├── map.rs │ │ ├── mod.rs │ │ ├── num.rs │ │ ├── option.rs │ │ ├── osstr.rs │ │ ├── path.rs │ │ ├── set.rs │ │ ├── slice.rs │ │ ├── string.rs │ │ ├── time.rs │ │ └── vec.rs │ ├── time.rs │ └── uuid.rs ├── coroutine.rs ├── coroutine │ ├── cancel.rs │ └── waker.rs ├── err │ ├── err_state.rs │ ├── impls.rs │ └── mod.rs ├── exceptions.rs ├── ffi │ ├── mod.rs │ └── tests.rs ├── ffi_ptr_ext.rs ├── gil.rs ├── impl_.rs ├── impl_ │ ├── callback.rs │ ├── concat.rs │ ├── coroutine.rs │ ├── exceptions.rs │ ├── extract_argument.rs │ ├── freelist.rs │ ├── frompyobject.rs │ ├── not_send.rs │ ├── panic.rs │ ├── pycell.rs │ ├── pyclass.rs │ ├── pyclass │ │ ├── assertions.rs │ │ ├── lazy_type_object.rs │ │ └── probes.rs │ ├── pyclass_init.rs │ ├── pyfunction.rs │ ├── pymethods.rs │ ├── pymodule.rs │ ├── trampoline.rs │ └── wrap.rs ├── inspect │ ├── mod.rs │ └── types.rs ├── instance.rs ├── internal.rs ├── internal │ └── get_slot.rs ├── internal_tricks.rs ├── lib.rs ├── macros.rs ├── marker.rs ├── marshal.rs ├── panic.rs ├── prelude.rs ├── py_result_ext.rs ├── pybacked.rs ├── pycell.rs ├── pycell │ └── impl_.rs ├── pyclass.rs ├── pyclass │ ├── create_type_object.rs │ └── gc.rs ├── pyclass_init.rs ├── sealed.rs ├── sync.rs ├── test_utils.rs ├── tests │ ├── common.rs │ ├── hygiene │ │ ├── misc.rs │ │ ├── mod.rs │ │ ├── pyclass.rs │ │ ├── pyfunction.rs │ │ ├── pymethods.rs │ │ └── pymodule.rs │ └── mod.rs ├── type_object.rs ├── types │ ├── any.rs │ ├── boolobject.rs │ ├── bytearray.rs │ ├── bytes.rs │ ├── capsule.rs │ ├── code.rs │ ├── complex.rs │ ├── datetime.rs │ ├── dict.rs │ ├── ellipsis.rs │ ├── float.rs │ ├── frame.rs │ ├── frozenset.rs │ ├── function.rs │ ├── genericalias.rs │ ├── iterator.rs │ ├── list.rs │ ├── mapping.rs │ ├── mappingproxy.rs │ ├── memoryview.rs │ ├── mod.rs │ ├── module.rs │ ├── none.rs │ ├── notimplemented.rs │ ├── num.rs │ ├── pysuper.rs │ ├── range.rs │ ├── sequence.rs │ ├── set.rs │ ├── slice.rs │ ├── string.rs │ ├── traceback.rs │ ├── tuple.rs │ ├── typeobject.rs │ └── weakref │ │ ├── anyref.rs │ │ ├── mod.rs │ │ ├── proxy.rs │ │ └── reference.rs └── version.rs └── tests ├── test_anyhow.rs ├── test_append_to_inittab.rs ├── test_arithmetics.rs ├── test_buffer.rs ├── test_buffer_protocol.rs ├── test_bytes.rs ├── test_class_attributes.rs ├── test_class_basics.rs ├── test_class_comparisons.rs ├── test_class_conversion.rs ├── test_class_formatting.rs ├── test_class_new.rs ├── test_compile_error.rs ├── test_coroutine.rs ├── test_datetime.rs ├── test_datetime_import.rs ├── test_declarative_module.rs ├── test_default_impls.rs ├── test_enum.rs ├── test_exceptions.rs ├── test_field_cfg.rs ├── test_frompy_intopy_roundtrip.rs ├── test_frompyobject.rs ├── test_gc.rs ├── test_getter_setter.rs ├── test_inheritance.rs ├── test_intopyobject.rs ├── test_macro_docs.rs ├── test_macros.rs ├── test_mapping.rs ├── test_methods.rs ├── test_module.rs ├── test_multiple_pymethods.rs ├── test_proto_methods.rs ├── test_pyerr_debug_unformattable.rs ├── test_pyfunction.rs ├── test_pyself.rs ├── test_sequence.rs ├── test_serde.rs ├── test_static_slots.rs ├── test_string.rs ├── test_super.rs ├── test_text_signature.rs ├── test_variable_arguments.rs ├── test_various.rs └── ui ├── abi3_dict.rs ├── abi3_dict.stderr ├── abi3_inheritance.rs ├── abi3_inheritance.stderr ├── abi3_nativetype_inheritance.rs ├── abi3_nativetype_inheritance.stderr ├── abi3_weakref.rs ├── abi3_weakref.stderr ├── ambiguous_associated_items.rs ├── duplicate_pymodule_submodule.rs ├── duplicate_pymodule_submodule.stderr ├── empty.rs ├── forbid_unsafe.rs ├── get_set_all.rs ├── get_set_all.stderr ├── immutable_type.rs ├── immutable_type.stderr ├── invalid_argument_attributes.rs ├── invalid_argument_attributes.stderr ├── invalid_async.rs ├── invalid_async.stderr ├── invalid_base_class.rs ├── invalid_base_class.stderr ├── invalid_cancel_handle.rs ├── invalid_cancel_handle.stderr ├── invalid_closure.rs ├── invalid_closure.stderr ├── invalid_frompy_derive.rs ├── invalid_frompy_derive.stderr ├── invalid_frozen_pyclass_borrow.rs ├── invalid_frozen_pyclass_borrow.stderr ├── invalid_intern_arg.rs ├── invalid_intern_arg.stderr ├── invalid_intopy_derive.rs ├── invalid_intopy_derive.stderr ├── invalid_intopy_with.rs ├── invalid_intopy_with.stderr ├── invalid_property_args.rs ├── invalid_property_args.stderr ├── invalid_proto_pymethods.rs ├── invalid_proto_pymethods.stderr ├── invalid_pycallargs.rs ├── invalid_pycallargs.stderr ├── invalid_pyclass_args.rs ├── invalid_pyclass_args.stderr ├── invalid_pyclass_enum.rs ├── invalid_pyclass_enum.stderr ├── invalid_pyclass_generic.rs ├── invalid_pyclass_generic.stderr ├── invalid_pyclass_item.rs ├── invalid_pyclass_item.stderr ├── invalid_pyfunction_definition.rs ├── invalid_pyfunction_definition.stderr ├── invalid_pyfunction_signatures.rs ├── invalid_pyfunction_signatures.stderr ├── invalid_pyfunction_warn.rs ├── invalid_pyfunction_warn.stderr ├── invalid_pyfunctions.rs ├── invalid_pyfunctions.stderr ├── invalid_pymethod_enum.rs ├── invalid_pymethod_enum.stderr ├── invalid_pymethod_names.rs ├── invalid_pymethod_names.stderr ├── invalid_pymethod_receiver.rs ├── invalid_pymethod_receiver.stderr ├── invalid_pymethods.rs ├── invalid_pymethods.stderr ├── invalid_pymethods_buffer.rs ├── invalid_pymethods_buffer.stderr ├── invalid_pymethods_duplicates.rs ├── invalid_pymethods_duplicates.stderr ├── invalid_pymethods_warn.rs ├── invalid_pymethods_warn.stderr ├── invalid_pymodule_args.rs ├── invalid_pymodule_args.stderr ├── invalid_pymodule_glob.rs ├── invalid_pymodule_glob.stderr ├── invalid_pymodule_in_root.rs ├── invalid_pymodule_in_root.stderr ├── invalid_pymodule_trait.rs ├── invalid_pymodule_trait.stderr ├── invalid_pymodule_two_pymodule_init.rs ├── invalid_pymodule_two_pymodule_init.stderr ├── invalid_result_conversion.rs ├── invalid_result_conversion.stderr ├── missing_intopy.rs ├── missing_intopy.stderr ├── not_send.rs ├── not_send.stderr ├── not_send2.rs ├── not_send2.stderr ├── pyclass_generic_enum.rs ├── pyclass_generic_enum.stderr ├── pyclass_probe.rs ├── pyclass_send.rs ├── pyclass_send.stderr ├── pymodule_missing_docs.rs ├── reject_generics.rs ├── reject_generics.stderr ├── static_ref.rs ├── static_ref.stderr ├── traverse.rs ├── traverse.stderr ├── wrong_aspyref_lifetimes.rs └── wrong_aspyref_lifetimes.stderr /.github/ISSUE_TEMPLATE/blank_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 📝 Blank Issue 3 | about: Create a blank issue. 4 | --- -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: ❓ Question 4 | url: https://github.com/PyO3/pyo3/discussions 5 | about: Ask and answer questions about PyO3 on Discussions 6 | - name: 🔧 Troubleshooting 7 | url: https://github.com/PyO3/pyo3/discussions 8 | about: For troubleshooting help, see the Discussions 9 | - name: 👋 Chat 10 | url: https://discord.gg/33kcChzH7f 11 | about: Engage with PyO3's users and developers on Discord 12 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | 13 | - package-ecosystem: "cargo" 14 | directory: "/pyo3-benches/" 15 | schedule: 16 | interval: "weekly" 17 | 18 | - package-ecosystem: "cargo" 19 | directory: "/pyo3-ffi-check/" 20 | schedule: 21 | interval: "weekly" 22 | 23 | - package-ecosystem: "github-actions" 24 | directory: "/" 25 | schedule: 26 | interval: "weekly" 27 | labels: 28 | # dependabot default labels 29 | - "dependencies" 30 | - "github-actions" 31 | # additional labels 32 | - "CI-skip-changelog" 33 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Thank you for contributing to PyO3! 2 | 3 | By submitting these contributions you agree for them to be dual-licensed under PyO3's [MIT OR Apache-2.0 license](https://github.com/PyO3/pyo3#license). 4 | 5 | Please consider adding the following to your pull request: 6 | - an entry for this PR in newsfragments - see [https://pyo3.rs/main/contributing.html#documenting-changes] 7 | - or start the PR title with `docs:` if this is a docs-only change to skip the check 8 | - or start the PR title with `ci:` if this is a ci-only change to skip the check 9 | - docs to all new functions and / or detail in the guide 10 | - tests for all new or changed functions 11 | 12 | PyO3's CI pipeline will check your pull request, thus make sure you have checked the `Contributing.md` guidelines. To run most of its tests 13 | locally, you can run ```nox```. See ```nox --list-sessions``` 14 | for a list of supported actions. 15 | -------------------------------------------------------------------------------- /.github/workflows/benches.yml: -------------------------------------------------------------------------------- 1 | name: benches 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | pull_request: 8 | # `workflow_dispatch` allows CodSpeed to trigger backtest 9 | # performance analysis in order to generate initial data. 10 | workflow_dispatch: 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}-benches 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | benchmarks: 18 | runs-on: ubuntu-24.04 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: actions/setup-python@v5 22 | with: 23 | python-version: '3.13' 24 | - uses: dtolnay/rust-toolchain@stable 25 | with: 26 | components: rust-src 27 | 28 | - uses: Swatinem/rust-cache@v2 29 | with: 30 | workspaces: | 31 | . 32 | pyo3-benches 33 | save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }} 34 | 35 | - uses: taiki-e/install-action@v2 36 | with: 37 | tool: cargo-codspeed 38 | 39 | - name: Install nox 40 | run: pip install nox 41 | 42 | - name: Run the benchmarks 43 | uses: CodSpeedHQ/action@v3 44 | with: 45 | run: nox -s codspeed 46 | token: ${{ secrets.CODSPEED_TOKEN }} 47 | -------------------------------------------------------------------------------- /.github/workflows/cache-cleanup.yml: -------------------------------------------------------------------------------- 1 | name: CI Cache Cleanup 2 | on: 3 | pull_request_target: 4 | types: 5 | - closed 6 | 7 | jobs: 8 | cleanup: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | actions: write 12 | steps: 13 | - name: Cleanup 14 | run: | 15 | gh extension install actions/gh-actions-cache 16 | 17 | echo "Fetching list of cache key" 18 | cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 ) 19 | 20 | ## Setting this to not fail the workflow while deleting cache keys. 21 | set +e 22 | echo "Deleting caches..." 23 | for cacheKey in $cacheKeysForPR 24 | do 25 | gh actions-cache delete -R $REPO -B $BRANCH --confirm -- $cacheKey 26 | done 27 | echo "Done" 28 | env: 29 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | REPO: ${{ github.repository }} 31 | BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge 32 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: changelog 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, labeled, unlabeled, edited] 6 | 7 | jobs: 8 | check: 9 | name: Check changelog entry 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-python@v5 14 | with: 15 | python-version: '3.13' 16 | - run: python -m pip install --upgrade pip && pip install nox 17 | - run: nox -s check-changelog 18 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release Rust Crate 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | environment: release 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - uses: actions/setup-python@v5 17 | with: 18 | python-version: "3.12" 19 | - run: python -m pip install --upgrade pip && pip install nox 20 | 21 | - uses: dtolnay/rust-toolchain@stable 22 | 23 | - name: Publish to crates.io 24 | run: nox -s publish 25 | env: 26 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | /doc 4 | /gh-pages 5 | build/ 6 | *.py[co] 7 | __pycache__/ 8 | .cache 9 | .pytest_cache/ 10 | dist/ 11 | .tox/ 12 | .mypy_cache/ 13 | .hypothesis/ 14 | .eggs/ 15 | venv* 16 | guide/book/ 17 | guide/src/LICENSE-APACHE 18 | guide/src/LICENSE-MIT 19 | *.so 20 | *.out 21 | *.egg-info 22 | extensions/stamps/ 23 | pip-wheel-metadata 24 | valgrind-python.supp 25 | *.pyd 26 | lcov.info 27 | coverage.json 28 | netlify_build/ 29 | .nox/ 30 | -------------------------------------------------------------------------------- /.towncrier.template.md: -------------------------------------------------------------------------------- 1 | {% for section_text, section in sections.items() %}{%- if section %}{{section_text}}{% endif -%} 2 | {% if section %} 3 | {% for category in ['packaging', 'added', 'changed', 'removed', 'fixed' ] if category in section %} 4 | ### {{ definitions[category]['name'] }} 5 | 6 | {% if definitions[category]['showcontent'] %} 7 | {% for text, pull_requests in section[category].items() %} 8 | - {{ text }} {{ pull_requests|join(', ') }} 9 | {% endfor %} 10 | {% else %} 11 | - {{ section[category]['']|join(', ') }} 12 | {% endif %} 13 | 14 | {% endfor %}{% else %}No significant changes.{% endif %}{% endfor %} 15 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: PyO3 3 | message: >- 4 | If you use this software as part of a publication and wish to cite 5 | it, please use the metadata from this file. 6 | type: software 7 | authors: 8 | - name: PyO3 Project and Contributors 9 | website: https://github.com/PyO3 10 | license: 11 | - Apache-2.0 12 | - MIT 13 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present PyO3 Project and Contributors. https://github.com/PyO3 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 | -------------------------------------------------------------------------------- /assets/script.py: -------------------------------------------------------------------------------- 1 | # Used in PyModule examples. 2 | 3 | 4 | class Blah: 5 | pass 6 | -------------------------------------------------------------------------------- /branding/favicon/pyo3_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/pyo3/8373f451fdf9caf6b6ed3cb57b576aac067bad07/branding/favicon/pyo3_16x16.png -------------------------------------------------------------------------------- /branding/favicon/pyo3_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/pyo3/8373f451fdf9caf6b6ed3cb57b576aac067bad07/branding/favicon/pyo3_32x32.png -------------------------------------------------------------------------------- /branding/pyo3logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/pyo3/8373f451fdf9caf6b6ed3cb57b576aac067bad07/branding/pyo3logo.png -------------------------------------------------------------------------------- /branding/pyotr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/pyo3/8373f451fdf9caf6b6ed3cb57b576aac067bad07/branding/pyotr.png -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | # Allow a tiny drop of overall project coverage in PR to reduce spurious failures. 9 | threshold: 0.25% 10 | 11 | ignore: 12 | - tests/ 13 | - pytests/ 14 | - src/test_hygiene/*.rs 15 | -------------------------------------------------------------------------------- /emscripten/.gitignore: -------------------------------------------------------------------------------- 1 | pybuilddir.txt 2 | -------------------------------------------------------------------------------- /emscripten/emscripten_patches/0001-Add-_gxx_personality_v0-stub-to-library.js.patch: -------------------------------------------------------------------------------- 1 | From 4b56f37c3dc9185a235a8314086c4d7a6239b2f8 Mon Sep 17 00:00:00 2001 2 | From: Hood Chatham 3 | Date: Sat, 4 Jun 2022 19:19:47 -0700 4 | Subject: [PATCH] Add _gxx_personality_v0 stub to library.js 5 | 6 | Mitigation for an incompatibility between Rust and Emscripten: 7 | https://github.com/rust-lang/rust/issues/85821 8 | https://github.com/emscripten-core/emscripten/issues/17128 9 | --- 10 | src/library.js | 2 ++ 11 | 1 file changed, 2 insertions(+) 12 | 13 | diff --git a/src/library.js b/src/library.js 14 | index e7bb4c38e..7d01744df 100644 15 | --- a/src/library.js 16 | +++ b/src/library.js 17 | @@ -403,6 +403,8 @@ mergeInto(LibraryManager.library, { 18 | abort('Assertion failed: ' + UTF8ToString(condition) + ', at: ' + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']); 19 | }, 20 | 21 | + __gxx_personality_v0: function() {}, 22 | + 23 | // ========================================================================== 24 | // time.h 25 | // ========================================================================== 26 | -- 27 | 2.25.1 28 | 29 | -------------------------------------------------------------------------------- /emscripten/env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Activate emsdk environment. emsdk_env.sh writes a lot to stderr so we suppress 4 | # the output. This also prevents it from complaining when emscripten isn't yet 5 | # installed. 6 | source "$EMSDKDIR/emsdk_env.sh" 2> /dev/null || true 7 | -------------------------------------------------------------------------------- /emscripten/pybuilddir.txt: -------------------------------------------------------------------------------- 1 | build/lib.linux-x86_64-3.11 -------------------------------------------------------------------------------- /emscripten/runner.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | import pathlib 3 | import sys 4 | import subprocess 5 | 6 | p = pathlib.Path(sys.argv[1]) 7 | 8 | sys.exit(subprocess.call(["node", p.name], cwd=p.parent)) 9 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-examples" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | rust-version = "1.63" 7 | 8 | [dev-dependencies] 9 | pyo3 = { path = "..", features = ["auto-initialize", "extension-module"] } 10 | 11 | [[example]] 12 | name = "decorator" 13 | path = "decorator/src/lib.rs" 14 | crate-type = ["cdylib"] 15 | doc-scrape-examples = true 16 | -------------------------------------------------------------------------------- /examples/decorator/.template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["{{authors}}"] 3 | name = "{{project-name}}" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "decorator" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } 13 | -------------------------------------------------------------------------------- /examples/decorator/.template/pre-script.rhai: -------------------------------------------------------------------------------- 1 | variable::set("PYO3_VERSION", "0.25.0"); 2 | file::rename(".template/Cargo.toml", "Cargo.toml"); 3 | file::rename(".template/pyproject.toml", "pyproject.toml"); 4 | file::delete(".template"); 5 | -------------------------------------------------------------------------------- /examples/decorator/.template/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "{{project-name}}" 7 | version = "0.1.0" 8 | 9 | [project.optional-dependencies] 10 | dev = ["pytest"] 11 | -------------------------------------------------------------------------------- /examples/decorator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decorator" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.63" 6 | 7 | [lib] 8 | name = "decorator" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { path = "../../", features = ["extension-module"] } 13 | 14 | [workspace] 15 | -------------------------------------------------------------------------------- /examples/decorator/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml Cargo.toml 2 | recursive-include src * 3 | -------------------------------------------------------------------------------- /examples/decorator/README.md: -------------------------------------------------------------------------------- 1 | # decorator 2 | 3 | A project showcasing the example from the [Emulating callable objects](https://pyo3.rs/latest/class/call.html) chapter of the guide. 4 | 5 | ## Building and Testing 6 | 7 | To build this package, first install `maturin`: 8 | 9 | ```shell 10 | pip install maturin 11 | ``` 12 | 13 | To build and test use `maturin develop`: 14 | 15 | ```shell 16 | pip install -r requirements-dev.txt 17 | maturin develop 18 | pytest 19 | ``` 20 | 21 | Alternatively, install nox and run the tests inside an isolated environment: 22 | 23 | ```shell 24 | nox 25 | ``` 26 | 27 | ## Copying this example 28 | 29 | Use [`cargo-generate`](https://crates.io/crates/cargo-generate): 30 | 31 | ```bash 32 | $ cargo install cargo-generate 33 | $ cargo generate --git https://github.com/PyO3/pyo3 examples/decorator 34 | ``` 35 | 36 | (`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.) 37 | -------------------------------------------------------------------------------- /examples/decorator/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | ignore = [".nox"] 3 | 4 | [hooks] 5 | pre = [".template/pre-script.rhai"] 6 | -------------------------------------------------------------------------------- /examples/decorator/noxfile.py: -------------------------------------------------------------------------------- 1 | import nox 2 | 3 | 4 | @nox.session 5 | def python(session: nox.Session): 6 | session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" 7 | session.install(".[dev]") 8 | session.run("pytest") 9 | -------------------------------------------------------------------------------- /examples/decorator/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "decorator" 7 | version = "0.1.0" 8 | classifiers = [ 9 | "License :: OSI Approved :: MIT License", 10 | "Development Status :: 3 - Alpha", 11 | "Intended Audience :: Developers", 12 | "Programming Language :: Python", 13 | "Programming Language :: Rust", 14 | "Operating System :: POSIX", 15 | "Operating System :: MacOS :: MacOS X", 16 | ] 17 | 18 | [project.optional-dependencies] 19 | dev = ["pytest"] 20 | -------------------------------------------------------------------------------- /examples/decorator/tests/example.py: -------------------------------------------------------------------------------- 1 | from decorator import Counter 2 | 3 | 4 | @Counter 5 | def say_hello(): 6 | print("hello") 7 | 8 | 9 | say_hello() 10 | say_hello() 11 | say_hello() 12 | say_hello() 13 | 14 | assert say_hello.count == 4 15 | -------------------------------------------------------------------------------- /examples/decorator/tests/test_.py: -------------------------------------------------------------------------------- 1 | from decorator import Counter 2 | 3 | 4 | def test_no_args(): 5 | @Counter 6 | def say_hello(): 7 | print("hello") 8 | 9 | say_hello() 10 | say_hello() 11 | say_hello() 12 | say_hello() 13 | 14 | assert say_hello.count == 4 15 | 16 | 17 | def test_arg(): 18 | @Counter 19 | def say_hello(name): 20 | print(f"hello {name}") 21 | 22 | say_hello("a") 23 | say_hello("b") 24 | say_hello("c") 25 | say_hello("d") 26 | 27 | assert say_hello.count == 4 28 | 29 | 30 | def test_default_arg(): 31 | @Counter 32 | def say_hello(name="default"): 33 | print(f"hello {name}") 34 | 35 | say_hello("a") 36 | say_hello() 37 | say_hello("c") 38 | say_hello() 39 | 40 | assert say_hello.count == 4 41 | 42 | 43 | # https://github.com/PyO3/pyo3/discussions/2598 44 | def test_discussion_2598(): 45 | @Counter 46 | def say_hello(): 47 | if say_hello.count < 2: 48 | print("hello from decorator") 49 | 50 | say_hello() 51 | say_hello() 52 | -------------------------------------------------------------------------------- /examples/getitem/.template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["{{authors}}"] 3 | name = "{{project-name}}" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "getitem" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } 13 | -------------------------------------------------------------------------------- /examples/getitem/.template/pre-script.rhai: -------------------------------------------------------------------------------- 1 | variable::set("PYO3_VERSION", "0.19.0"); 2 | file::rename(".template/Cargo.toml", "Cargo.toml"); 3 | file::rename(".template/pyproject.toml", "pyproject.toml"); 4 | file::delete(".template"); 5 | -------------------------------------------------------------------------------- /examples/getitem/.template/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "{{project-name}}" 7 | version = "0.1.0" 8 | 9 | [project.optional-dependencies] 10 | dev = ["pytest"] 11 | -------------------------------------------------------------------------------- /examples/getitem/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "getitem" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.63" 6 | 7 | [lib] 8 | name = "getitem" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { path = "../../", features = ["extension-module"] } 13 | 14 | [workspace] 15 | -------------------------------------------------------------------------------- /examples/getitem/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml Cargo.toml 2 | recursive-include src * 3 | -------------------------------------------------------------------------------- /examples/getitem/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | ignore = [".nox"] 3 | 4 | [hooks] 5 | pre = [".template/pre-script.rhai"] 6 | -------------------------------------------------------------------------------- /examples/getitem/noxfile.py: -------------------------------------------------------------------------------- 1 | import nox 2 | 3 | 4 | @nox.session 5 | def python(session: nox.Session): 6 | session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" 7 | session.install(".[dev]") 8 | session.run("pytest") 9 | -------------------------------------------------------------------------------- /examples/getitem/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "getitem" 7 | version = "0.1.0" 8 | classifiers = [ 9 | "License :: OSI Approved :: MIT License", 10 | "Development Status :: 3 - Alpha", 11 | "Intended Audience :: Developers", 12 | "Programming Language :: Python", 13 | "Programming Language :: Rust", 14 | "Operating System :: POSIX", 15 | "Operating System :: MacOS :: MacOS X", 16 | ] 17 | 18 | [project.optional-dependencies] 19 | dev = ["pytest"] 20 | -------------------------------------------------------------------------------- /examples/getitem/tests/test_getitem.py: -------------------------------------------------------------------------------- 1 | import getitem 2 | import pytest 3 | 4 | 5 | def test_simple(): 6 | container = getitem.ExampleContainer() 7 | assert container[3] == 3 8 | assert container[4] == 4 9 | assert container[-1] == -1 10 | assert container[5:3] == 2 11 | assert container[3:5] == 2 12 | # test setitem, but this just displays, no return to check 13 | container[3:5] = 2 14 | container[2] = 2 15 | # and note we will get an error on this one since we didn't 16 | # add strings 17 | with pytest.raises(TypeError): 18 | container["foo"] = 2 19 | -------------------------------------------------------------------------------- /examples/maturin-starter/.template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["{{authors}}"] 3 | name = "{{project-name}}" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "maturin_starter" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } 13 | -------------------------------------------------------------------------------- /examples/maturin-starter/.template/pre-script.rhai: -------------------------------------------------------------------------------- 1 | variable::set("PYO3_VERSION", "0.25.0"); 2 | file::rename(".template/Cargo.toml", "Cargo.toml"); 3 | file::rename(".template/pyproject.toml", "pyproject.toml"); 4 | file::delete(".template"); 5 | -------------------------------------------------------------------------------- /examples/maturin-starter/.template/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "{{project-name}}" 7 | version = "0.1.0" 8 | 9 | [project.optional-dependencies] 10 | dev = ["pytest"] 11 | -------------------------------------------------------------------------------- /examples/maturin-starter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "maturin-starter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.63" 6 | 7 | [lib] 8 | name = "maturin_starter" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { path = "../../", features = ["extension-module"] } 13 | 14 | [features] 15 | abi3 = ["pyo3/abi3-py37", "generate-import-lib"] 16 | generate-import-lib = ["pyo3/generate-import-lib"] 17 | 18 | [workspace] 19 | -------------------------------------------------------------------------------- /examples/maturin-starter/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml Cargo.toml 2 | recursive-include src * 3 | -------------------------------------------------------------------------------- /examples/maturin-starter/README.md: -------------------------------------------------------------------------------- 1 | # maturin-starter 2 | 3 | An example of a basic Python extension module built using PyO3 and [`maturin`](https://github.com/PyO3/maturin). 4 | 5 | ## Building and Testing 6 | 7 | To build this package, first install `maturin`: 8 | 9 | ```shell 10 | pip install maturin 11 | ``` 12 | 13 | To build and test use `maturin develop`: 14 | 15 | ```shell 16 | pip install -r requirements-dev.txt 17 | maturin develop && pytest 18 | ``` 19 | 20 | Alternatively, install nox and run the tests inside an isolated environment: 21 | 22 | ```shell 23 | nox 24 | ``` 25 | 26 | ## Copying this example 27 | 28 | Use [`cargo-generate`](https://crates.io/crates/cargo-generate): 29 | 30 | ```bash 31 | $ cargo install cargo-generate 32 | $ cargo generate --git https://github.com/PyO3/pyo3 examples/maturin-starter 33 | ``` 34 | 35 | (`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.) 36 | -------------------------------------------------------------------------------- /examples/maturin-starter/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | ignore = [".nox"] 3 | 4 | [hooks] 5 | pre = [".template/pre-script.rhai"] 6 | -------------------------------------------------------------------------------- /examples/maturin-starter/maturin_starter/__init__.py: -------------------------------------------------------------------------------- 1 | # import the contents of the Rust library into the Python extension 2 | from .maturin_starter import * 3 | from .maturin_starter import __all__ 4 | 5 | # optional: include the documentation from the Rust module 6 | from .maturin_starter import __doc__ # noqa: F401 7 | 8 | __all__ = __all__ + ["PythonClass"] 9 | 10 | 11 | class PythonClass: 12 | def __init__(self, value: int) -> None: 13 | self.value = value 14 | -------------------------------------------------------------------------------- /examples/maturin-starter/noxfile.py: -------------------------------------------------------------------------------- 1 | import nox 2 | 3 | 4 | @nox.session 5 | def python(session): 6 | session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" 7 | session.install(".[dev]") 8 | session.run("pytest") 9 | -------------------------------------------------------------------------------- /examples/maturin-starter/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "maturin-starter" 7 | version = "0.1.0" 8 | classifiers = [ 9 | "License :: OSI Approved :: MIT License", 10 | "Development Status :: 3 - Alpha", 11 | "Intended Audience :: Developers", 12 | "Programming Language :: Python", 13 | "Programming Language :: Rust", 14 | "Operating System :: POSIX", 15 | "Operating System :: MacOS :: MacOS X", 16 | ] 17 | 18 | [project.optional-dependencies] 19 | dev = ["pytest"] 20 | -------------------------------------------------------------------------------- /examples/maturin-starter/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::PyDict; 3 | use pyo3::wrap_pymodule; 4 | 5 | mod submodule; 6 | 7 | #[pyclass] 8 | struct ExampleClass { 9 | #[pyo3(get, set)] 10 | value: i32, 11 | } 12 | 13 | #[pymethods] 14 | impl ExampleClass { 15 | #[new] 16 | pub fn new(value: i32) -> Self { 17 | ExampleClass { value } 18 | } 19 | } 20 | 21 | /// An example module implemented in Rust using PyO3. 22 | #[pymodule] 23 | fn maturin_starter(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { 24 | m.add_class::()?; 25 | m.add_wrapped(wrap_pymodule!(submodule::submodule))?; 26 | 27 | // Inserting to sys.modules allows importing submodules nicely from Python 28 | // e.g. from maturin_starter.submodule import SubmoduleClass 29 | 30 | let sys = PyModule::import(py, "sys")?; 31 | let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.downcast_into()?; 32 | sys_modules.set_item("maturin_starter.submodule", m.getattr("submodule")?)?; 33 | 34 | Ok(()) 35 | } 36 | -------------------------------------------------------------------------------- /examples/maturin-starter/src/submodule.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | struct SubmoduleClass {} 5 | 6 | #[pymethods] 7 | impl SubmoduleClass { 8 | #[new] 9 | pub fn __new__() -> Self { 10 | SubmoduleClass {} 11 | } 12 | 13 | pub fn greeting(&self) -> &'static str { 14 | "Hello, world!" 15 | } 16 | } 17 | 18 | #[pymodule] 19 | pub fn submodule(m: &Bound<'_, PyModule>) -> PyResult<()> { 20 | m.add_class::()?; 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /examples/maturin-starter/tests/test_maturin_starter.py: -------------------------------------------------------------------------------- 1 | from maturin_starter import ExampleClass, PythonClass 2 | 3 | 4 | def test_python_class() -> None: 5 | py_class = PythonClass(value=10) 6 | assert py_class.value == 10 7 | 8 | 9 | def test_example_class() -> None: 10 | example = ExampleClass(value=11) 11 | assert example.value == 11 12 | 13 | 14 | def test_doc() -> None: 15 | import maturin_starter 16 | 17 | assert ( 18 | maturin_starter.__doc__ == "An example module implemented in Rust using PyO3." 19 | ) 20 | -------------------------------------------------------------------------------- /examples/maturin-starter/tests/test_submodule.py: -------------------------------------------------------------------------------- 1 | from maturin_starter.submodule import SubmoduleClass 2 | 3 | 4 | def test_submodule_class() -> None: 5 | submodule_class = SubmoduleClass() 6 | assert submodule_class.greeting() == "Hello, world!" 7 | -------------------------------------------------------------------------------- /examples/plugin/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/pyo3/8373f451fdf9caf6b6ed3cb57b576aac067bad07/examples/plugin/.DS_Store -------------------------------------------------------------------------------- /examples/plugin/.template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["{{authors}}"] 3 | name = "{{project-name}}" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | pyo3 = "{{PYO3_VERSION}}" 9 | plugin_api = { path = "plugin_api" } 10 | -------------------------------------------------------------------------------- /examples/plugin/.template/plugin_api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plugin_api" 3 | version = "0.1.0" 4 | description = "Plugin API example" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "plugin_api" 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [dependencies] 12 | #!!! Important - DO NOT ENABLE extension-module FEATURE HERE!!! 13 | pyo3 = "{{PYO3_VERSION}}" 14 | 15 | [features] 16 | # instead extension-module feature for pyo3 is enabled conditionally when we want to build a standalone extension module to test our plugins without "main" program 17 | extension-module = ["pyo3/extension-module"] 18 | -------------------------------------------------------------------------------- /examples/plugin/.template/pre-script.rhai: -------------------------------------------------------------------------------- 1 | variable::set("PYO3_VERSION", "0.25.0"); 2 | file::rename(".template/Cargo.toml", "Cargo.toml"); 3 | file::rename(".template/plugin_api/Cargo.toml", "plugin_api/Cargo.toml"); 4 | file::delete(".template"); 5 | -------------------------------------------------------------------------------- /examples/plugin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plugin_example" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.63" 6 | 7 | [dependencies] 8 | pyo3={path="../../", features=["macros"]} 9 | plugin_api={path="plugin_api"} 10 | 11 | 12 | [workspace] 13 | -------------------------------------------------------------------------------- /examples/plugin/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | ignore = [".nox"] 3 | 4 | [hooks] 5 | pre = [".template/pre-script.rhai"] 6 | -------------------------------------------------------------------------------- /examples/plugin/plugin_api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plugin_api" 3 | version = "0.1.0" 4 | description = "Plugin API example" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "plugin_api" 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [dependencies] 12 | #!!! Important - DO NOT ENABLE extension-module FEATURE HERE!!! 13 | pyo3 = { path = "../../../" } 14 | 15 | [features] 16 | # instead extension-module feature for pyo3 is enabled conditionally when we want to build a standalone extension module to test our plugins without "main" program 17 | extension-module = ["pyo3/extension-module"] 18 | -------------------------------------------------------------------------------- /examples/plugin/plugin_api/noxfile.py: -------------------------------------------------------------------------------- 1 | import nox 2 | 3 | 4 | @nox.session 5 | def python(session): 6 | session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" 7 | session.install(".[dev]") 8 | session.run("pytest") 9 | -------------------------------------------------------------------------------- /examples/plugin/plugin_api/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "plugin_api" 7 | requires-python = ">=3.7" 8 | classifiers = [ 9 | "Programming Language :: Rust", 10 | "Programming Language :: Python :: Implementation :: CPython", 11 | "Programming Language :: Python :: Implementation :: PyPy", 12 | ] 13 | 14 | [project.optional-dependencies] 15 | dev = ["pytest"] 16 | -------------------------------------------------------------------------------- /examples/plugin/plugin_api/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | ///this is our Gadget that python plugin code can create, and rust app can then access natively. 4 | #[pyclass] 5 | pub struct Gadget { 6 | #[pyo3(get, set)] 7 | pub prop: usize, 8 | //this field will only be accessible to rust code 9 | pub rustonly: Vec, 10 | } 11 | 12 | #[pymethods] 13 | impl Gadget { 14 | #[new] 15 | fn new() -> Self { 16 | Gadget { 17 | prop: 777, 18 | rustonly: Vec::new(), 19 | } 20 | } 21 | 22 | fn push(&mut self, v: usize) { 23 | self.rustonly.push(v); 24 | } 25 | } 26 | 27 | /// A Python module for plugin interface types 28 | #[pymodule] 29 | pub fn plugin_api(m: &Bound<'_, PyModule>) -> PyResult<()> { 30 | m.add_class::()?; 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /examples/plugin/plugin_api/tests/test_Gadget.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture 5 | def gadget(): 6 | import plugin_api as pa 7 | 8 | g = pa.Gadget() 9 | return g 10 | 11 | 12 | def test_creation(gadget): 13 | pass 14 | 15 | 16 | def test_property(gadget): 17 | gadget.prop = 42 18 | assert gadget.prop == 42 19 | 20 | 21 | def test_push(gadget): 22 | gadget.push(42) 23 | -------------------------------------------------------------------------------- /examples/plugin/plugin_api/tests/test_import.py: -------------------------------------------------------------------------------- 1 | def test_import(): 2 | import plugin_api # noqa: F401 3 | -------------------------------------------------------------------------------- /examples/plugin/python_plugin/gadget_init_plugin.py: -------------------------------------------------------------------------------- 1 | import plugin_api 2 | import rng 3 | 4 | 5 | def start(): 6 | """create an instance of Gadget, configure it and return to Rust""" 7 | g = plugin_api.Gadget() 8 | g.push(1) 9 | g.push(2) 10 | g.push(3) 11 | g.prop = rng.get_random_number() 12 | return g 13 | -------------------------------------------------------------------------------- /examples/plugin/python_plugin/rng.py: -------------------------------------------------------------------------------- 1 | def get_random_number(): 2 | # verified by the roll of a fair die to be random 3 | return 4 4 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/.template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["{{authors}}"] 3 | name = "{{project-name}}" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "setuptools_rust_starter" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } 13 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/.template/pre-script.rhai: -------------------------------------------------------------------------------- 1 | variable::set("PYO3_VERSION", "0.25.0"); 2 | file::rename(".template/Cargo.toml", "Cargo.toml"); 3 | file::rename(".template/setup.cfg", "setup.cfg"); 4 | file::delete(".template"); 5 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/.template/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = {{project-name}} 3 | version = 0.1.0 4 | packages = 5 | setuptools_rust_starter 6 | 7 | [options] 8 | include_package_data = True 9 | zip_safe = False 10 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "setuptools-rust-starter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.63" 6 | 7 | [lib] 8 | name = "setuptools_rust_starter" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { path = "../../", features = ["extension-module"] } 13 | 14 | [workspace] 15 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml Cargo.toml 2 | recursive-include src * 3 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/README.md: -------------------------------------------------------------------------------- 1 | # setuptools-rust-starter 2 | 3 | An example of a basic Python extension module built using PyO3 and [`setuptools_rust`](https://github.com/PyO3/setuptools-rust). 4 | 5 | ## Building and Testing 6 | 7 | To build this package, first install `setuptools_rust`: 8 | 9 | ```shell 10 | pip install setuptools_rust 11 | ``` 12 | 13 | To build and test use `python setup.py develop`: 14 | 15 | ```shell 16 | pip install -r requirements-dev.txt 17 | python setup.py develop && pytest 18 | ``` 19 | 20 | Alternatively, install nox and run the tests inside an isolated environment: 21 | 22 | ```shell 23 | nox 24 | ``` 25 | 26 | ## Copying this example 27 | 28 | Use [`cargo-generate`](https://crates.io/crates/cargo-generate): 29 | 30 | ```bash 31 | $ cargo install cargo-generate 32 | $ cargo generate --git https://github.com/PyO3/pyo3 examples/setuptools-rust-starter 33 | ``` 34 | 35 | (`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.) 36 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | ignore = [".nox"] 3 | 4 | [hooks] 5 | pre = [".template/pre-script.rhai"] 6 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/noxfile.py: -------------------------------------------------------------------------------- 1 | import nox 2 | import sys 3 | 4 | 5 | @nox.session 6 | def python(session: nox.Session): 7 | if sys.version_info < (3, 9): 8 | session.skip("Python 3.9 or later is required for setuptools-rust 1.11") 9 | session.env["SETUPTOOLS_RUST_CARGO_PROFILE"] = "dev" 10 | session.install(".[dev]") 11 | session.run("pytest") 12 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=62.4", "setuptools_rust>=1.11"] 3 | 4 | [project] 5 | name = "setuptools-rust-starter" 6 | version = "0.1.0" 7 | classifiers = [ 8 | "License :: OSI Approved :: MIT License", 9 | "Development Status :: 3 - Alpha", 10 | "Intended Audience :: Developers", 11 | "Programming Language :: Python", 12 | "Programming Language :: Rust", 13 | "Operating System :: POSIX", 14 | "Operating System :: MacOS :: MacOS X", 15 | ] 16 | 17 | [project.optional-dependencies] 18 | dev = ["pytest"] 19 | 20 | [tool.setuptools.packages.find] 21 | include = ["setuptools_rust_starter"] 22 | 23 | [[tool.setuptools-rust.ext-modules]] 24 | target = "setuptools_rust_starter._setuptools_rust_starter" 25 | path = "Cargo.toml" 26 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pytest>=3.5.0 2 | setuptools_rust~=1.0.0 3 | pip>=21.3 4 | wheel 5 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/setuptools_rust_starter/__init__.py: -------------------------------------------------------------------------------- 1 | # import the contents of the Rust library into the Python extension 2 | from ._setuptools_rust_starter import * 3 | from ._setuptools_rust_starter import __all__ 4 | 5 | # optional: include the documentation from the Rust module 6 | from ._setuptools_rust_starter import __doc__ # noqa: F401 7 | 8 | __all__ = __all__ + ["PythonClass"] 9 | 10 | 11 | class PythonClass: 12 | def __init__(self, value: int) -> None: 13 | self.value = value 14 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::PyDict; 3 | use pyo3::wrap_pymodule; 4 | 5 | mod submodule; 6 | 7 | #[pyclass] 8 | struct ExampleClass { 9 | #[pyo3(get, set)] 10 | value: i32, 11 | } 12 | 13 | #[pymethods] 14 | impl ExampleClass { 15 | #[new] 16 | pub fn new(value: i32) -> Self { 17 | ExampleClass { value } 18 | } 19 | } 20 | 21 | /// An example module implemented in Rust using PyO3. 22 | #[pymodule] 23 | fn _setuptools_rust_starter(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { 24 | m.add_class::()?; 25 | m.add_wrapped(wrap_pymodule!(submodule::submodule))?; 26 | 27 | // Inserting to sys.modules allows importing submodules nicely from Python 28 | // e.g. from setuptools_rust_starter.submodule import SubmoduleClass 29 | 30 | let sys = PyModule::import(py, "sys")?; 31 | let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.downcast_into()?; 32 | sys_modules.set_item("setuptools_rust_starter.submodule", m.getattr("submodule")?)?; 33 | 34 | Ok(()) 35 | } 36 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/src/submodule.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | struct SubmoduleClass {} 5 | 6 | #[pymethods] 7 | impl SubmoduleClass { 8 | #[new] 9 | pub fn __new__() -> Self { 10 | SubmoduleClass {} 11 | } 12 | 13 | pub fn greeting(&self) -> &'static str { 14 | "Hello, world!" 15 | } 16 | } 17 | 18 | #[pymodule] 19 | pub fn submodule(m: &Bound<'_, PyModule>) -> PyResult<()> { 20 | m.add_class::()?; 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/tests/test_setuptools_rust_starter.py: -------------------------------------------------------------------------------- 1 | from setuptools_rust_starter import ExampleClass, PythonClass 2 | 3 | 4 | def test_python_class() -> None: 5 | py_class = PythonClass(value=10) 6 | assert py_class.value == 10 7 | 8 | 9 | def test_example_class() -> None: 10 | example = ExampleClass(value=11) 11 | assert example.value == 11 12 | 13 | 14 | def test_doc() -> None: 15 | import setuptools_rust_starter 16 | 17 | assert ( 18 | setuptools_rust_starter.__doc__ 19 | == "An example module implemented in Rust using PyO3." 20 | ) 21 | -------------------------------------------------------------------------------- /examples/setuptools-rust-starter/tests/test_submodule.py: -------------------------------------------------------------------------------- 1 | from setuptools_rust_starter.submodule import SubmoduleClass 2 | 3 | 4 | def test_submodule_class() -> None: 5 | submodule_class = SubmoduleClass() 6 | assert submodule_class.greeting() == "Hello, world!" 7 | -------------------------------------------------------------------------------- /examples/word-count/.template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["{{authors}}"] 3 | name = "{{project-name}}" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "word_count" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } 13 | rayon = "1.0.2" 14 | -------------------------------------------------------------------------------- /examples/word-count/.template/pre-script.rhai: -------------------------------------------------------------------------------- 1 | variable::set("PYO3_VERSION", "0.25.0"); 2 | file::rename(".template/Cargo.toml", "Cargo.toml"); 3 | file::rename(".template/pyproject.toml", "pyproject.toml"); 4 | file::delete(".template"); 5 | -------------------------------------------------------------------------------- /examples/word-count/.template/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "{{project-name}}" 7 | version = "0.1.0" 8 | 9 | [project.optional-dependencies] 10 | dev = ["pytest"] 11 | 12 | [tool.pytest.ini_options] 13 | addopts = "--benchmark-disable" 14 | -------------------------------------------------------------------------------- /examples/word-count/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "word-count" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.63" 6 | 7 | [lib] 8 | name = "word_count" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3 = { path = "../..", features = ["extension-module"] } 13 | rayon = "1.0.2" 14 | 15 | [workspace] 16 | -------------------------------------------------------------------------------- /examples/word-count/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml Cargo.toml 2 | recursive-include src * 3 | -------------------------------------------------------------------------------- /examples/word-count/README.md: -------------------------------------------------------------------------------- 1 | # word-count 2 | 3 | Demonstrates searching for a file in plain python, with rust singlethreaded and with rust multithreaded. 4 | 5 | ## Build 6 | 7 | ```shell 8 | pip install . 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```python 14 | from word_count import search_py, search, search_sequential 15 | 16 | search_py("foo bar", "foo") 17 | search("foo bar", "foo") 18 | search_sequential("foo bar", "foo") 19 | ``` 20 | 21 | ## Testing 22 | 23 | To test install nox globally and run 24 | 25 | ```shell 26 | nox 27 | ``` 28 | 29 | ## Benchmark 30 | 31 | To test install nox globally and run 32 | 33 | ```shell 34 | nox -s bench 35 | ``` 36 | 37 | ## Copying this example 38 | 39 | Use [`cargo-generate`](https://crates.io/crates/cargo-generate): 40 | 41 | ```bash 42 | $ cargo install cargo-generate 43 | $ cargo generate --git https://github.com/PyO3/pyo3 examples/word-count 44 | ``` 45 | 46 | (`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.) 47 | -------------------------------------------------------------------------------- /examples/word-count/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | ignore = [".nox"] 3 | 4 | [hooks] 5 | pre = [".template/pre-script.rhai"] 6 | -------------------------------------------------------------------------------- /examples/word-count/noxfile.py: -------------------------------------------------------------------------------- 1 | import nox 2 | 3 | nox.options.sessions = ["test"] 4 | 5 | 6 | @nox.session 7 | def test(session: nox.Session): 8 | session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" 9 | session.install(".[dev]") 10 | session.run("pytest") 11 | 12 | 13 | @nox.session 14 | def bench(session: nox.Session): 15 | session.install(".[dev]") 16 | session.run("pytest", "--benchmark-enable") 17 | -------------------------------------------------------------------------------- /examples/word-count/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "word-count" 7 | version = "0.1.0" 8 | classifiers = [ 9 | "License :: OSI Approved :: MIT License", 10 | "Development Status :: 3 - Alpha", 11 | "Intended Audience :: Developers", 12 | "Programming Language :: Python", 13 | "Programming Language :: Rust", 14 | "Operating System :: POSIX", 15 | "Operating System :: MacOS :: MacOS X", 16 | ] 17 | 18 | [project.optional-dependencies] 19 | dev = ["pytest", "pytest-benchmark"] 20 | 21 | [tool.pytest.ini_options] 22 | addopts = "--benchmark-disable" 23 | -------------------------------------------------------------------------------- /examples/word-count/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use rayon::prelude::*; 3 | 4 | /// Searches for the word, parallelized by rayon 5 | #[pyfunction] 6 | fn search(contents: &str, needle: &str) -> usize { 7 | contents 8 | .par_lines() 9 | .map(|line| count_line(line, needle)) 10 | .sum() 11 | } 12 | 13 | /// Searches for a word in a classic sequential fashion 14 | #[pyfunction] 15 | fn search_sequential(contents: &str, needle: &str) -> usize { 16 | contents.lines().map(|line| count_line(line, needle)).sum() 17 | } 18 | 19 | #[pyfunction] 20 | fn search_sequential_allow_threads(py: Python<'_>, contents: &str, needle: &str) -> usize { 21 | py.allow_threads(|| search_sequential(contents, needle)) 22 | } 23 | 24 | /// Count the occurrences of needle in line, case insensitive 25 | fn count_line(line: &str, needle: &str) -> usize { 26 | let mut total = 0; 27 | for word in line.split(' ') { 28 | if word == needle { 29 | total += 1; 30 | } 31 | } 32 | total 33 | } 34 | 35 | #[pymodule] 36 | fn word_count(m: &Bound<'_, PyModule>) -> PyResult<()> { 37 | m.add_function(wrap_pyfunction!(search, m)?)?; 38 | m.add_function(wrap_pyfunction!(search_sequential, m)?)?; 39 | m.add_function(wrap_pyfunction!(search_sequential_allow_threads, m)?)?; 40 | 41 | Ok(()) 42 | } 43 | -------------------------------------------------------------------------------- /examples/word-count/word_count/__init__.py: -------------------------------------------------------------------------------- 1 | from .word_count import search, search_sequential, search_sequential_allow_threads 2 | 3 | __all__ = [ 4 | "search_py", 5 | "search", 6 | "search_sequential", 7 | "search_sequential_allow_threads", 8 | ] 9 | 10 | 11 | def search_py(contents: str, needle: str) -> int: 12 | total = 0 13 | for line in contents.splitlines(): 14 | for word in line.split(" "): 15 | if word == needle: 16 | total += 1 17 | return total 18 | -------------------------------------------------------------------------------- /guide/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "PyO3 user guide" 3 | description = "PyO3 user guide" 4 | author = "PyO3 Project and Contributors" 5 | 6 | [preprocessor.pyo3_version] 7 | command = "python3 guide/pyo3_version.py" 8 | 9 | [preprocessor.tabs] 10 | 11 | [output.html] 12 | git-repository-url = "https://github.com/PyO3/pyo3/tree/main/guide" 13 | edit-url-template = "https://github.com/PyO3/pyo3/edit/main/guide/{path}" 14 | playground.runnable = false 15 | additional-css = ["theme/tabs.css"] 16 | additional-js = ["theme/tabs.js"] 17 | -------------------------------------------------------------------------------- /guide/src/advanced.md: -------------------------------------------------------------------------------- 1 | # Advanced topics 2 | 3 | ## FFI 4 | 5 | PyO3 exposes much of Python's C API through the `ffi` module. 6 | 7 | The C API is naturally unsafe and requires you to manage reference counts, errors and specific invariants yourself. Please refer to the [C API Reference Manual](https://docs.python.org/3/c-api/) and [The Rustonomicon](https://doc.rust-lang.org/nightly/nomicon/ffi.html) before using any function from that API. 8 | -------------------------------------------------------------------------------- /guide/src/changelog.md: -------------------------------------------------------------------------------- 1 | {{#include ../../CHANGELOG.md}} 2 | -------------------------------------------------------------------------------- /guide/src/contributing.md: -------------------------------------------------------------------------------- 1 | {{#include ../../Contributing.md}} 2 | -------------------------------------------------------------------------------- /guide/src/conversions.md: -------------------------------------------------------------------------------- 1 | # Type conversions 2 | 3 | In this portion of the guide we'll talk about the mapping of Python types to Rust types offered by PyO3, as well as the traits available to perform conversions between them. 4 | 5 | See also the conversion [tables](conversions/tables.md) and [traits](conversions/traits.md). 6 | -------------------------------------------------------------------------------- /guide/src/ecosystem.md: -------------------------------------------------------------------------------- 1 | # The PyO3 ecosystem 2 | 3 | This portion of the guide is dedicated to crates which are external to the main PyO3 project and provide additional functionality you might find useful. 4 | 5 | Because these projects evolve independently of the PyO3 repository the content of these articles may fall out of date over time; please file issues on the PyO3 GitHub to alert maintainers when this is the case. 6 | -------------------------------------------------------------------------------- /guide/src/ecosystem/async-await.md: -------------------------------------------------------------------------------- 1 | # Using `async` and `await` 2 | 3 | *`async`/`await` support is currently being integrated in PyO3. See the [dedicated documentation](../async-await.md)* 4 | 5 | If you are working with a Python library that makes use of async functions or wish to provide 6 | Python bindings for an async Rust library, [`pyo3-async-runtimes`](https://github.com/PyO3/pyo3-async-runtimes) 7 | likely has the tools you need. It provides conversions between async functions in both Python and 8 | Rust and was designed with first-class support for popular Rust runtimes such as 9 | [`tokio`](https://tokio.rs/) and [`async-std`](https://async.rs/). In addition, all async Python 10 | code runs on the default `asyncio` event loop, so `pyo3-async-runtimes` should work just fine with existing 11 | Python libraries. 12 | 13 | ## Additional Information 14 | - Managing event loop references can be tricky with `pyo3-async-runtimes`. See [Event Loop References](https://docs.rs/pyo3-async-runtimes/#event-loop-references-and-contextvars) in the API docs to get a better intuition for how event loop references are managed in this library. 15 | - Testing `pyo3-async-runtimes` libraries and applications requires a custom test harness since Python requires control over the main thread. You can find a testing guide in the [API docs for the `testing` module](https://docs.rs/pyo3-async-runtimes/latest/pyo3_async_runtimes/testing) 16 | -------------------------------------------------------------------------------- /guide/src/function-calls.md: -------------------------------------------------------------------------------- 1 | # Calling Python functions 2 | -------------------------------------------------------------------------------- /guide/src/index.md: -------------------------------------------------------------------------------- 1 | # The PyO3 user guide 2 | 3 | Welcome to the PyO3 user guide! This book is a companion to [PyO3's API docs](https://docs.rs/pyo3). It contains examples and documentation to explain all of PyO3's use cases in detail. 4 | 5 | The rough order of material in this user guide is as follows: 6 | 1. Getting started 7 | 2. Wrapping Rust code for use from Python 8 | 3. How to use Python code from Rust 9 | 4. Remaining topics which go into advanced concepts in detail 10 | 11 | Please choose from the chapters on the left to jump to individual topics, or continue below to start with PyO3's README. 12 | 13 |
14 | 15 | {{#include ../../README.md}} 16 | -------------------------------------------------------------------------------- /guide/src/rust-from-python.md: -------------------------------------------------------------------------------- 1 | # Using Rust from Python 2 | 3 | This chapter of the guide is dedicated to explaining how to wrap Rust code into Python objects. 4 | 5 | PyO3 uses Rust's "procedural macros" to provide a powerful yet simple API to denote what Rust code should map into Python objects. 6 | 7 | PyO3 can create three types of Python objects: 8 | 9 | - Python modules, via the `#[pymodule]` macro 10 | - Python functions, via the `#[pyfunction]` macro 11 | - Python classes, via the `#[pyclass]` macro (plus `#[pymethods]` to define methods for those classes) 12 | 13 | The following subchapters go through each of these in turn. 14 | -------------------------------------------------------------------------------- /guide/theme/tabs.css: -------------------------------------------------------------------------------- 1 | .mdbook-tabs { 2 | display: flex; 3 | } 4 | 5 | .mdbook-tab { 6 | background-color: var(--table-alternate-bg); 7 | padding: 0.5rem 1rem; 8 | cursor: pointer; 9 | border: none; 10 | font-size: 1.6rem; 11 | line-height: 1.45em; 12 | } 13 | 14 | .mdbook-tab.active { 15 | background-color: var(--table-header-bg); 16 | font-weight: bold; 17 | } 18 | 19 | .mdbook-tab-content { 20 | padding: 1rem 0rem; 21 | } 22 | 23 | .mdbook-tab-content table { 24 | margin: unset; 25 | } 26 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "netlify_build/" 3 | command = ".netlify/build.sh" 4 | 5 | [build.environment] 6 | PYTHON_VERSION = "3.8" 7 | -------------------------------------------------------------------------------- /newsfragments/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/pyo3/8373f451fdf9caf6b6ed3cb57b576aac067bad07/newsfragments/.gitignore -------------------------------------------------------------------------------- /newsfragments/4364.added.md: -------------------------------------------------------------------------------- 1 | Added `#[pyo3(warn(message = "...", category = ...))]` attribute for automatic warnings generation for `#[pyfunction]` and `#[pymethods]`. -------------------------------------------------------------------------------- /newsfragments/5121.added.md: -------------------------------------------------------------------------------- 1 | Add `PyBytes_AS_STRING` 2 | -------------------------------------------------------------------------------- /newsfragments/5121.changed.md: -------------------------------------------------------------------------------- 1 | Enable vectorcall methods on GraalPy 2 | Call Py_Is function on GraalPy 3 | -------------------------------------------------------------------------------- /newsfragments/5144.changed.md: -------------------------------------------------------------------------------- 1 | When building with `abi3` for a Python version newer than pyo3 supports, we now automatically fall back to an abi3 build for the latest version. 2 | -------------------------------------------------------------------------------- /newsfragments/5145.packaging.md: -------------------------------------------------------------------------------- 1 | add support for Windows on ARM64. -------------------------------------------------------------------------------- /newsfragments/5146.changed.md: -------------------------------------------------------------------------------- 1 | Allow `is_instance_of` for types that only implement `PyTypeCheck` 2 | -------------------------------------------------------------------------------- /newsfragments/5150.added.md: -------------------------------------------------------------------------------- 1 | - Add support for module associated consts introspection. -------------------------------------------------------------------------------- /newsfragments/5152.packaging.md: -------------------------------------------------------------------------------- 1 | Bump hashbrown to 0.15.0 -------------------------------------------------------------------------------- /newsfragments/5154.added.md: -------------------------------------------------------------------------------- 1 | * added missing ffi functions for `PyFrameObject` but without 2 | including unstable API from python 3.13 3 | * moved ffi functions to correct files to mach CPython as of 3.13 -------------------------------------------------------------------------------- /newsfragments/5154.removed.md: -------------------------------------------------------------------------------- 1 | * Removed access to internals of PyFrameObject -------------------------------------------------------------------------------- /newsfragments/5156.fixed.md: -------------------------------------------------------------------------------- 1 | Catching `async` declaration when not using `experimental-async` -------------------------------------------------------------------------------- /newsfragments/5161.fixed.md: -------------------------------------------------------------------------------- 1 | Fixed FromPyObject impl for `uuid::Uuid` on big-endian architectures 2 | -------------------------------------------------------------------------------- /pyo3-benches/benches/bench_decimal.rs: -------------------------------------------------------------------------------- 1 | use std::hint::black_box; 2 | 3 | use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criterion}; 4 | use rust_decimal::Decimal; 5 | 6 | use pyo3::prelude::*; 7 | use pyo3::types::PyDict; 8 | 9 | fn decimal_via_extract(b: &mut Bencher<'_>) { 10 | Python::with_gil(|py| { 11 | let locals = PyDict::new(py); 12 | py.run( 13 | cr#" 14 | import decimal 15 | py_dec = decimal.Decimal("0.0") 16 | "#, 17 | None, 18 | Some(&locals), 19 | ) 20 | .unwrap(); 21 | let py_dec = locals.get_item("py_dec").unwrap().unwrap(); 22 | 23 | b.iter(|| black_box(&py_dec).extract::().unwrap()); 24 | }) 25 | } 26 | 27 | fn criterion_benchmark(c: &mut Criterion) { 28 | c.bench_function("decimal_via_extract", decimal_via_extract); 29 | } 30 | 31 | criterion_group!(benches, criterion_benchmark); 32 | criterion_main!(benches); 33 | -------------------------------------------------------------------------------- /pyo3-benches/benches/bench_err.rs: -------------------------------------------------------------------------------- 1 | use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criterion}; 2 | 3 | use pyo3::{exceptions::PyValueError, prelude::*}; 4 | 5 | fn err_new_restore_and_fetch(b: &mut Bencher<'_>) { 6 | Python::with_gil(|py| { 7 | b.iter(|| { 8 | PyValueError::new_err("some exception message").restore(py); 9 | PyErr::fetch(py) 10 | }) 11 | }) 12 | } 13 | 14 | fn err_new_without_gil(b: &mut Bencher<'_>) { 15 | b.iter(|| PyValueError::new_err("some exception message")) 16 | } 17 | 18 | fn criterion_benchmark(c: &mut Criterion) { 19 | c.bench_function("err_new_restore_and_fetch", err_new_restore_and_fetch); 20 | c.bench_function("err_new_without_gil", err_new_without_gil); 21 | } 22 | 23 | criterion_group!(benches, criterion_benchmark); 24 | criterion_main!(benches); 25 | -------------------------------------------------------------------------------- /pyo3-benches/benches/bench_gil.rs: -------------------------------------------------------------------------------- 1 | use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criterion}; 2 | 3 | use pyo3::prelude::*; 4 | 5 | fn bench_clean_acquire_gil(b: &mut Bencher<'_>) { 6 | // Acquiring first GIL will also create a "clean" GILPool, so this measures the Python overhead. 7 | b.iter(|| Python::with_gil(|_| {})); 8 | } 9 | 10 | fn bench_dirty_acquire_gil(b: &mut Bencher<'_>) { 11 | let obj = Python::with_gil(|py| py.None()); 12 | // Drop the returned clone of the object so that the reference pool has work to do. 13 | b.iter(|| Python::with_gil(|py| obj.clone_ref(py))); 14 | } 15 | 16 | fn criterion_benchmark(c: &mut Criterion) { 17 | c.bench_function("clean_acquire_gil", bench_clean_acquire_gil); 18 | c.bench_function("dirty_acquire_gil", bench_dirty_acquire_gil); 19 | } 20 | 21 | criterion_group!(benches, criterion_benchmark); 22 | criterion_main!(benches); 23 | -------------------------------------------------------------------------------- /pyo3-benches/benches/bench_intern.rs: -------------------------------------------------------------------------------- 1 | use std::hint::black_box; 2 | 3 | use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criterion}; 4 | 5 | use pyo3::prelude::*; 6 | 7 | use pyo3::intern; 8 | 9 | fn getattr_direct(b: &mut Bencher<'_>) { 10 | Python::with_gil(|py| { 11 | let sys = &py.import("sys").unwrap(); 12 | 13 | b.iter(|| black_box(sys).getattr("version").unwrap()); 14 | }); 15 | } 16 | 17 | fn getattr_intern(b: &mut Bencher<'_>) { 18 | Python::with_gil(|py| { 19 | let sys = &py.import("sys").unwrap(); 20 | 21 | b.iter(|| black_box(sys).getattr(intern!(py, "version")).unwrap()); 22 | }); 23 | } 24 | 25 | fn criterion_benchmark(c: &mut Criterion) { 26 | c.bench_function("getattr_direct", getattr_direct); 27 | c.bench_function("getattr_intern", getattr_intern); 28 | } 29 | 30 | criterion_group!(benches, criterion_benchmark); 31 | criterion_main!(benches); 32 | -------------------------------------------------------------------------------- /pyo3-benches/benches/bench_pyclass.rs: -------------------------------------------------------------------------------- 1 | use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criterion}; 2 | use pyo3::{impl_::pyclass::LazyTypeObject, prelude::*}; 3 | 4 | /// This is a feature-rich class instance used to benchmark various parts of the pyclass lifecycle. 5 | #[pyclass] 6 | struct MyClass { 7 | #[pyo3(get, set)] 8 | elements: Vec, 9 | } 10 | 11 | #[pymethods] 12 | impl MyClass { 13 | #[new] 14 | fn new(elements: Vec) -> Self { 15 | Self { elements } 16 | } 17 | 18 | fn __call__(&mut self, new_element: i32) -> usize { 19 | self.elements.push(new_element); 20 | self.elements.len() 21 | } 22 | 23 | /// A basic __str__ implementation. 24 | fn __str__(&self) -> &'static str { 25 | "MyClass" 26 | } 27 | } 28 | 29 | pub fn first_time_init(b: &mut Bencher<'_>) { 30 | Python::with_gil(|py| { 31 | b.iter(|| { 32 | // This is using an undocumented internal PyO3 API to measure pyclass performance; please 33 | // don't use this in your own code! 34 | let ty = LazyTypeObject::::new(); 35 | ty.get_or_init(py); 36 | }); 37 | }); 38 | } 39 | 40 | fn criterion_benchmark(c: &mut Criterion) { 41 | c.bench_function("first_time_init", first_time_init); 42 | } 43 | 44 | criterion_group!(benches, criterion_benchmark); 45 | 46 | criterion_main!(benches); 47 | -------------------------------------------------------------------------------- /pyo3-benches/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pyo3_build_config::use_pyo3_cfgs(); 3 | } 4 | -------------------------------------------------------------------------------- /pyo3-build-config/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /pyo3-build-config/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /pyo3-ffi-check/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-ffi-check" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | pyo3-ffi-check-macro = { path = "./macro" } 9 | memoffset = "0.9.0" 10 | 11 | [dependencies.pyo3-ffi] 12 | path = "../pyo3-ffi" 13 | features = ["extension-module"] # A lazy way of skipping linking in most cases (as we don't use any runtime symbols) 14 | 15 | [build-dependencies] 16 | bindgen = "0.69.4" 17 | pyo3-build-config = { path = "../pyo3-build-config" } 18 | 19 | [workspace] 20 | members = [ 21 | "macro" 22 | ] 23 | -------------------------------------------------------------------------------- /pyo3-ffi-check/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-ffi-check 2 | 3 | This is a simple program which compares ffi definitions from `pyo3-ffi` against those produced by `bindgen`. 4 | 5 | If any differ in size, these are printed to stdout and a the process will exit nonzero. 6 | 7 | The main purpose of this program is to be run as part of PyO3's continuous integration pipeline to catch possible errors in PyO3's ffi definitions. 8 | -------------------------------------------------------------------------------- /pyo3-ffi-check/macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-ffi-check-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | glob = "0.3" 12 | quote = "1" 13 | proc-macro2 = "1.0.60" 14 | scraper = "0.17" 15 | pyo3-build-config = { path = "../../pyo3-build-config" } 16 | -------------------------------------------------------------------------------- /pyo3-ffi-check/wrapper.h: -------------------------------------------------------------------------------- 1 | #include "Python.h" 2 | #include "datetime.h" 3 | #include "frameobject.h" 4 | #include "structmember.h" 5 | -------------------------------------------------------------------------------- /pyo3-ffi/ACKNOWLEDGEMENTS: -------------------------------------------------------------------------------- 1 | This is a Rust reimplementation of the CPython public header files as necessary 2 | for binary compatibility, with additional metadata to support PyPy. 3 | 4 | For original implementations please see: 5 | - https://github.com/python/cpython 6 | - https://github.com/pypy/pypy 7 | -------------------------------------------------------------------------------- /pyo3-ffi/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /pyo3-ffi/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /pyo3-ffi/examples/README.md: -------------------------------------------------------------------------------- 1 | # `pyo3-ffi` Examples 2 | 3 | These example crates are a collection of toy extension modules built with 4 | `pyo3-ffi`. They are all tested using `nox` in PyO3's CI. 5 | 6 | Below is a brief description of each of these: 7 | 8 | | Example | Description | 9 | | `word-count` | Illustrates how to use pyo3-ffi to write a static rust extension | 10 | | `sequential` | Illustrates how to use pyo3-ffi to write subinterpreter-safe modules using multi-phase module initialization | 11 | 12 | ## Creating new projects from these examples 13 | 14 | To copy an example, use [`cargo-generate`](https://crates.io/crates/cargo-generate). Follow the commands below, replacing `` with the example to start from: 15 | 16 | ```bash 17 | $ cargo install cargo-generate 18 | $ cargo generate --git https://github.com/PyO3/pyo3 examples/ 19 | ``` 20 | 21 | (`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.) 22 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/.template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["{{authors}}"] 3 | name = "{{project-name}}" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "sequential" 9 | crate-type = ["cdylib", "lib"] 10 | 11 | [dependencies] 12 | pyo3-ffi = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } 13 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/.template/pre-script.rhai: -------------------------------------------------------------------------------- 1 | variable::set("PYO3_VERSION", "0.19.2"); 2 | file::rename(".template/Cargo.toml", "Cargo.toml"); 3 | file::rename(".template/pyproject.toml", "pyproject.toml"); 4 | file::delete(".template"); 5 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/.template/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "{{project-name}}" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sequential" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.63" 6 | 7 | [lib] 8 | name = "sequential" 9 | crate-type = ["cdylib", "lib"] 10 | 11 | [dependencies] 12 | pyo3-ffi = { path = "../../", features = ["extension-module"] } 13 | 14 | [build-dependencies] 15 | pyo3-build-config = { path = "../../../pyo3-build-config" } 16 | 17 | [workspace] 18 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml Cargo.toml 2 | recursive-include src * 3 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/README.md: -------------------------------------------------------------------------------- 1 | # sequential 2 | 3 | A project built using only `pyo3_ffi`, without any of PyO3's safe api. It can be executed by subinterpreters that have their own GIL. 4 | 5 | ## Building and Testing 6 | 7 | To build this package, first install `maturin`: 8 | 9 | ```shell 10 | pip install maturin 11 | ``` 12 | 13 | To build and test use `maturin develop`: 14 | 15 | ```shell 16 | pip install -r requirements-dev.txt 17 | maturin develop 18 | pytest 19 | ``` 20 | 21 | Alternatively, install nox and run the tests inside an isolated environment: 22 | 23 | ```shell 24 | nox 25 | ``` 26 | 27 | ## Copying this example 28 | 29 | Use [`cargo-generate`](https://crates.io/crates/cargo-generate): 30 | 31 | ```bash 32 | $ cargo install cargo-generate 33 | $ cargo generate --git https://github.com/PyO3/pyo3 examples/sequential 34 | ``` 35 | 36 | (`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.) 37 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pyo3_build_config::use_pyo3_cfgs(); 3 | } 4 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | ignore = [".nox"] 3 | 4 | [hooks] 5 | pre = [".template/pre-script.rhai"] 6 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/noxfile.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import nox 3 | 4 | 5 | @nox.session 6 | def python(session): 7 | if sys.version_info < (3, 12): 8 | session.skip("Python 3.12+ is required") 9 | session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" 10 | session.install(".[dev]") 11 | session.run("pytest") 12 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "sequential" 7 | version = "0.1.0" 8 | classifiers = [ 9 | "License :: OSI Approved :: MIT License", 10 | "Development Status :: 3 - Alpha", 11 | "Intended Audience :: Developers", 12 | "Programming Language :: Python", 13 | "Programming Language :: Rust", 14 | "Operating System :: POSIX", 15 | "Operating System :: MacOS :: MacOS X", 16 | ] 17 | requires-python = ">=3.12" 18 | 19 | [project.optional-dependencies] 20 | dev = ["pytest"] 21 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use pyo3_ffi::*; 4 | 5 | mod id; 6 | mod module; 7 | use crate::module::MODULE_DEF; 8 | 9 | // The module initialization function, which must be named `PyInit_`. 10 | #[allow(non_snake_case)] 11 | #[no_mangle] 12 | pub unsafe extern "C" fn PyInit_sequential() -> *mut PyObject { 13 | PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF)) 14 | } 15 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/sequential/tests/test_.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from sequential import Id 3 | 4 | 5 | def test_make_some(): 6 | for x in range(12): 7 | i = Id() 8 | assert x == int(i) 9 | 10 | 11 | def test_args(): 12 | with pytest.raises(TypeError, match="Id\\(\\) takes no arguments"): 13 | Id(3, 4) 14 | 15 | 16 | def test_cmp(): 17 | a = Id() 18 | b = Id() 19 | assert a <= b 20 | assert a < b 21 | assert a == a 22 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/.template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["{{authors}}"] 3 | name = "{{project-name}}" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "string_sum" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3-ffi = { version = "{{PYO3_VERSION}}", features = ["extension-module"] } 13 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/.template/pre-script.rhai: -------------------------------------------------------------------------------- 1 | variable::set("PYO3_VERSION", "0.19.2"); 2 | file::rename(".template/Cargo.toml", "Cargo.toml"); 3 | file::rename(".template/pyproject.toml", "pyproject.toml"); 4 | file::delete(".template"); 5 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/.template/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "{{project-name}}" 7 | version = "0.1.0" 8 | 9 | [project.optional-dependencies] 10 | dev = ["pytest"] 11 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "string_sum" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.63" 6 | 7 | [lib] 8 | name = "string_sum" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | pyo3-ffi = { path = "../../", features = ["extension-module"] } 13 | 14 | [build-dependencies] 15 | pyo3-build-config = { path = "../../../pyo3-build-config" } 16 | 17 | [workspace] 18 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml Cargo.toml 2 | recursive-include src * 3 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/README.md: -------------------------------------------------------------------------------- 1 | # string_sum 2 | 3 | A project built using only `pyo3_ffi`, without any of PyO3's safe api. 4 | 5 | ## Building and Testing 6 | 7 | To build this package, first install `maturin`: 8 | 9 | ```shell 10 | pip install maturin 11 | ``` 12 | 13 | To build and test use `maturin develop`: 14 | 15 | ```shell 16 | pip install -r requirements-dev.txt 17 | maturin develop 18 | pytest 19 | ``` 20 | 21 | Alternatively, install nox and run the tests inside an isolated environment: 22 | 23 | ```shell 24 | nox 25 | ``` 26 | 27 | ## Copying this example 28 | 29 | Use [`cargo-generate`](https://crates.io/crates/cargo-generate): 30 | 31 | ```bash 32 | $ cargo install cargo-generate 33 | $ cargo generate --git https://github.com/PyO3/pyo3 examples/string_sum 34 | ``` 35 | 36 | (`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.) 37 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pyo3_build_config::use_pyo3_cfgs(); 3 | } 4 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | ignore = [".nox"] 3 | 4 | [hooks] 5 | pre = [".template/pre-script.rhai"] 6 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/noxfile.py: -------------------------------------------------------------------------------- 1 | import nox 2 | 3 | 4 | @nox.session 5 | def python(session: nox.Session): 6 | session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" 7 | session.install(".[dev]") 8 | session.run("pytest") 9 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "string_sum" 7 | version = "0.1.0" 8 | classifiers = [ 9 | "License :: OSI Approved :: MIT License", 10 | "Development Status :: 3 - Alpha", 11 | "Intended Audience :: Developers", 12 | "Programming Language :: Python", 13 | "Programming Language :: Rust", 14 | "Operating System :: POSIX", 15 | "Operating System :: MacOS :: MacOS X", 16 | ] 17 | 18 | [project.optional-dependencies] 19 | dev = ["pytest"] 20 | -------------------------------------------------------------------------------- /pyo3-ffi/examples/string-sum/tests/test_.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from string_sum import sum_as_string 3 | 4 | 5 | def test_sum(): 6 | a, b = 12, 42 7 | 8 | added = sum_as_string(a, b) 9 | assert added == "54" 10 | 11 | 12 | def test_err1(): 13 | a, b = "abc", 42 14 | 15 | with pytest.raises( 16 | TypeError, match="sum_as_string expected an int for positional argument 1" 17 | ): 18 | sum_as_string(a, b) 19 | 20 | 21 | def test_err2(): 22 | a, b = 0, {} 23 | 24 | with pytest.raises( 25 | TypeError, match="sum_as_string expected an int for positional argument 2" 26 | ): 27 | sum_as_string(a, b) 28 | 29 | 30 | def test_overflow1(): 31 | a, b = 0, 1 << 43 32 | 33 | with pytest.raises(OverflowError, match="cannot fit 8796093022208 in 32 bits"): 34 | sum_as_string(a, b) 35 | 36 | 37 | def test_overflow2(): 38 | a, b = 1 << 30, 1 << 30 39 | 40 | with pytest.raises(OverflowError, match="arguments too large to add"): 41 | sum_as_string(a, b) 42 | -------------------------------------------------------------------------------- /pyo3-ffi/src/bltinmodule.rs: -------------------------------------------------------------------------------- 1 | use crate::object::PyTypeObject; 2 | 3 | #[cfg_attr(windows, link(name = "pythonXY"))] 4 | extern "C" { 5 | pub static mut PyFilter_Type: PyTypeObject; 6 | pub static mut PyMap_Type: PyTypeObject; 7 | pub static mut PyZip_Type: PyTypeObject; 8 | } 9 | -------------------------------------------------------------------------------- /pyo3-ffi/src/code.rs: -------------------------------------------------------------------------------- 1 | // This header doesn't exist in CPython, but Include/cpython/code.h does. We add 2 | // this here so that PyCodeObject has a definition under the limited API. 3 | 4 | opaque_struct!(pub PyCodeObject); 5 | -------------------------------------------------------------------------------- /pyo3-ffi/src/compat/py_3_10.rs: -------------------------------------------------------------------------------- 1 | compat_function!( 2 | originally_defined_for(Py_3_10); 3 | 4 | #[inline] 5 | pub unsafe fn Py_NewRef(obj: *mut crate::PyObject) -> *mut crate::PyObject { 6 | crate::Py_INCREF(obj); 7 | obj 8 | } 9 | ); 10 | 11 | compat_function!( 12 | originally_defined_for(Py_3_10); 13 | 14 | #[inline] 15 | pub unsafe fn Py_XNewRef(obj: *mut crate::PyObject) -> *mut crate::PyObject { 16 | crate::Py_XINCREF(obj); 17 | obj 18 | } 19 | ); 20 | 21 | compat_function!( 22 | originally_defined_for(Py_3_10); 23 | 24 | #[inline] 25 | pub unsafe fn PyModule_AddObjectRef( 26 | module: *mut crate::PyObject, 27 | name: *const std::os::raw::c_char, 28 | value: *mut crate::PyObject, 29 | ) -> std::os::raw::c_int { 30 | if value.is_null() && crate::PyErr_Occurred().is_null() { 31 | crate::PyErr_SetString( 32 | crate::PyExc_SystemError, 33 | c_str!("PyModule_AddObjectRef() must be called with an exception raised if value is NULL").as_ptr(), 34 | ); 35 | return -1; 36 | } 37 | 38 | crate::Py_XINCREF(value); 39 | let result = crate::PyModule_AddObject(module, name, value); 40 | if result < 0 { 41 | crate::Py_XDECREF(value); 42 | } 43 | result 44 | } 45 | ); 46 | -------------------------------------------------------------------------------- /pyo3-ffi/src/compat/py_3_14.rs: -------------------------------------------------------------------------------- 1 | compat_function!( 2 | originally_defined_for(all(Py_3_14, not(Py_LIMITED_API))); 3 | 4 | #[inline] 5 | pub unsafe fn Py_HashBuffer( 6 | ptr: *const std::ffi::c_void, 7 | len: crate::Py_ssize_t, 8 | ) -> crate::Py_hash_t { 9 | #[cfg(not(any(Py_LIMITED_API, PyPy)))] 10 | { 11 | crate::_Py_HashBytes(ptr, len) 12 | } 13 | 14 | #[cfg(any(Py_LIMITED_API, PyPy))] 15 | { 16 | let bytes = crate::PyBytes_FromStringAndSize(ptr as *const std::os::raw::c_char, len); 17 | if bytes.is_null() { 18 | -1 19 | } else { 20 | let result = crate::PyObject_Hash(bytes); 21 | crate::Py_DECREF(bytes); 22 | result 23 | } 24 | } 25 | } 26 | ); 27 | -------------------------------------------------------------------------------- /pyo3-ffi/src/compat/py_3_9.rs: -------------------------------------------------------------------------------- 1 | compat_function!( 2 | originally_defined_for(all( 3 | not(PyPy), 4 | any(Py_3_10, all(not(Py_LIMITED_API), Py_3_9)) // Added to python in 3.9 but to limited API in 3.10 5 | )); 6 | 7 | #[inline] 8 | pub unsafe fn PyObject_CallNoArgs(obj: *mut crate::PyObject) -> *mut crate::PyObject { 9 | crate::PyObject_CallObject(obj, std::ptr::null_mut()) 10 | } 11 | ); 12 | 13 | compat_function!( 14 | originally_defined_for(all(Py_3_9, not(any(Py_LIMITED_API, PyPy)))); 15 | 16 | #[inline] 17 | pub unsafe fn PyObject_CallMethodNoArgs(obj: *mut crate::PyObject, name: *mut crate::PyObject) -> *mut crate::PyObject { 18 | crate::PyObject_CallMethodObjArgs(obj, name, std::ptr::null_mut::()) 19 | } 20 | ); 21 | -------------------------------------------------------------------------------- /pyo3-ffi/src/compile.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_int; 2 | 3 | pub const Py_single_input: c_int = 256; 4 | pub const Py_file_input: c_int = 257; 5 | pub const Py_eval_input: c_int = 258; 6 | #[cfg(Py_3_8)] 7 | pub const Py_func_type_input: c_int = 345; 8 | 9 | #[cfg(Py_3_9)] 10 | pub const Py_fstring_input: c_int = 800; 11 | -------------------------------------------------------------------------------- /pyo3-ffi/src/complexobject.rs: -------------------------------------------------------------------------------- 1 | use crate::object::*; 2 | use std::os::raw::{c_double, c_int}; 3 | use std::ptr::addr_of_mut; 4 | 5 | #[cfg_attr(windows, link(name = "pythonXY"))] 6 | extern "C" { 7 | #[cfg_attr(PyPy, link_name = "PyPyComplex_Type")] 8 | pub static mut PyComplex_Type: PyTypeObject; 9 | } 10 | 11 | #[inline] 12 | pub unsafe fn PyComplex_Check(op: *mut PyObject) -> c_int { 13 | PyObject_TypeCheck(op, addr_of_mut!(PyComplex_Type)) 14 | } 15 | 16 | #[inline] 17 | pub unsafe fn PyComplex_CheckExact(op: *mut PyObject) -> c_int { 18 | Py_IS_TYPE(op, addr_of_mut!(PyComplex_Type)) 19 | } 20 | 21 | extern "C" { 22 | // skipped non-limited PyComplex_FromCComplex 23 | #[cfg_attr(PyPy, link_name = "PyPyComplex_FromDoubles")] 24 | pub fn PyComplex_FromDoubles(real: c_double, imag: c_double) -> *mut PyObject; 25 | 26 | #[cfg_attr(PyPy, link_name = "PyPyComplex_RealAsDouble")] 27 | pub fn PyComplex_RealAsDouble(op: *mut PyObject) -> c_double; 28 | #[cfg_attr(PyPy, link_name = "PyPyComplex_ImagAsDouble")] 29 | pub fn PyComplex_ImagAsDouble(op: *mut PyObject) -> c_double; 30 | } 31 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/bytesobject.rs: -------------------------------------------------------------------------------- 1 | use crate::object::*; 2 | use crate::Py_ssize_t; 3 | #[cfg(not(Py_LIMITED_API))] 4 | use std::os::raw::c_char; 5 | use std::os::raw::c_int; 6 | 7 | #[cfg(not(any(PyPy, GraalPy, Py_LIMITED_API)))] 8 | #[repr(C)] 9 | pub struct PyBytesObject { 10 | pub ob_base: PyVarObject, 11 | #[cfg_attr( 12 | Py_3_11, 13 | deprecated(note = "Deprecated in Python 3.11 and will be removed in a future version.") 14 | )] 15 | pub ob_shash: crate::Py_hash_t, 16 | pub ob_sval: [c_char; 1], 17 | } 18 | 19 | #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] 20 | opaque_struct!(pub PyBytesObject); 21 | 22 | extern "C" { 23 | #[cfg_attr(PyPy, link_name = "_PyPyBytes_Resize")] 24 | pub fn _PyBytes_Resize(bytes: *mut *mut PyObject, newsize: Py_ssize_t) -> c_int; 25 | } 26 | 27 | #[cfg(not(Py_LIMITED_API))] 28 | #[inline] 29 | pub unsafe fn PyBytes_AS_STRING(op: *mut PyObject) -> *const c_char { 30 | #[cfg(not(any(PyPy, GraalPy)))] 31 | return &(*op.cast::()).ob_sval as *const c_char; 32 | #[cfg(any(PyPy, GraalPy))] 33 | return crate::PyBytes_AsString(op); 34 | } 35 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/ceval.rs: -------------------------------------------------------------------------------- 1 | use crate::cpython::pystate::Py_tracefunc; 2 | use crate::object::{freefunc, PyObject}; 3 | use std::os::raw::c_int; 4 | 5 | extern "C" { 6 | // skipped non-limited _PyEval_CallTracing 7 | 8 | #[cfg(not(Py_3_11))] 9 | pub fn _PyEval_EvalFrameDefault(arg1: *mut crate::PyFrameObject, exc: c_int) -> *mut PyObject; 10 | 11 | #[cfg(Py_3_11)] 12 | pub fn _PyEval_EvalFrameDefault( 13 | tstate: *mut crate::PyThreadState, 14 | frame: *mut crate::_PyInterpreterFrame, 15 | exc: c_int, 16 | ) -> *mut crate::PyObject; 17 | 18 | pub fn _PyEval_RequestCodeExtraIndex(func: freefunc) -> c_int; 19 | pub fn PyEval_SetProfile(trace_func: Option, arg1: *mut PyObject); 20 | pub fn PyEval_SetTrace(trace_func: Option, arg1: *mut PyObject); 21 | } 22 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/complexobject.rs: -------------------------------------------------------------------------------- 1 | use crate::PyObject; 2 | use std::os::raw::c_double; 3 | 4 | #[repr(C)] 5 | #[derive(Copy, Clone)] 6 | pub struct Py_complex { 7 | pub real: c_double, 8 | pub imag: c_double, 9 | } 10 | 11 | // skipped private function _Py_c_sum 12 | // skipped private function _Py_c_diff 13 | // skipped private function _Py_c_neg 14 | // skipped private function _Py_c_prod 15 | // skipped private function _Py_c_quot 16 | // skipped private function _Py_c_pow 17 | // skipped private function _Py_c_abs 18 | 19 | #[repr(C)] 20 | pub struct PyComplexObject { 21 | pub ob_base: PyObject, 22 | pub cval: Py_complex, 23 | } 24 | 25 | extern "C" { 26 | #[cfg_attr(PyPy, link_name = "PyPyComplex_FromCComplex")] 27 | pub fn PyComplex_FromCComplex(v: Py_complex) -> *mut PyObject; 28 | #[cfg_attr(PyPy, link_name = "PyPyComplex_AsCComplex")] 29 | pub fn PyComplex_AsCComplex(op: *mut PyObject) -> Py_complex; 30 | } 31 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/critical_section.rs: -------------------------------------------------------------------------------- 1 | #[cfg(Py_GIL_DISABLED)] 2 | use crate::PyMutex; 3 | use crate::PyObject; 4 | 5 | #[repr(C)] 6 | #[cfg(Py_GIL_DISABLED)] 7 | pub struct PyCriticalSection { 8 | _cs_prev: usize, 9 | _cs_mutex: *mut PyMutex, 10 | } 11 | 12 | #[repr(C)] 13 | #[cfg(Py_GIL_DISABLED)] 14 | pub struct PyCriticalSection2 { 15 | _cs_base: PyCriticalSection, 16 | _cs_mutex2: *mut PyMutex, 17 | } 18 | 19 | #[cfg(not(Py_GIL_DISABLED))] 20 | opaque_struct!(pub PyCriticalSection); 21 | 22 | #[cfg(not(Py_GIL_DISABLED))] 23 | opaque_struct!(pub PyCriticalSection2); 24 | 25 | extern "C" { 26 | pub fn PyCriticalSection_Begin(c: *mut PyCriticalSection, op: *mut PyObject); 27 | pub fn PyCriticalSection_End(c: *mut PyCriticalSection); 28 | pub fn PyCriticalSection2_Begin(c: *mut PyCriticalSection2, a: *mut PyObject, b: *mut PyObject); 29 | pub fn PyCriticalSection2_End(c: *mut PyCriticalSection2); 30 | } 31 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/floatobject.rs: -------------------------------------------------------------------------------- 1 | #[cfg(GraalPy)] 2 | use crate::PyFloat_AsDouble; 3 | use crate::{PyFloat_Check, PyObject}; 4 | use std::os::raw::c_double; 5 | 6 | #[repr(C)] 7 | pub struct PyFloatObject { 8 | pub ob_base: PyObject, 9 | pub ob_fval: c_double, 10 | } 11 | 12 | #[inline] 13 | pub unsafe fn _PyFloat_CAST(op: *mut PyObject) -> *mut PyFloatObject { 14 | debug_assert_eq!(PyFloat_Check(op), 1); 15 | op.cast() 16 | } 17 | 18 | #[inline] 19 | pub unsafe fn PyFloat_AS_DOUBLE(op: *mut PyObject) -> c_double { 20 | #[cfg(not(GraalPy))] 21 | return (*_PyFloat_CAST(op)).ob_fval; 22 | #[cfg(GraalPy)] 23 | return PyFloat_AsDouble(op); 24 | } 25 | 26 | // skipped PyFloat_Pack2 27 | // skipped PyFloat_Pack4 28 | // skipped PyFloat_Pack8 29 | 30 | // skipped PyFloat_Unpack2 31 | // skipped PyFloat_Unpack4 32 | // skipped PyFloat_Unpack8 33 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/listobject.rs: -------------------------------------------------------------------------------- 1 | use crate::object::*; 2 | #[cfg(not(PyPy))] 3 | use crate::pyport::Py_ssize_t; 4 | 5 | #[cfg(not(PyPy))] 6 | #[repr(C)] 7 | pub struct PyListObject { 8 | pub ob_base: PyVarObject, 9 | pub ob_item: *mut *mut PyObject, 10 | pub allocated: Py_ssize_t, 11 | } 12 | 13 | #[cfg(PyPy)] 14 | pub struct PyListObject { 15 | pub ob_base: PyObject, 16 | } 17 | 18 | // skipped _PyList_Extend 19 | // skipped _PyList_DebugMallocStats 20 | // skipped _PyList_CAST (used inline below) 21 | 22 | /// Macro, trading safety for speed 23 | #[inline] 24 | #[cfg(not(any(PyPy, GraalPy)))] 25 | pub unsafe fn PyList_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mut PyObject { 26 | *(*(op as *mut PyListObject)).ob_item.offset(i) 27 | } 28 | 29 | /// Macro, *only* to be used to fill in brand new lists 30 | #[inline] 31 | #[cfg(not(any(PyPy, GraalPy)))] 32 | pub unsafe fn PyList_SET_ITEM(op: *mut PyObject, i: Py_ssize_t, v: *mut PyObject) { 33 | *(*(op as *mut PyListObject)).ob_item.offset(i) = v; 34 | } 35 | 36 | #[inline] 37 | #[cfg(not(PyPy))] 38 | pub unsafe fn PyList_GET_SIZE(op: *mut PyObject) -> Py_ssize_t { 39 | Py_SIZE(op) 40 | } 41 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/lock.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomPinned; 2 | use std::sync::atomic::AtomicU8; 3 | 4 | #[repr(transparent)] 5 | #[derive(Debug)] 6 | pub struct PyMutex { 7 | pub(crate) _bits: AtomicU8, 8 | pub(crate) _pin: PhantomPinned, 9 | } 10 | 11 | extern "C" { 12 | pub fn PyMutex_Lock(m: *mut PyMutex); 13 | pub fn PyMutex_Unlock(m: *mut PyMutex); 14 | } 15 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/pyhash.rs: -------------------------------------------------------------------------------- 1 | #[cfg(Py_3_14)] 2 | use crate::Py_ssize_t; 3 | #[cfg(Py_3_13)] 4 | use crate::{PyObject, Py_hash_t}; 5 | #[cfg(any(Py_3_13, not(PyPy)))] 6 | use std::os::raw::c_void; 7 | #[cfg(not(PyPy))] 8 | use std::os::raw::{c_char, c_int}; 9 | 10 | #[cfg(not(PyPy))] 11 | #[repr(C)] 12 | #[derive(Copy, Clone)] 13 | pub struct PyHash_FuncDef { 14 | pub hash: 15 | Option crate::Py_hash_t>, 16 | pub name: *const c_char, 17 | pub hash_bits: c_int, 18 | pub seed_bits: c_int, 19 | } 20 | 21 | #[cfg(not(PyPy))] 22 | impl Default for PyHash_FuncDef { 23 | #[inline] 24 | fn default() -> Self { 25 | unsafe { std::mem::zeroed() } 26 | } 27 | } 28 | 29 | extern "C" { 30 | #[cfg(not(PyPy))] 31 | pub fn PyHash_GetFuncDef() -> *mut PyHash_FuncDef; 32 | #[cfg(Py_3_13)] 33 | pub fn Py_HashPointer(ptr: *const c_void) -> Py_hash_t; 34 | #[cfg(Py_3_13)] 35 | pub fn PyObject_GenericHash(obj: *mut PyObject) -> Py_hash_t; 36 | #[cfg(Py_3_14)] 37 | pub fn Py_HashBuffer(ptr: *const c_void, len: Py_ssize_t) -> Py_hash_t; 38 | } 39 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/tupleobject.rs: -------------------------------------------------------------------------------- 1 | use crate::object::*; 2 | #[cfg(Py_3_14)] 3 | use crate::pyport::Py_hash_t; 4 | #[cfg(not(PyPy))] 5 | use crate::pyport::Py_ssize_t; 6 | 7 | #[repr(C)] 8 | pub struct PyTupleObject { 9 | pub ob_base: PyVarObject, 10 | #[cfg(Py_3_14)] 11 | pub ob_hash: Py_hash_t, 12 | pub ob_item: [*mut PyObject; 1], 13 | } 14 | 15 | // skipped _PyTuple_Resize 16 | // skipped _PyTuple_MaybeUntrack 17 | 18 | // skipped _PyTuple_CAST 19 | 20 | /// Macro, trading safety for speed 21 | #[inline] 22 | #[cfg(not(PyPy))] 23 | pub unsafe fn PyTuple_GET_SIZE(op: *mut PyObject) -> Py_ssize_t { 24 | Py_SIZE(op) 25 | } 26 | 27 | #[inline] 28 | #[cfg(not(any(PyPy, GraalPy)))] 29 | pub unsafe fn PyTuple_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mut PyObject { 30 | *(*(op as *mut PyTupleObject)).ob_item.as_ptr().offset(i) 31 | } 32 | 33 | /// Macro, *only* to be used to fill in brand new tuples 34 | #[inline] 35 | #[cfg(not(any(PyPy, GraalPy)))] 36 | pub unsafe fn PyTuple_SET_ITEM(op: *mut PyObject, i: Py_ssize_t, v: *mut PyObject) { 37 | *(*(op as *mut PyTupleObject)).ob_item.as_mut_ptr().offset(i) = v; 38 | } 39 | 40 | // skipped _PyTuple_DebugMallocStats 41 | -------------------------------------------------------------------------------- /pyo3-ffi/src/cpython/weakrefobject.rs: -------------------------------------------------------------------------------- 1 | // NB publicly re-exported in `src/weakrefobject.rs` 2 | #[cfg(not(any(PyPy, GraalPy)))] 3 | pub struct _PyWeakReference { 4 | pub ob_base: crate::PyObject, 5 | pub wr_object: *mut crate::PyObject, 6 | pub wr_callback: *mut crate::PyObject, 7 | pub hash: crate::Py_hash_t, 8 | pub wr_prev: *mut crate::PyWeakReference, 9 | pub wr_next: *mut crate::PyWeakReference, 10 | #[cfg(Py_3_11)] 11 | pub vectorcall: Option, 12 | #[cfg(all(Py_3_13, Py_GIL_DISABLED))] 13 | pub weakrefs_lock: *mut crate::PyMutex, 14 | } 15 | 16 | // skipped _PyWeakref_GetWeakrefCount 17 | // skipped _PyWeakref_ClearRef 18 | // skipped PyWeakRef_GET_OBJECT 19 | -------------------------------------------------------------------------------- /pyo3-ffi/src/enumobject.rs: -------------------------------------------------------------------------------- 1 | use crate::object::PyTypeObject; 2 | 3 | #[cfg_attr(windows, link(name = "pythonXY"))] 4 | extern "C" { 5 | pub static mut PyEnum_Type: PyTypeObject; 6 | pub static mut PyReversed_Type: PyTypeObject; 7 | } 8 | -------------------------------------------------------------------------------- /pyo3-ffi/src/fileutils.rs: -------------------------------------------------------------------------------- 1 | use crate::pyport::Py_ssize_t; 2 | use libc::wchar_t; 3 | use std::os::raw::c_char; 4 | 5 | extern "C" { 6 | pub fn Py_DecodeLocale(arg1: *const c_char, size: *mut Py_ssize_t) -> *mut wchar_t; 7 | 8 | pub fn Py_EncodeLocale(text: *const wchar_t, error_pos: *mut Py_ssize_t) -> *mut c_char; 9 | } 10 | -------------------------------------------------------------------------------- /pyo3-ffi/src/genericaliasobject.rs: -------------------------------------------------------------------------------- 1 | #[cfg(Py_3_9)] 2 | use crate::object::{PyObject, PyTypeObject}; 3 | 4 | #[cfg_attr(windows, link(name = "pythonXY"))] 5 | extern "C" { 6 | #[cfg(Py_3_9)] 7 | #[cfg_attr(PyPy, link_name = "PyPy_GenericAlias")] 8 | pub fn Py_GenericAlias(origin: *mut PyObject, args: *mut PyObject) -> *mut PyObject; 9 | 10 | #[cfg(Py_3_9)] 11 | pub static mut Py_GenericAliasType: PyTypeObject; 12 | } 13 | -------------------------------------------------------------------------------- /pyo3-ffi/src/impl_/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(Py_GIL_DISABLED)] 2 | mod atomic_c_ulong { 3 | pub struct GetAtomicCULong(); 4 | 5 | pub trait AtomicCULongType { 6 | type Type; 7 | } 8 | impl AtomicCULongType for GetAtomicCULong<32> { 9 | type Type = std::sync::atomic::AtomicU32; 10 | } 11 | impl AtomicCULongType for GetAtomicCULong<64> { 12 | type Type = std::sync::atomic::AtomicU64; 13 | } 14 | 15 | pub type TYPE = 16 | () * 8 }> as AtomicCULongType>::Type; 17 | } 18 | 19 | /// Typedef for an atomic integer to match the platform-dependent c_ulong type. 20 | #[cfg(Py_GIL_DISABLED)] 21 | #[doc(hidden)] 22 | pub type AtomicCULong = atomic_c_ulong::TYPE; 23 | -------------------------------------------------------------------------------- /pyo3-ffi/src/intrcheck.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_int; 2 | 3 | extern "C" { 4 | #[cfg_attr(PyPy, link_name = "PyPyOS_InterruptOccurred")] 5 | pub fn PyOS_InterruptOccurred() -> c_int; 6 | #[cfg(not(Py_3_10))] 7 | #[deprecated(note = "Not documented in Python API; see Python 3.10 release notes")] 8 | pub fn PyOS_InitInterrupts(); 9 | 10 | pub fn PyOS_BeforeFork(); 11 | pub fn PyOS_AfterFork_Parent(); 12 | pub fn PyOS_AfterFork_Child(); 13 | #[deprecated(note = "use PyOS_AfterFork_Child instead")] 14 | #[cfg_attr(PyPy, link_name = "PyPyOS_AfterFork")] 15 | pub fn PyOS_AfterFork(); 16 | 17 | // skipped non-limited _PyOS_IsMainThread 18 | // skipped non-limited Windows _PyOS_SigintEvent 19 | } 20 | -------------------------------------------------------------------------------- /pyo3-ffi/src/iterobject.rs: -------------------------------------------------------------------------------- 1 | use crate::object::*; 2 | use std::os::raw::c_int; 3 | use std::ptr::addr_of_mut; 4 | 5 | #[cfg_attr(windows, link(name = "pythonXY"))] 6 | extern "C" { 7 | pub static mut PySeqIter_Type: PyTypeObject; 8 | pub static mut PyCallIter_Type: PyTypeObject; 9 | } 10 | 11 | #[inline] 12 | pub unsafe fn PySeqIter_Check(op: *mut PyObject) -> c_int { 13 | (Py_TYPE(op) == addr_of_mut!(PySeqIter_Type)) as c_int 14 | } 15 | 16 | extern "C" { 17 | #[cfg_attr(PyPy, link_name = "PyPySeqIter_New")] 18 | pub fn PySeqIter_New(arg1: *mut PyObject) -> *mut PyObject; 19 | } 20 | 21 | #[inline] 22 | pub unsafe fn PyCallIter_Check(op: *mut PyObject) -> c_int { 23 | (Py_TYPE(op) == addr_of_mut!(PyCallIter_Type)) as c_int 24 | } 25 | 26 | extern "C" { 27 | #[cfg_attr(PyPy, link_name = "PyPyCallIter_New")] 28 | pub fn PyCallIter_New(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject; 29 | } 30 | -------------------------------------------------------------------------------- /pyo3-ffi/src/marshal.rs: -------------------------------------------------------------------------------- 1 | use super::{PyObject, Py_ssize_t}; 2 | use std::os::raw::{c_char, c_int}; 3 | 4 | // skipped Py_MARSHAL_VERSION 5 | // skipped PyMarshal_WriteLongToFile 6 | // skipped PyMarshal_WriteObjectToFile 7 | 8 | extern "C" { 9 | #[cfg_attr(PyPy, link_name = "PyPyMarshal_WriteObjectToString")] 10 | pub fn PyMarshal_WriteObjectToString(object: *mut PyObject, version: c_int) -> *mut PyObject; 11 | 12 | // skipped non-limited PyMarshal_ReadLongFromFile 13 | // skipped non-limited PyMarshal_ReadShortFromFile 14 | // skipped non-limited PyMarshal_ReadObjectFromFile 15 | // skipped non-limited PyMarshal_ReadLastObjectFromFile 16 | 17 | #[cfg_attr(PyPy, link_name = "PyPyMarshal_ReadObjectFromString")] 18 | pub fn PyMarshal_ReadObjectFromString(data: *const c_char, len: Py_ssize_t) -> *mut PyObject; 19 | } 20 | -------------------------------------------------------------------------------- /pyo3-ffi/src/osmodule.rs: -------------------------------------------------------------------------------- 1 | use crate::object::PyObject; 2 | 3 | extern "C" { 4 | #[cfg_attr(PyPy, link_name = "PyPyOS_FSPath")] 5 | pub fn PyOS_FSPath(path: *mut PyObject) -> *mut PyObject; 6 | } 7 | -------------------------------------------------------------------------------- /pyo3-ffi/src/pyarena.rs: -------------------------------------------------------------------------------- 1 | opaque_struct!(pub PyArena); 2 | -------------------------------------------------------------------------------- /pyo3-ffi/src/pyframe.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_imports)] 2 | use crate::object::PyObject; 3 | #[cfg(not(GraalPy))] 4 | #[cfg(any(Py_3_10, all(Py_3_9, not(Py_LIMITED_API))))] 5 | use crate::PyCodeObject; 6 | use crate::PyFrameObject; 7 | use std::os::raw::c_int; 8 | 9 | extern "C" { 10 | pub fn PyFrame_GetLineNumber(frame: *mut PyFrameObject) -> c_int; 11 | 12 | #[cfg(not(GraalPy))] 13 | #[cfg(any(Py_3_10, all(Py_3_9, not(Py_LIMITED_API))))] 14 | pub fn PyFrame_GetCode(frame: *mut PyFrameObject) -> *mut PyCodeObject; 15 | } 16 | -------------------------------------------------------------------------------- /pyo3-ffi/src/pyhash.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(any(Py_LIMITED_API, PyPy)))] 2 | use crate::pyport::{Py_hash_t, Py_ssize_t}; 3 | #[cfg(not(any(Py_LIMITED_API, PyPy)))] 4 | use std::os::raw::c_void; 5 | 6 | use std::os::raw::{c_int, c_ulong}; 7 | 8 | extern "C" { 9 | // skipped non-limited _Py_HashDouble 10 | // skipped non-limited _Py_HashPointer 11 | // skipped non-limited _Py_HashPointerRaw 12 | 13 | #[cfg(not(any(Py_LIMITED_API, PyPy)))] 14 | pub fn _Py_HashBytes(src: *const c_void, len: Py_ssize_t) -> Py_hash_t; 15 | } 16 | 17 | pub const _PyHASH_MULTIPLIER: c_ulong = 1000003; 18 | 19 | // skipped _PyHASH_BITS 20 | 21 | // skipped non-limited _Py_HashSecret_t 22 | 23 | // skipped Py_HASH_CUTOFF 24 | 25 | pub const Py_HASH_EXTERNAL: c_int = 0; 26 | pub const Py_HASH_SIPHASH24: c_int = 1; 27 | pub const Py_HASH_FNV: c_int = 2; 28 | 29 | // skipped Py_HASH_ALGORITHM 30 | -------------------------------------------------------------------------------- /pyo3-ffi/src/pymem.rs: -------------------------------------------------------------------------------- 1 | use libc::size_t; 2 | use std::os::raw::c_void; 3 | 4 | extern "C" { 5 | #[cfg_attr(PyPy, link_name = "PyPyMem_Malloc")] 6 | pub fn PyMem_Malloc(size: size_t) -> *mut c_void; 7 | #[cfg_attr(PyPy, link_name = "PyPyMem_Calloc")] 8 | pub fn PyMem_Calloc(nelem: size_t, elsize: size_t) -> *mut c_void; 9 | #[cfg_attr(PyPy, link_name = "PyPyMem_Realloc")] 10 | pub fn PyMem_Realloc(ptr: *mut c_void, new_size: size_t) -> *mut c_void; 11 | #[cfg_attr(PyPy, link_name = "PyPyMem_Free")] 12 | pub fn PyMem_Free(ptr: *mut c_void); 13 | } 14 | -------------------------------------------------------------------------------- /pyo3-ffi/src/pyport.rs: -------------------------------------------------------------------------------- 1 | // NB libc does not define this constant on all platforms, so we hard code it 2 | // like CPython does. 3 | // https://github.com/python/cpython/blob/d8b9011702443bb57579f8834f3effe58e290dfc/Include/pyport.h#L372 4 | pub const INT_MAX: std::os::raw::c_int = 2147483647; 5 | 6 | pub type PY_UINT32_T = u32; 7 | pub type PY_UINT64_T = u64; 8 | 9 | pub type PY_INT32_T = i32; 10 | pub type PY_INT64_T = i64; 11 | 12 | pub type Py_uintptr_t = ::libc::uintptr_t; 13 | pub type Py_intptr_t = ::libc::intptr_t; 14 | pub type Py_ssize_t = ::libc::ssize_t; 15 | 16 | pub type Py_hash_t = Py_ssize_t; 17 | pub type Py_uhash_t = ::libc::size_t; 18 | 19 | pub const PY_SSIZE_T_MIN: Py_ssize_t = Py_ssize_t::MIN; 20 | pub const PY_SSIZE_T_MAX: Py_ssize_t = Py_ssize_t::MAX; 21 | 22 | #[cfg(target_endian = "big")] 23 | pub const PY_BIG_ENDIAN: usize = 1; 24 | #[cfg(target_endian = "big")] 25 | pub const PY_LITTLE_ENDIAN: usize = 0; 26 | 27 | #[cfg(target_endian = "little")] 28 | pub const PY_BIG_ENDIAN: usize = 0; 29 | #[cfg(target_endian = "little")] 30 | pub const PY_LITTLE_ENDIAN: usize = 1; 31 | -------------------------------------------------------------------------------- /pyo3-ffi/src/pystrtod.rs: -------------------------------------------------------------------------------- 1 | use crate::object::PyObject; 2 | use std::os::raw::{c_char, c_double, c_int}; 3 | 4 | extern "C" { 5 | #[cfg_attr(PyPy, link_name = "PyPyOS_string_to_double")] 6 | pub fn PyOS_string_to_double( 7 | str: *const c_char, 8 | endptr: *mut *mut c_char, 9 | overflow_exception: *mut PyObject, 10 | ) -> c_double; 11 | #[cfg_attr(PyPy, link_name = "PyPyOS_double_to_string")] 12 | pub fn PyOS_double_to_string( 13 | val: c_double, 14 | format_code: c_char, 15 | precision: c_int, 16 | flags: c_int, 17 | _type: *mut c_int, 18 | ) -> *mut c_char; 19 | } 20 | 21 | // skipped non-limited _Py_string_to_number_with_underscores 22 | // skipped non-limited _Py_parse_inf_or_nan 23 | 24 | /* PyOS_double_to_string's "flags" parameter can be set to 0 or more of: */ 25 | pub const Py_DTSF_SIGN: c_int = 0x01; /* always add the sign */ 26 | pub const Py_DTSF_ADD_DOT_0: c_int = 0x02; /* if the result is an integer add ".0" */ 27 | pub const Py_DTSF_ALT: c_int = 0x04; /* "alternate" formatting. it's format_code specific */ 28 | 29 | /* PyOS_double_to_string's "type", if non-NULL, will be set to one of: */ 30 | pub const Py_DTST_FINITE: c_int = 0; 31 | pub const Py_DTST_INFINITE: c_int = 1; 32 | pub const Py_DTST_NAN: c_int = 2; 33 | -------------------------------------------------------------------------------- /pyo3-ffi/src/pytypedefs.rs: -------------------------------------------------------------------------------- 1 | // TODO: Created this file as part of fixing pyframe.rs and cpython/pyframe.rs 2 | // TODO: Finish defining or moving declarations now in Include/pytypedefs.h 3 | 4 | opaque_struct!(pub PyFrameObject); 5 | -------------------------------------------------------------------------------- /pyo3-ffi/src/rangeobject.rs: -------------------------------------------------------------------------------- 1 | use crate::object::*; 2 | use std::os::raw::c_int; 3 | use std::ptr::addr_of_mut; 4 | 5 | #[cfg_attr(windows, link(name = "pythonXY"))] 6 | extern "C" { 7 | #[cfg_attr(PyPy, link_name = "PyPyRange_Type")] 8 | pub static mut PyRange_Type: PyTypeObject; 9 | pub static mut PyRangeIter_Type: PyTypeObject; 10 | pub static mut PyLongRangeIter_Type: PyTypeObject; 11 | } 12 | 13 | #[inline] 14 | pub unsafe fn PyRange_Check(op: *mut PyObject) -> c_int { 15 | (Py_TYPE(op) == addr_of_mut!(PyRange_Type)) as c_int 16 | } 17 | -------------------------------------------------------------------------------- /pyo3-ffi/src/structmember.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_int; 2 | 3 | pub use crate::PyMemberDef; 4 | 5 | pub use crate::Py_T_BOOL as T_BOOL; 6 | pub use crate::Py_T_BYTE as T_BYTE; 7 | pub use crate::Py_T_CHAR as T_CHAR; 8 | pub use crate::Py_T_DOUBLE as T_DOUBLE; 9 | pub use crate::Py_T_FLOAT as T_FLOAT; 10 | pub use crate::Py_T_INT as T_INT; 11 | pub use crate::Py_T_LONG as T_LONG; 12 | pub use crate::Py_T_LONGLONG as T_LONGLONG; 13 | pub use crate::Py_T_OBJECT_EX as T_OBJECT_EX; 14 | pub use crate::Py_T_SHORT as T_SHORT; 15 | pub use crate::Py_T_STRING as T_STRING; 16 | pub use crate::Py_T_STRING_INPLACE as T_STRING_INPLACE; 17 | pub use crate::Py_T_UBYTE as T_UBYTE; 18 | pub use crate::Py_T_UINT as T_UINT; 19 | pub use crate::Py_T_ULONG as T_ULONG; 20 | pub use crate::Py_T_ULONGLONG as T_ULONGLONG; 21 | pub use crate::Py_T_USHORT as T_USHORT; 22 | #[allow(deprecated)] 23 | pub use crate::_Py_T_OBJECT as T_OBJECT; 24 | 25 | pub use crate::Py_T_PYSSIZET as T_PYSSIZET; 26 | #[allow(deprecated)] 27 | pub use crate::_Py_T_NONE as T_NONE; 28 | 29 | /* Flags */ 30 | pub use crate::Py_READONLY as READONLY; 31 | pub const READ_RESTRICTED: c_int = 2; 32 | pub const PY_WRITE_RESTRICTED: c_int = 4; 33 | pub const RESTRICTED: c_int = READ_RESTRICTED | PY_WRITE_RESTRICTED; 34 | -------------------------------------------------------------------------------- /pyo3-ffi/src/traceback.rs: -------------------------------------------------------------------------------- 1 | use crate::object::*; 2 | use std::os::raw::c_int; 3 | #[cfg(not(PyPy))] 4 | use std::ptr::addr_of_mut; 5 | 6 | extern "C" { 7 | #[cfg_attr(PyPy, link_name = "PyPyTraceBack_Here")] 8 | pub fn PyTraceBack_Here(arg1: *mut crate::PyFrameObject) -> c_int; 9 | #[cfg_attr(PyPy, link_name = "PyPyTraceBack_Print")] 10 | pub fn PyTraceBack_Print(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int; 11 | } 12 | 13 | #[cfg_attr(windows, link(name = "pythonXY"))] 14 | extern "C" { 15 | #[cfg_attr(PyPy, link_name = "PyPyTraceBack_Type")] 16 | pub static mut PyTraceBack_Type: PyTypeObject; 17 | 18 | #[cfg(PyPy)] 19 | #[link_name = "PyPyTraceBack_Check"] 20 | pub fn PyTraceBack_Check(op: *mut PyObject) -> c_int; 21 | } 22 | 23 | #[inline] 24 | #[cfg(not(PyPy))] 25 | pub unsafe fn PyTraceBack_Check(op: *mut PyObject) -> c_int { 26 | (Py_TYPE(op) == addr_of_mut!(PyTraceBack_Type)) as c_int 27 | } 28 | -------------------------------------------------------------------------------- /pyo3-ffi/src/warnings.rs: -------------------------------------------------------------------------------- 1 | use crate::object::PyObject; 2 | use crate::pyport::Py_ssize_t; 3 | use std::os::raw::{c_char, c_int}; 4 | 5 | extern "C" { 6 | #[cfg_attr(PyPy, link_name = "PyPyErr_WarnEx")] 7 | pub fn PyErr_WarnEx( 8 | category: *mut PyObject, 9 | message: *const c_char, 10 | stack_level: Py_ssize_t, 11 | ) -> c_int; 12 | #[cfg_attr(PyPy, link_name = "PyPyErr_WarnFormat")] 13 | pub fn PyErr_WarnFormat( 14 | category: *mut PyObject, 15 | stack_level: Py_ssize_t, 16 | format: *const c_char, 17 | ... 18 | ) -> c_int; 19 | pub fn PyErr_ResourceWarning( 20 | source: *mut PyObject, 21 | stack_level: Py_ssize_t, 22 | format: *const c_char, 23 | ... 24 | ) -> c_int; 25 | #[cfg_attr(PyPy, link_name = "PyPyErr_WarnExplicit")] 26 | pub fn PyErr_WarnExplicit( 27 | category: *mut PyObject, 28 | message: *const c_char, 29 | filename: *const c_char, 30 | lineno: c_int, 31 | module: *const c_char, 32 | registry: *mut PyObject, 33 | ) -> c_int; 34 | } 35 | -------------------------------------------------------------------------------- /pyo3-introspection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-introspection" 3 | version = "0.25.0" 4 | description = "Introspect dynamic libraries built with PyO3 to get metadata about the exported Python types" 5 | authors = ["PyO3 Project and Contributors "] 6 | homepage = "https://github.com/pyo3/pyo3" 7 | repository = "https://github.com/pyo3/pyo3" 8 | license = "MIT OR Apache-2.0" 9 | edition = "2021" 10 | 11 | [dependencies] 12 | anyhow = "1" 13 | goblin = "0.9.0" 14 | serde = { version = "1", features = ["derive"] } 15 | serde_json = "1" 16 | 17 | [lints] 18 | workspace = true 19 | -------------------------------------------------------------------------------- /pyo3-introspection/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /pyo3-introspection/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /pyo3-introspection/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Utilities to introspect cdylib built using PyO3 and generate [type stubs](https://typing.readthedocs.io/en/latest/source/stubs.html). 2 | 3 | pub use crate::introspection::introspect_cdylib; 4 | pub use crate::stubs::module_stub_files; 5 | 6 | mod introspection; 7 | pub mod model; 8 | mod stubs; 9 | -------------------------------------------------------------------------------- /pyo3-macros-backend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-macros-backend" 3 | version = "0.25.0" 4 | description = "Code generation for PyO3 package" 5 | authors = ["PyO3 Project and Contributors "] 6 | keywords = ["pyo3", "python", "cpython", "ffi"] 7 | homepage = "https://github.com/pyo3/pyo3" 8 | repository = "https://github.com/pyo3/pyo3" 9 | categories = ["api-bindings", "development-tools::ffi"] 10 | license = "MIT OR Apache-2.0" 11 | edition = "2021" 12 | rust-version = "1.63" 13 | 14 | # Note: we use default-features = false for proc-macro related crates 15 | # not to depend on proc-macro itself. 16 | # See https://github.com/PyO3/pyo3/pull/810 for more. 17 | [dependencies] 18 | heck = "0.5" 19 | proc-macro2 = { version = "1.0.60", default-features = false } 20 | pyo3-build-config = { path = "../pyo3-build-config", version = "=0.25.0", features = ["resolve-config"] } 21 | quote = { version = "1", default-features = false } 22 | 23 | [dependencies.syn] 24 | version = "2.0.59" # for `LitCStr` 25 | default-features = false 26 | features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits"] 27 | 28 | [build-dependencies] 29 | pyo3-build-config = { path = "../pyo3-build-config", version = "=0.25.0" } 30 | 31 | [lints] 32 | workspace = true 33 | 34 | [features] 35 | experimental-async = [] 36 | experimental-inspect = [] 37 | -------------------------------------------------------------------------------- /pyo3-macros-backend/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /pyo3-macros-backend/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /pyo3-macros-backend/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pyo3_build_config::print_expected_cfgs(); 3 | pyo3_build_config::print_feature_cfgs(); 4 | } 5 | -------------------------------------------------------------------------------- /pyo3-macros-backend/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate contains the implementation of the proc macro attributes 2 | 3 | #![warn(elided_lifetimes_in_paths, unused_lifetimes)] 4 | #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] 5 | #![recursion_limit = "1024"] 6 | 7 | // Listed first so that macros in this module are available in the rest of the crate. 8 | #[macro_use] 9 | mod utils; 10 | 11 | mod attributes; 12 | mod derive_attributes; 13 | mod frompyobject; 14 | mod intopyobject; 15 | #[cfg(feature = "experimental-inspect")] 16 | mod introspection; 17 | mod konst; 18 | mod method; 19 | mod module; 20 | mod params; 21 | mod pyclass; 22 | mod pyfunction; 23 | mod pyimpl; 24 | mod pymethod; 25 | mod pyversions; 26 | mod quotes; 27 | 28 | pub use frompyobject::build_derive_from_pyobject; 29 | pub use intopyobject::build_derive_into_pyobject; 30 | pub use module::{pymodule_function_impl, pymodule_module_impl, PyModuleOptions}; 31 | pub use pyclass::{build_py_class, build_py_enum, PyClassArgs}; 32 | pub use pyfunction::{build_py_function, PyFunctionOptions}; 33 | pub use pyimpl::{build_py_methods, PyClassMethodsType}; 34 | pub use utils::get_doc; 35 | -------------------------------------------------------------------------------- /pyo3-macros-backend/src/pyversions.rs: -------------------------------------------------------------------------------- 1 | use pyo3_build_config::PythonVersion; 2 | 3 | pub fn is_abi3_before(major: u8, minor: u8) -> bool { 4 | let config = pyo3_build_config::get(); 5 | config.abi3 && !config.is_free_threaded() && config.version < PythonVersion { major, minor } 6 | } 7 | 8 | pub fn is_py_before(major: u8, minor: u8) -> bool { 9 | let config = pyo3_build_config::get(); 10 | config.version < PythonVersion { major, minor } 11 | } 12 | -------------------------------------------------------------------------------- /pyo3-macros-backend/src/quotes.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::Ctx; 2 | use proc_macro2::TokenStream; 3 | use quote::{quote, quote_spanned}; 4 | 5 | pub(crate) fn some_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream { 6 | let Ctx { pyo3_path, .. } = ctx; 7 | quote! { 8 | #pyo3_path::impl_::wrap::SomeWrap::wrap(#obj) 9 | } 10 | } 11 | 12 | pub(crate) fn ok_wrap(obj: TokenStream, ctx: &Ctx) -> TokenStream { 13 | let Ctx { 14 | pyo3_path, 15 | output_span, 16 | } = ctx; 17 | let pyo3_path = pyo3_path.to_tokens_spanned(*output_span); 18 | quote_spanned! { *output_span => { 19 | let obj = #obj; 20 | #[allow(clippy::useless_conversion)] 21 | #pyo3_path::impl_::wrap::converter(&obj).wrap(obj).map_err(::core::convert::Into::<#pyo3_path::PyErr>::into) 22 | }} 23 | } 24 | 25 | pub(crate) fn map_result_into_ptr(result: TokenStream, ctx: &Ctx) -> TokenStream { 26 | let Ctx { 27 | pyo3_path, 28 | output_span, 29 | } = ctx; 30 | let pyo3_path = pyo3_path.to_tokens_spanned(*output_span); 31 | let py = syn::Ident::new("py", proc_macro2::Span::call_site()); 32 | quote_spanned! { *output_span => { 33 | let result = #result; 34 | #pyo3_path::impl_::wrap::converter(&result).map_into_ptr(#py, result) 35 | }} 36 | } 37 | -------------------------------------------------------------------------------- /pyo3-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pyo3-macros" 3 | version = "0.25.0" 4 | description = "Proc macros for PyO3 package" 5 | authors = ["PyO3 Project and Contributors "] 6 | keywords = ["pyo3", "python", "cpython", "ffi"] 7 | homepage = "https://github.com/pyo3/pyo3" 8 | repository = "https://github.com/pyo3/pyo3" 9 | categories = ["api-bindings", "development-tools::ffi"] 10 | license = "MIT OR Apache-2.0" 11 | edition = "2021" 12 | rust-version = "1.63" 13 | 14 | [lib] 15 | proc-macro = true 16 | 17 | [features] 18 | multiple-pymethods = [] 19 | experimental-async = ["pyo3-macros-backend/experimental-async"] 20 | experimental-inspect = ["pyo3-macros-backend/experimental-inspect"] 21 | 22 | [dependencies] 23 | proc-macro2 = { version = "1.0.60", default-features = false } 24 | quote = "1" 25 | syn = { version = "2", features = ["full", "extra-traits"] } 26 | pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.25.0" } 27 | 28 | [lints] 29 | workspace = true 30 | -------------------------------------------------------------------------------- /pyo3-macros/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /pyo3-macros/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /pyo3-runtime/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /pyo3-runtime/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /pyo3-runtime/README.md: -------------------------------------------------------------------------------- 1 | Coming soon! 2 | -------------------------------------------------------------------------------- /pyo3-runtime/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "pyo3-runtime" 7 | dynamic = ["version"] 8 | description = '' 9 | readme = "README.md" 10 | requires-python = ">=3.7" 11 | license = "MIT OR Apache-2.0" 12 | keywords = [] 13 | authors = [ 14 | { name = "David Hewitt", email = "1939362+davidhewitt@users.noreply.github.com" }, 15 | ] 16 | classifiers = [ 17 | "Development Status :: 4 - Beta", 18 | "Programming Language :: Python", 19 | "Programming Language :: Python :: 3.7", 20 | "Programming Language :: Python :: 3.8", 21 | "Programming Language :: Python :: 3.9", 22 | "Programming Language :: Python :: 3.10", 23 | "Programming Language :: Python :: 3.11", 24 | "Programming Language :: Python :: 3.12", 25 | "Programming Language :: Python :: Implementation :: CPython", 26 | "Programming Language :: Python :: Implementation :: PyPy", 27 | ] 28 | dependencies = [] 29 | 30 | [project.urls] 31 | Homepage = "https://github.com/PyO3/pyo3" 32 | 33 | [tool.hatch.version] 34 | path = "src/pyo3_runtime/__init__.py" 35 | -------------------------------------------------------------------------------- /pyo3-runtime/src/pyo3_runtime/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.0.1" 2 | -------------------------------------------------------------------------------- /pyo3-runtime/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/pyo3/8373f451fdf9caf6b6ed3cb57b576aac067bad07/pyo3-runtime/tests/__init__.py -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.ruff.lint.extend-per-file-ignores] 2 | "__init__.py" = ["F403"] 3 | 4 | [tool.towncrier] 5 | filename = "CHANGELOG.md" 6 | version = "0.25.0" 7 | start_string = "\n" 8 | template = ".towncrier.template.md" 9 | title_format = "## [{version}] - {project_date}" 10 | issue_format = "[#{issue}](https://github.com/PyO3/pyo3/pull/{issue})" # Note PyO3 shows pulls, not issues, in the CHANGELOG 11 | underlines = ["", "", ""] 12 | 13 | [tool.towncrier.fragment.packaging] 14 | name = "Packaging" 15 | 16 | [tool.towncrier.fragment.added] 17 | name = "Added" 18 | 19 | [tool.towncrier.fragment.changed] 20 | name = "Changed" 21 | 22 | [tool.towncrier.fragment.removed] 23 | name = "Removed" 24 | 25 | [tool.towncrier.fragment.fixed] 26 | name = "Fixed" 27 | -------------------------------------------------------------------------------- /pytests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["PyO3 Authors"] 3 | name = "pyo3-pytests" 4 | version = "0.1.0" 5 | description = "Python-based tests for PyO3" 6 | edition = "2021" 7 | publish = false 8 | rust-version = "1.63" 9 | 10 | [dependencies] 11 | pyo3 = { path = "../", features = ["extension-module", "experimental-inspect"] } 12 | 13 | [build-dependencies] 14 | pyo3-build-config = { path = "../pyo3-build-config" } 15 | 16 | [lib] 17 | name = "pyo3_pytests" 18 | crate-type = ["cdylib"] 19 | 20 | [lints] 21 | workspace = true 22 | -------------------------------------------------------------------------------- /pytests/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml Cargo.toml 2 | recursive-include src * 3 | -------------------------------------------------------------------------------- /pytests/README.md: -------------------------------------------------------------------------------- 1 | # pyo3-pytests 2 | 3 | An extension module built using PyO3, used to test and benchmark PyO3 from Python. 4 | 5 | The `stubs` directory contains Python stubs used to test the automated stubs introspection. 6 | To test them run `nox -s test-introspection`. 7 | 8 | ## Testing 9 | 10 | This package is intended to be built using `maturin`. Once built, you can run the tests using `pytest`: 11 | 12 | ```shell 13 | pip install maturin 14 | maturin develop 15 | pytest 16 | ``` 17 | 18 | Alternatively, install nox and run the tests inside an isolated environment: 19 | 20 | ```shell 21 | nox 22 | ``` 23 | 24 | ## Running benchmarks 25 | 26 | You can install the module in your Python environment and then run the benchmarks with pytest: 27 | 28 | ```shell 29 | pip install . 30 | pytest --benchmark-enable 31 | ``` 32 | 33 | Or with nox: 34 | 35 | ```shell 36 | nox -s bench 37 | ``` 38 | -------------------------------------------------------------------------------- /pytests/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | pyo3_build_config::use_pyo3_cfgs(); 3 | pyo3_build_config::add_extension_module_link_args(); 4 | } 5 | -------------------------------------------------------------------------------- /pytests/conftest.py: -------------------------------------------------------------------------------- 1 | import sysconfig 2 | import sys 3 | import pytest 4 | 5 | FREE_THREADED_BUILD = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) 6 | 7 | gil_enabled_at_start = True 8 | if FREE_THREADED_BUILD: 9 | gil_enabled_at_start = sys._is_gil_enabled() 10 | 11 | 12 | def pytest_terminal_summary(terminalreporter, exitstatus, config): 13 | if FREE_THREADED_BUILD and not gil_enabled_at_start and sys._is_gil_enabled(): 14 | tr = terminalreporter 15 | tr.ensure_newline() 16 | tr.section("GIL re-enabled", sep="=", red=True, bold=True) 17 | tr.line("The GIL was re-enabled at runtime during the tests.") 18 | tr.line("") 19 | tr.line("Please ensure all new modules declare support for running") 20 | tr.line("without the GIL. Any new tests that intentionally imports ") 21 | tr.line("code that re-enables the GIL should do so in a subprocess.") 22 | pytest.exit("GIL re-enabled during tests", returncode=1) 23 | -------------------------------------------------------------------------------- /pytests/noxfile.py: -------------------------------------------------------------------------------- 1 | import nox 2 | import sys 3 | from nox.command import CommandFailed 4 | 5 | nox.options.sessions = ["test"] 6 | 7 | 8 | @nox.session 9 | def test(session: nox.Session): 10 | session.env["MATURIN_PEP517_ARGS"] = "--profile=dev" 11 | session.run_always("python", "-m", "pip", "install", "-v", ".[dev]") 12 | 13 | def try_install_binary(package: str, constraint: str): 14 | try: 15 | session.install("--only-binary=:all:", f"{package}{constraint}") 16 | except CommandFailed: 17 | # No binary wheel available on this platform 18 | pass 19 | 20 | try_install_binary("numpy", ">=1.16") 21 | # https://github.com/zopefoundation/zope.interface/issues/316 22 | # - is a dependency of gevent 23 | try_install_binary("zope.interface", "<7") 24 | try_install_binary("gevent", ">=22.10.2") 25 | ignored_paths = [] 26 | if sys.version_info < (3, 10): 27 | # Match syntax is only available in Python >= 3.10 28 | ignored_paths.append("tests/test_enums_match.py") 29 | ignore_args = [f"--ignore={path}" for path in ignored_paths] 30 | session.run("pytest", *ignore_args, *session.posargs) 31 | 32 | 33 | @nox.session 34 | def bench(session: nox.Session): 35 | session.install(".[dev]") 36 | session.run("pytest", "--benchmark-enable", "--benchmark-only", *session.posargs) 37 | -------------------------------------------------------------------------------- /pytests/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [tool.pytest.ini_options] 6 | addopts = "--benchmark-disable" 7 | 8 | [project] 9 | name = "pyo3_pytests" 10 | version = "0.1.0" 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 3 - Alpha", 14 | "Intended Audience :: Developers", 15 | "Programming Language :: Python", 16 | "Programming Language :: Rust", 17 | "Operating System :: POSIX", 18 | "Operating System :: MacOS :: MacOS X", 19 | ] 20 | 21 | [project.optional-dependencies] 22 | dev = [ 23 | "hypothesis>=3.55", 24 | "pytest-asyncio>=0.21", 25 | "pytest-benchmark>=3.4", 26 | # pinned < 8.1 because https://github.com/CodSpeedHQ/pytest-codspeed/issues/27 27 | "pytest>=7,<8.1", 28 | "typing_extensions>=4.0.0" 29 | ] 30 | -------------------------------------------------------------------------------- /pytests/src/consts.rs: -------------------------------------------------------------------------------- 1 | use pyo3::pymodule; 2 | 3 | #[pymodule] 4 | pub mod consts { 5 | #[pymodule_export] 6 | pub const PI: f64 = std::f64::consts::PI; // Exports PI constant as part of the module 7 | 8 | #[pymodule_export] 9 | pub const SIMPLE: &str = "SIMPLE"; 10 | } 11 | -------------------------------------------------------------------------------- /pytests/src/dict_iter.rs: -------------------------------------------------------------------------------- 1 | use pyo3::exceptions::PyRuntimeError; 2 | use pyo3::prelude::*; 3 | use pyo3::types::PyDict; 4 | 5 | #[pymodule] 6 | pub fn dict_iter(m: &Bound<'_, PyModule>) -> PyResult<()> { 7 | m.add_class::()?; 8 | Ok(()) 9 | } 10 | 11 | #[pyclass] 12 | pub struct DictSize { 13 | expected: u32, 14 | } 15 | 16 | #[pymethods] 17 | impl DictSize { 18 | #[new] 19 | fn new(expected: u32) -> Self { 20 | DictSize { expected } 21 | } 22 | 23 | fn iter_dict(&mut self, _py: Python<'_>, dict: &Bound<'_, PyDict>) -> PyResult { 24 | let mut seen = 0u32; 25 | for (sym, values) in dict { 26 | seen += 1; 27 | println!( 28 | "{:4}/{:4} iterations:{}=>{}", 29 | seen, self.expected, sym, values 30 | ); 31 | } 32 | 33 | if seen == self.expected { 34 | Ok(seen) 35 | } else { 36 | Err(PyErr::new::(format!( 37 | "Expected {} iterations - performed {}", 38 | self.expected, seen 39 | ))) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pytests/src/objstore.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | #[derive(Default)] 5 | pub struct ObjStore { 6 | obj: Vec, 7 | } 8 | 9 | #[pymethods] 10 | impl ObjStore { 11 | #[new] 12 | fn new() -> Self { 13 | ObjStore::default() 14 | } 15 | 16 | fn push(&mut self, obj: &Bound<'_, PyAny>) { 17 | self.obj.push(obj.clone().unbind()); 18 | } 19 | } 20 | 21 | #[pymodule(gil_used = false)] 22 | pub fn objstore(m: &Bound<'_, PyModule>) -> PyResult<()> { 23 | m.add_class::() 24 | } 25 | -------------------------------------------------------------------------------- /pytests/src/othermod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! 3 | //! The code below just tries to use the most important code generation paths 4 | 5 | use pyo3::prelude::*; 6 | 7 | #[pyclass] 8 | pub struct ModClass { 9 | _somefield: String, 10 | } 11 | 12 | #[pymethods] 13 | impl ModClass { 14 | #[new] 15 | fn new() -> Self { 16 | ModClass { 17 | _somefield: String::from("contents"), 18 | } 19 | } 20 | 21 | fn noop(&self, x: usize) -> usize { 22 | x 23 | } 24 | } 25 | 26 | #[pyfunction] 27 | fn double(x: i32) -> i32 { 28 | x * 2 29 | } 30 | 31 | #[pymodule(gil_used = false)] 32 | pub fn othermod(m: &Bound<'_, PyModule>) -> PyResult<()> { 33 | m.add_function(wrap_pyfunction!(double, m)?)?; 34 | 35 | m.add_class::()?; 36 | 37 | m.add("USIZE_MIN", usize::MIN)?; 38 | m.add("USIZE_MAX", usize::MAX)?; 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /pytests/src/path.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use std::path::{Path, PathBuf}; 3 | 4 | #[pyfunction] 5 | fn make_path() -> PathBuf { 6 | Path::new("/root").to_owned() 7 | } 8 | 9 | #[pyfunction] 10 | fn take_pathbuf(path: PathBuf) -> PathBuf { 11 | path 12 | } 13 | 14 | #[pymodule(gil_used = false)] 15 | pub fn path(m: &Bound<'_, PyModule>) -> PyResult<()> { 16 | m.add_function(wrap_pyfunction!(make_path, m)?)?; 17 | m.add_function(wrap_pyfunction!(take_pathbuf, m)?)?; 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /pytests/src/sequence.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::PyString; 3 | 4 | #[pyfunction] 5 | fn vec_to_vec_i32(vec: Vec) -> Vec { 6 | vec 7 | } 8 | 9 | #[pyfunction] 10 | fn array_to_array_i32(arr: [i32; 3]) -> [i32; 3] { 11 | arr 12 | } 13 | 14 | #[pyfunction] 15 | fn vec_to_vec_pystring(vec: Vec>) -> Vec> { 16 | vec 17 | } 18 | 19 | #[pymodule(gil_used = false)] 20 | pub fn sequence(m: &Bound<'_, PyModule>) -> PyResult<()> { 21 | m.add_function(wrap_pyfunction!(vec_to_vec_i32, m)?)?; 22 | m.add_function(wrap_pyfunction!(array_to_array_i32, m)?)?; 23 | m.add_function(wrap_pyfunction!(vec_to_vec_pystring, m)?)?; 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /pytests/src/subclassing.rs: -------------------------------------------------------------------------------- 1 | //! Test for [#220](https://github.com/PyO3/pyo3/issues/220) 2 | 3 | use pyo3::prelude::*; 4 | 5 | #[pyclass(subclass)] 6 | pub struct Subclassable {} 7 | 8 | #[pymethods] 9 | impl Subclassable { 10 | #[new] 11 | fn new() -> Self { 12 | Subclassable {} 13 | } 14 | 15 | fn __str__(&self) -> &'static str { 16 | "Subclassable" 17 | } 18 | } 19 | 20 | #[pymodule(gil_used = false)] 21 | pub fn subclassing(m: &Bound<'_, PyModule>) -> PyResult<()> { 22 | m.add_class::()?; 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /pytests/stubs/__init__.pyi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyO3/pyo3/8373f451fdf9caf6b6ed3cb57b576aac067bad07/pytests/stubs/__init__.pyi -------------------------------------------------------------------------------- /pytests/stubs/consts.pyi: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | PI: typing.Final = ... 4 | SIMPLE: typing.Final = "SIMPLE" 5 | -------------------------------------------------------------------------------- /pytests/stubs/pyclasses.pyi: -------------------------------------------------------------------------------- 1 | class AssertingBaseClass: 2 | def __new__(cls, /, expected_type): ... 3 | 4 | class ClassWithDecorators: 5 | def __new__(cls, /): ... 6 | @property 7 | def attr(self, /): ... 8 | @attr.setter 9 | def attr(self, /, value): ... 10 | @classmethod 11 | @property 12 | def cls_attribute(cls, /): ... 13 | @classmethod 14 | def cls_method(cls, /): ... 15 | @staticmethod 16 | def static_method(): ... 17 | 18 | class ClassWithoutConstructor: ... 19 | 20 | class EmptyClass: 21 | def __len__(self, /): ... 22 | def __new__(cls, /): ... 23 | def method(self, /): ... 24 | 25 | class PyClassIter: 26 | def __new__(cls, /): ... 27 | def __next__(self, /): ... 28 | 29 | class PyClassThreadIter: 30 | def __new__(cls, /): ... 31 | def __next__(self, /): ... 32 | -------------------------------------------------------------------------------- /pytests/stubs/pyfunctions.pyi: -------------------------------------------------------------------------------- 1 | def args_kwargs(*args, **kwargs): ... 2 | def none(): ... 3 | def positional_only(a, /, b): ... 4 | def simple(a, b=None, *, c=None): ... 5 | def simple_args(a, b=None, *args, c=None): ... 6 | def simple_args_kwargs(a, b=None, *args, c=None, **kwargs): ... 7 | def simple_kwargs(a, b=None, c=None, **kwargs): ... 8 | def with_typed_args(a=False, b=0, c=0.0, d=""): ... 9 | -------------------------------------------------------------------------------- /pytests/tests/test_awaitable.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import sys 3 | 4 | from pyo3_pytests.awaitable import IterAwaitable, FutureAwaitable 5 | 6 | 7 | @pytest.mark.skipif( 8 | sys.implementation.name == "graalpy", 9 | reason="GraalPy's asyncio module has a bug with native classes, see oracle/graalpython#365", 10 | ) 11 | @pytest.mark.asyncio 12 | async def test_iter_awaitable(): 13 | assert await IterAwaitable(5) == 5 14 | 15 | 16 | @pytest.mark.skipif( 17 | sys.implementation.name == "graalpy", 18 | reason="GraalPy's asyncio module has a bug with native classes, see oracle/graalpython#365", 19 | ) 20 | @pytest.mark.asyncio 21 | async def test_future_awaitable(): 22 | assert await FutureAwaitable(5) == 5 23 | -------------------------------------------------------------------------------- /pytests/tests/test_buf_and_str.py: -------------------------------------------------------------------------------- 1 | from pyo3_pytests.buf_and_str import BytesExtractor, return_memoryview 2 | 3 | 4 | def test_extract_bytes(): 5 | extractor = BytesExtractor() 6 | message = b'\\(-"-;) A message written in bytes' 7 | assert extractor.from_bytes(message) == len(message) 8 | 9 | 10 | def test_extract_str(): 11 | extractor = BytesExtractor() 12 | message = '\\(-"-;) A message written as a string' 13 | assert extractor.from_str(message) == len(message) 14 | 15 | 16 | def test_extract_str_lossy(): 17 | extractor = BytesExtractor() 18 | message = '\\(-"-;) A message written with a trailing surrogate \ud800' 19 | rust_surrogate_len = extractor.from_str_lossy("\ud800") 20 | assert extractor.from_str_lossy(message) == len(message) - 1 + rust_surrogate_len 21 | 22 | 23 | def test_extract_buffer(): 24 | extractor = BytesExtractor() 25 | message = b'\\(-"-;) A message written in bytes' 26 | assert extractor.from_buffer(message) == len(message) 27 | 28 | arr = bytearray(b'\\(-"-;) A message written in bytes') 29 | assert extractor.from_buffer(arr) == len(arr) 30 | 31 | 32 | def test_return_memoryview(): 33 | view = return_memoryview() 34 | assert view.readonly 35 | assert view.contiguous 36 | assert view.tobytes() == b"hello world" 37 | -------------------------------------------------------------------------------- /pytests/tests/test_dict_iter.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pyo3_pytests.dict_iter import DictSize 3 | 4 | 5 | @pytest.mark.parametrize("size", [64, 128, 256]) 6 | def test_size(size): 7 | d = {} 8 | for i in range(size): 9 | d[i] = str(i) 10 | assert DictSize(len(d)).iter_dict(d) == size 11 | -------------------------------------------------------------------------------- /pytests/tests/test_hammer_gil_in_thread.py: -------------------------------------------------------------------------------- 1 | import sysconfig 2 | 3 | import pytest 4 | 5 | from pyo3_pytests import misc 6 | 7 | 8 | def make_loop(): 9 | # create a reference loop that will only be destroyed when the GC is called at the end 10 | # of execution 11 | start = [] 12 | cur = [start] 13 | for _ in range(1000 * 1000 * 10): 14 | cur = [cur] 15 | start.append(cur) 16 | return start 17 | 18 | 19 | # set a bomb that will explode when modules are cleaned up 20 | loopy = [make_loop()] 21 | 22 | 23 | @pytest.mark.skipif( 24 | sysconfig.get_config_var("Py_DEBUG"), 25 | reason="causes a crash on debug builds, see discussion in https://github.com/PyO3/pyo3/pull/4874", 26 | ) 27 | def test_hammer_gil(): 28 | loopy.append(misc.hammer_gil_in_thread()) 29 | -------------------------------------------------------------------------------- /pytests/tests/test_objstore.py: -------------------------------------------------------------------------------- 1 | import gc 2 | import sys 3 | 4 | from pyo3_pytests.objstore import ObjStore 5 | 6 | 7 | def test_objstore_doesnot_leak_memory(): 8 | N = 10000 9 | message = b'\\(-"-;) Praying that memory leak would not happen..' 10 | 11 | # PyPy does not have sys.getrefcount, provide a no-op lambda and don't 12 | # check refcount on PyPy 13 | getrefcount = getattr(sys, "getrefcount", lambda obj: 0) 14 | 15 | if sys.implementation.name == "graalpy": 16 | # GraalPy has an incomplete sys.getrefcount implementation 17 | def getrefcount(obj): 18 | return 0 19 | 20 | before = getrefcount(message) 21 | store = ObjStore() 22 | for _ in range(N): 23 | store.push(message) 24 | del store 25 | gc.collect() 26 | after = getrefcount(message) 27 | 28 | assert after - before == 0 29 | -------------------------------------------------------------------------------- /pytests/tests/test_othermod.py: -------------------------------------------------------------------------------- 1 | from hypothesis import given, assume 2 | from hypothesis import strategies as st 3 | 4 | from pyo3_pytests import othermod 5 | 6 | INTEGER31_ST = st.integers(min_value=(-(2**30)), max_value=(2**30 - 1)) 7 | USIZE_ST = st.integers(min_value=othermod.USIZE_MIN, max_value=othermod.USIZE_MAX) 8 | 9 | 10 | # If the full 32 bits are used here, then you can get failures that look like this: 11 | # hypothesis.errors.FailedHealthCheck: It looks like your strategy is filtering out a lot of data. 12 | # Health check found 50 filtered examples but only 7 good ones. 13 | # 14 | # Limit the range to 31 bits to avoid this problem. 15 | @given(x=INTEGER31_ST) 16 | def test_double(x): 17 | expected = x * 2 18 | assume(-(2**31) <= expected <= (2**31 - 1)) 19 | assert othermod.double(x) == expected 20 | 21 | 22 | def test_modclass(): 23 | # Test that the repr of the class itself doesn't crash anything 24 | repr(othermod.ModClass) 25 | 26 | assert isinstance(othermod.ModClass, type) 27 | 28 | 29 | def test_modclass_instance(): 30 | mi = othermod.ModClass() 31 | 32 | repr(mi) 33 | repr(mi.__class__) 34 | 35 | assert isinstance(mi, othermod.ModClass) 36 | assert isinstance(mi, object) 37 | 38 | 39 | @given(x=USIZE_ST) 40 | def test_modclas_noop(x): 41 | mi = othermod.ModClass() 42 | 43 | assert mi.noop(x) == x 44 | -------------------------------------------------------------------------------- /pytests/tests/test_path.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | import pytest 4 | 5 | import pyo3_pytests.path as rpath 6 | 7 | 8 | def test_make_path(): 9 | p = rpath.make_path() 10 | assert p == pathlib.Path("/root") 11 | 12 | 13 | def test_take_pathbuf(): 14 | p = "/root" 15 | assert rpath.take_pathbuf(p) == pathlib.Path(p) 16 | 17 | 18 | def test_take_pathlib(): 19 | p = pathlib.Path("/root") 20 | assert rpath.take_pathbuf(p) == p 21 | 22 | 23 | def test_take_pathlike(): 24 | assert rpath.take_pathbuf(PathLike("/root")) == pathlib.Path("/root") 25 | 26 | 27 | def test_take_invalid_pathlike(): 28 | with pytest.raises(TypeError): 29 | assert rpath.take_pathbuf(PathLike(1)) 30 | 31 | 32 | def test_take_invalid(): 33 | with pytest.raises(TypeError): 34 | assert rpath.take_pathbuf(3) 35 | 36 | 37 | class PathLike: 38 | def __init__(self, path): 39 | self._path = path 40 | 41 | def __fspath__(self): 42 | return self._path 43 | -------------------------------------------------------------------------------- /pytests/tests/test_sequence.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from pyo3_pytests import sequence 4 | 5 | 6 | def test_vec_from_list_i32(): 7 | assert sequence.vec_to_vec_i32([1, 2, 3]) == [1, 2, 3] 8 | 9 | 10 | def test_vec_from_list_pystring(): 11 | assert sequence.vec_to_vec_pystring(["1", "2", "3"]) == ["1", "2", "3"] 12 | 13 | 14 | def test_vec_from_bytes(): 15 | assert sequence.vec_to_vec_i32(b"123") == [49, 50, 51] 16 | 17 | 18 | def test_vec_from_str(): 19 | with pytest.raises(TypeError): 20 | sequence.vec_to_vec_pystring("123") 21 | 22 | 23 | def test_vec_from_array(): 24 | # binary numpy wheel not available on all platforms 25 | numpy = pytest.importorskip("numpy") 26 | 27 | assert sequence.vec_to_vec_i32(numpy.array([1, 2, 3])) == [1, 2, 3] 28 | 29 | 30 | def test_rust_array_from_array(): 31 | # binary numpy wheel not available on all platforms 32 | numpy = pytest.importorskip("numpy") 33 | 34 | assert sequence.array_to_array_i32(numpy.array([1, 2, 3])) == [1, 2, 3] 35 | -------------------------------------------------------------------------------- /pytests/tests/test_subclassing.py: -------------------------------------------------------------------------------- 1 | from pyo3_pytests.subclassing import Subclassable 2 | 3 | 4 | class SomeSubClass(Subclassable): 5 | def __str__(self): 6 | return "SomeSubclass" 7 | 8 | 9 | def test_subclassing(): 10 | a = SomeSubClass() 11 | assert str(a) == "SomeSubclass" 12 | assert type(a) is SomeSubClass 13 | -------------------------------------------------------------------------------- /src/conversions/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains conversions between various Rust object and their representation in Python. 2 | 3 | pub mod anyhow; 4 | pub mod bigdecimal; 5 | pub mod chrono; 6 | pub mod chrono_tz; 7 | pub mod either; 8 | pub mod eyre; 9 | pub mod hashbrown; 10 | pub mod indexmap; 11 | pub mod jiff; 12 | pub mod num_bigint; 13 | pub mod num_complex; 14 | pub mod num_rational; 15 | pub mod ordered_float; 16 | pub mod rust_decimal; 17 | pub mod serde; 18 | pub mod smallvec; 19 | mod std; 20 | pub mod time; 21 | pub mod uuid; 22 | -------------------------------------------------------------------------------- /src/conversions/std/cell.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | 3 | use crate::{ 4 | conversion::IntoPyObject, types::any::PyAnyMethods, Bound, FromPyObject, PyAny, PyResult, 5 | Python, 6 | }; 7 | 8 | impl<'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for Cell { 9 | type Target = T::Target; 10 | type Output = T::Output; 11 | type Error = T::Error; 12 | 13 | #[inline] 14 | fn into_pyobject(self, py: Python<'py>) -> Result { 15 | self.get().into_pyobject(py) 16 | } 17 | } 18 | 19 | impl<'py, T: Copy + IntoPyObject<'py>> IntoPyObject<'py> for &Cell { 20 | type Target = T::Target; 21 | type Output = T::Output; 22 | type Error = T::Error; 23 | 24 | #[inline] 25 | fn into_pyobject(self, py: Python<'py>) -> Result { 26 | self.get().into_pyobject(py) 27 | } 28 | } 29 | 30 | impl<'py, T: FromPyObject<'py>> FromPyObject<'py> for Cell { 31 | fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { 32 | ob.extract().map(Cell::new) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/conversions/std/mod.rs: -------------------------------------------------------------------------------- 1 | mod array; 2 | mod cell; 3 | mod ipaddr; 4 | mod map; 5 | mod num; 6 | mod option; 7 | mod osstr; 8 | mod path; 9 | mod set; 10 | mod slice; 11 | mod string; 12 | mod time; 13 | mod vec; 14 | -------------------------------------------------------------------------------- /src/ffi/mod.rs: -------------------------------------------------------------------------------- 1 | //! Raw FFI declarations for Python's C API. 2 | //! 3 | //! This module provides low level bindings to the Python interpreter. 4 | //! It is meant for advanced users only - regular PyO3 users shouldn't 5 | //! need to interact with this module at all. 6 | //! 7 | //! The contents of this module are not documented here, as it would entail 8 | //! basically copying the documentation from CPython. Consult the [Python/C API Reference 9 | //! Manual][capi] for up-to-date documentation. 10 | //! 11 | //! # Safety 12 | //! 13 | //! The functions in this module lack individual safety documentation, but 14 | //! generally the following apply: 15 | //! - Pointer arguments have to point to a valid Python object of the correct type, 16 | //! although null pointers are sometimes valid input. 17 | //! - The vast majority can only be used safely while the GIL is held. 18 | //! - Some functions have additional safety requirements, consult the 19 | //! [Python/C API Reference Manual][capi] for more information. 20 | //! 21 | //! [capi]: https://docs.python.org/3/c-api/index.html 22 | 23 | #[cfg(test)] 24 | mod tests; 25 | 26 | // reexport raw bindings exposed in pyo3_ffi 27 | pub use pyo3_ffi::*; 28 | 29 | /// Helper to enable #\[pymethods\] to see the workaround for __ipow__ on Python 3.7 30 | #[doc(hidden)] 31 | pub use crate::impl_::pymethods::ipowfunc; 32 | -------------------------------------------------------------------------------- /src/impl_.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | //! Internals of PyO3 which are accessed by code expanded from PyO3's procedural macros. 4 | //! 5 | //! Usage of any of these APIs in downstream code is implicitly acknowledging that these 6 | //! APIs may may change at any time without documentation in the CHANGELOG and without 7 | //! breaking semver guarantees. 8 | 9 | pub mod callback; 10 | #[cfg(feature = "experimental-inspect")] 11 | pub mod concat; 12 | #[cfg(feature = "experimental-async")] 13 | pub mod coroutine; 14 | pub mod exceptions; 15 | pub mod extract_argument; 16 | pub mod freelist; 17 | pub mod frompyobject; 18 | pub(crate) mod not_send; 19 | pub mod panic; 20 | pub mod pycell; 21 | pub mod pyclass; 22 | pub mod pyclass_init; 23 | pub mod pyfunction; 24 | pub mod pymethods; 25 | pub mod pymodule; 26 | #[doc(hidden)] 27 | pub mod trampoline; 28 | pub mod wrap; 29 | -------------------------------------------------------------------------------- /src/impl_/concat.rs: -------------------------------------------------------------------------------- 1 | /// `concat!` but working with constants 2 | #[macro_export] 3 | #[doc(hidden)] 4 | macro_rules! const_concat { 5 | ($e:expr) => {{ 6 | $e 7 | }}; 8 | ($l:expr, $($r:expr),+ $(,)?) => {{ 9 | const L: &'static str = $l; 10 | const R: &'static str = $crate::impl_::concat::const_concat!($($r),*); 11 | const LEN: usize = L.len() + R.len(); 12 | const fn combine(l: &'static [u8], r: &'static [u8]) -> [u8; LEN] { 13 | let mut out = [0u8; LEN]; 14 | let mut i = 0; 15 | while i < l.len() { 16 | out[i] = l[i]; 17 | i += 1; 18 | } 19 | while i < LEN { 20 | out[i] = r[i - l.len()]; 21 | i += 1; 22 | } 23 | out 24 | } 25 | #[allow(unsafe_code)] 26 | unsafe { ::std::str::from_utf8_unchecked(&combine(L.as_bytes(), R.as_bytes())) } 27 | }} 28 | } 29 | 30 | pub use const_concat; 31 | -------------------------------------------------------------------------------- /src/impl_/exceptions.rs: -------------------------------------------------------------------------------- 1 | use crate::{sync::GILOnceCell, types::PyType, Bound, Py, Python}; 2 | 3 | pub struct ImportedExceptionTypeObject { 4 | imported_value: GILOnceCell>, 5 | module: &'static str, 6 | name: &'static str, 7 | } 8 | 9 | impl ImportedExceptionTypeObject { 10 | pub const fn new(module: &'static str, name: &'static str) -> Self { 11 | Self { 12 | imported_value: GILOnceCell::new(), 13 | module, 14 | name, 15 | } 16 | } 17 | 18 | pub fn get<'py>(&self, py: Python<'py>) -> &Bound<'py, PyType> { 19 | self.imported_value 20 | .import(py, self.module, self.name) 21 | .unwrap_or_else(|e| { 22 | panic!( 23 | "failed to import exception {}.{}: {}", 24 | self.module, self.name, e 25 | ) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/impl_/not_send.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::Python; 4 | 5 | /// A marker type that makes the type !Send. 6 | /// Workaround for lack of !Send on stable (). 7 | pub(crate) struct NotSend(PhantomData<*mut Python<'static>>); 8 | -------------------------------------------------------------------------------- /src/impl_/panic.rs: -------------------------------------------------------------------------------- 1 | /// Type which will panic if dropped. 2 | /// 3 | /// If this is dropped during a panic, this will cause an abort. 4 | /// 5 | /// Use this to avoid letting unwinds cross through the FFI boundary, which is UB. 6 | pub struct PanicTrap { 7 | msg: &'static str, 8 | } 9 | 10 | impl PanicTrap { 11 | #[inline] 12 | pub const fn new(msg: &'static str) -> Self { 13 | Self { msg } 14 | } 15 | 16 | #[inline] 17 | pub const fn disarm(self) { 18 | std::mem::forget(self) 19 | } 20 | } 21 | 22 | impl Drop for PanicTrap { 23 | fn drop(&mut self) { 24 | // Panic here will abort the process, assuming in an unwind. 25 | panic!("{}", self.msg) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/impl_/pycell.rs: -------------------------------------------------------------------------------- 1 | //! Externally-accessible implementation of pycell 2 | pub use crate::pycell::impl_::{ 3 | GetBorrowChecker, PyClassMutability, PyClassObject, PyClassObjectBase, PyClassObjectLayout, 4 | }; 5 | -------------------------------------------------------------------------------- /src/impl_/pyclass/assertions.rs: -------------------------------------------------------------------------------- 1 | /// Helper function that can be used at compile time to emit a diagnostic if 2 | /// the type does not implement `Sync` when it should. 3 | /// 4 | /// The mere act of invoking this function will cause the diagnostic to be 5 | /// emitted if `T` does not implement `Sync` when it should. 6 | /// 7 | /// The additional `const IS_SYNC: bool` parameter is used to allow the custom 8 | /// diagnostic to be emitted; if `PyClassSync` 9 | #[allow(unused)] 10 | pub const fn assert_pyclass_sync() 11 | where 12 | T: PyClassSync + Sync, 13 | { 14 | } 15 | 16 | #[cfg_attr( 17 | diagnostic_namespace, 18 | diagnostic::on_unimplemented( 19 | message = "the trait `Sync` is not implemented for `{Self}`", 20 | label = "required by `#[pyclass]`", 21 | note = "replace thread-unsafe fields with thread-safe alternatives", 22 | note = "see for more information", 23 | ) 24 | )] 25 | pub trait PyClassSync {} 26 | 27 | impl PyClassSync for T where T: Sync {} 28 | 29 | mod tests { 30 | #[cfg(feature = "macros")] 31 | #[test] 32 | fn test_assert_pyclass_sync() { 33 | use super::assert_pyclass_sync; 34 | 35 | #[crate::pyclass(crate = "crate")] 36 | struct MyClass {} 37 | 38 | assert_pyclass_sync::(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/inspect/mod.rs: -------------------------------------------------------------------------------- 1 | //! Runtime inspection of objects exposed to Python. 2 | //! 3 | //! Tracking issue: . 4 | pub mod types; 5 | -------------------------------------------------------------------------------- /src/internal.rs: -------------------------------------------------------------------------------- 1 | //! Holding place for code which is not intended to be reachable from outside of PyO3. 2 | 3 | pub(crate) mod get_slot; 4 | -------------------------------------------------------------------------------- /src/panic.rs: -------------------------------------------------------------------------------- 1 | //! Helper to convert Rust panics to Python exceptions. 2 | use crate::exceptions::PyBaseException; 3 | use crate::PyErr; 4 | use std::any::Any; 5 | 6 | pyo3_exception!( 7 | " 8 | The exception raised when Rust code called from Python panics. 9 | 10 | Like SystemExit, this exception is derived from BaseException so that 11 | it will typically propagate all the way through the stack and cause the 12 | Python interpreter to exit. 13 | ", 14 | PanicException, 15 | PyBaseException 16 | ); 17 | 18 | impl PanicException { 19 | /// Creates a new PanicException from a panic payload. 20 | /// 21 | /// Attempts to format the error in the same way panic does. 22 | #[cold] 23 | pub(crate) fn from_panic_payload(payload: Box) -> PyErr { 24 | if let Some(string) = payload.downcast_ref::() { 25 | Self::new_err((string.clone(),)) 26 | } else if let Some(s) = payload.downcast_ref::<&str>() { 27 | Self::new_err((s.to_string(),)) 28 | } else { 29 | Self::new_err(("panic from Rust code",)) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/py_result_ext.rs: -------------------------------------------------------------------------------- 1 | use crate::{types::any::PyAnyMethods, Bound, PyAny, PyResult, PyTypeCheck}; 2 | 3 | pub(crate) trait PyResultExt<'py>: crate::sealed::Sealed { 4 | fn downcast_into(self) -> PyResult>; 5 | unsafe fn downcast_into_unchecked(self) -> PyResult>; 6 | } 7 | 8 | impl<'py> PyResultExt<'py> for PyResult> { 9 | #[inline] 10 | fn downcast_into(self) -> PyResult> where { 11 | self.and_then(|instance| instance.downcast_into().map_err(Into::into)) 12 | } 13 | 14 | #[inline] 15 | unsafe fn downcast_into_unchecked(self) -> PyResult> { 16 | self.map(|instance| unsafe { instance.downcast_into_unchecked() }) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test_utils.rs: -------------------------------------------------------------------------------- 1 | use crate as pyo3; 2 | include!("../tests/common.rs"); 3 | -------------------------------------------------------------------------------- /src/tests/hygiene/mod.rs: -------------------------------------------------------------------------------- 1 | #![no_implicit_prelude] 2 | #![allow(dead_code, unused_variables, clippy::unnecessary_wraps)] 3 | 4 | // The modules in this test are used to check PyO3 macro expansion is hygienic. By locating the test 5 | // inside the crate the global `::pyo3` namespace is not available, so in combination with 6 | // #[pyo3(crate = "crate")] this validates that all macro expansion respects the setting. 7 | 8 | mod misc; 9 | mod pyclass; 10 | mod pyfunction; 11 | mod pymethods; 12 | mod pymodule; 13 | -------------------------------------------------------------------------------- /src/tests/hygiene/pyfunction.rs: -------------------------------------------------------------------------------- 1 | #[crate::pyfunction] 2 | #[pyo3(crate = "crate")] 3 | fn do_something(x: i32) -> crate::PyResult { 4 | ::std::result::Result::Ok(x) 5 | } 6 | 7 | #[crate::pyfunction] 8 | #[pyo3(crate = "crate", name = "check5012")] 9 | fn check_5012(x: i32) -> crate::PyResult { 10 | ::std::result::Result::Ok(x) 11 | } 12 | 13 | #[crate::pyfunction] 14 | #[pyo3(crate = "crate")] 15 | #[pyo3(warn(message = "This is a warning message"))] 16 | fn function_with_warning() {} 17 | 18 | #[crate::pyfunction(crate = "crate")] 19 | #[pyo3(warn(message = "This is a warning message with custom category", category = crate::exceptions::PyFutureWarning))] 20 | fn function_with_warning_and_category() {} 21 | 22 | #[crate::pyfunction(crate = "crate")] 23 | #[pyo3(warn(message = "This is a warning message"))] 24 | #[pyo3(warn(message = "This is another warning message", category = crate::exceptions::PyFutureWarning))] 25 | fn multiple_warning_function() {} 26 | 27 | #[test] 28 | fn invoke_wrap_pyfunction() { 29 | crate::Python::with_gil(|py| { 30 | let func = crate::wrap_pyfunction!(do_something, py).unwrap(); 31 | crate::py_run!(py, func, r#"func(5)"#); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /src/tests/hygiene/pymodule.rs: -------------------------------------------------------------------------------- 1 | #[crate::pyfunction] 2 | #[pyo3(crate = "crate")] 3 | fn do_something(x: i32) -> crate::PyResult { 4 | ::std::result::Result::Ok(x) 5 | } 6 | 7 | #[crate::pymodule] 8 | #[pyo3(crate = "crate")] 9 | fn foo( 10 | _py: crate::Python<'_>, 11 | _m: &crate::Bound<'_, crate::types::PyModule>, 12 | ) -> crate::PyResult<()> { 13 | ::std::result::Result::Ok(()) 14 | } 15 | 16 | #[crate::pymodule] 17 | #[pyo3(crate = "crate")] 18 | fn my_module(m: &crate::Bound<'_, crate::types::PyModule>) -> crate::PyResult<()> { 19 | as crate::types::PyModuleMethods>::add_function( 20 | m, 21 | crate::wrap_pyfunction!(do_something, m)?, 22 | )?; 23 | as crate::types::PyModuleMethods>::add_wrapped( 24 | m, 25 | crate::wrap_pymodule!(foo), 26 | )?; 27 | 28 | ::std::result::Result::Ok(()) 29 | } 30 | 31 | #[crate::pymodule(submodule)] 32 | #[pyo3(crate = "crate")] 33 | mod my_module_declarative { 34 | #[pymodule_export] 35 | use super::{do_something, foo}; 36 | 37 | #[pymodule_export] 38 | const BAR: u32 = 42; 39 | } 40 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub(crate) mod common { 3 | #[cfg(not(Py_GIL_DISABLED))] 4 | use crate as pyo3; 5 | include!("./common.rs"); 6 | } 7 | 8 | /// Test macro hygiene - this is in the crate since we won't have 9 | /// `pyo3` available in the crate root. 10 | #[cfg(all(test, feature = "macros"))] 11 | mod hygiene; 12 | -------------------------------------------------------------------------------- /src/types/code.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use crate::PyAny; 3 | 4 | /// Represents a Python code object. 5 | /// 6 | /// Values of this type are accessed via PyO3's smart pointers, e.g. as 7 | /// [`Py`][crate::Py] or [`Bound<'py, PyCode>`][crate::Bound]. 8 | #[repr(transparent)] 9 | pub struct PyCode(PyAny); 10 | 11 | pyobject_native_type_core!( 12 | PyCode, 13 | pyobject_native_static_type_object!(ffi::PyCode_Type), 14 | #checkfunction=ffi::PyCode_Check 15 | ); 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use super::*; 20 | use crate::types::PyTypeMethods; 21 | use crate::{PyTypeInfo, Python}; 22 | 23 | #[test] 24 | fn test_type_object() { 25 | Python::with_gil(|py| { 26 | assert_eq!(PyCode::type_object(py).name().unwrap(), "code"); 27 | }) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/types/frame.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use crate::PyAny; 3 | 4 | /// Represents a Python frame. 5 | /// 6 | /// Values of this type are accessed via PyO3's smart pointers, e.g. as 7 | /// [`Py`][crate::Py] or [`Bound<'py, PyFrame>`][crate::Bound]. 8 | #[repr(transparent)] 9 | pub struct PyFrame(PyAny); 10 | 11 | pyobject_native_type_core!( 12 | PyFrame, 13 | pyobject_native_static_type_object!(ffi::PyFrame_Type), 14 | #checkfunction=ffi::PyFrame_Check 15 | ); 16 | -------------------------------------------------------------------------------- /src/types/memoryview.rs: -------------------------------------------------------------------------------- 1 | use crate::err::PyResult; 2 | use crate::ffi_ptr_ext::FfiPtrExt; 3 | use crate::py_result_ext::PyResultExt; 4 | use crate::{ffi, Bound, PyAny}; 5 | 6 | /// Represents a Python `memoryview`. 7 | /// 8 | /// Values of this type are accessed via PyO3's smart pointers, e.g. as 9 | /// [`Py`][crate::Py] or [`Bound<'py, PyMemoryView>`][Bound]. 10 | #[repr(transparent)] 11 | pub struct PyMemoryView(PyAny); 12 | 13 | pyobject_native_type_core!(PyMemoryView, pyobject_native_static_type_object!(ffi::PyMemoryView_Type), #checkfunction=ffi::PyMemoryView_Check); 14 | 15 | impl PyMemoryView { 16 | /// Creates a new Python `memoryview` object from another Python object that 17 | /// implements the buffer protocol. 18 | pub fn from<'py>(src: &Bound<'py, PyAny>) -> PyResult> { 19 | unsafe { 20 | ffi::PyMemoryView_FromObject(src.as_ptr()) 21 | .assume_owned_or_err(src.py()) 22 | .downcast_into_unchecked() 23 | } 24 | } 25 | } 26 | 27 | impl<'py> TryFrom<&Bound<'py, PyAny>> for Bound<'py, PyMemoryView> { 28 | type Error = crate::PyErr; 29 | 30 | /// Creates a new Python `memoryview` object from another Python object that 31 | /// implements the buffer protocol. 32 | fn try_from(value: &Bound<'py, PyAny>) -> Result { 33 | PyMemoryView::from(value) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/types/weakref/mod.rs: -------------------------------------------------------------------------------- 1 | pub use anyref::{PyWeakref, PyWeakrefMethods}; 2 | pub use proxy::PyWeakrefProxy; 3 | pub use reference::PyWeakrefReference; 4 | 5 | pub(crate) mod anyref; 6 | pub(crate) mod proxy; 7 | pub(crate) mod reference; 8 | -------------------------------------------------------------------------------- /tests/test_anyhow.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "anyhow")] 2 | 3 | use pyo3::{ffi, wrap_pyfunction}; 4 | 5 | #[test] 6 | fn test_anyhow_py_function_ok_result() { 7 | use pyo3::{py_run, pyfunction, Python}; 8 | 9 | #[pyfunction] 10 | #[allow(clippy::unnecessary_wraps)] 11 | fn produce_ok_result() -> anyhow::Result { 12 | Ok(String::from("OK buddy")) 13 | } 14 | 15 | Python::with_gil(|py| { 16 | let func = wrap_pyfunction!(produce_ok_result)(py).unwrap(); 17 | 18 | py_run!( 19 | py, 20 | func, 21 | r#" 22 | func() 23 | "# 24 | ); 25 | }); 26 | } 27 | 28 | #[test] 29 | fn test_anyhow_py_function_err_result() { 30 | use pyo3::prelude::PyDictMethods; 31 | use pyo3::{pyfunction, types::PyDict, Python}; 32 | 33 | #[pyfunction] 34 | fn produce_err_result() -> anyhow::Result { 35 | anyhow::bail!("error time") 36 | } 37 | 38 | Python::with_gil(|py| { 39 | let func = wrap_pyfunction!(produce_err_result)(py).unwrap(); 40 | let locals = PyDict::new(py); 41 | locals.set_item("func", func).unwrap(); 42 | 43 | py.run( 44 | ffi::c_str!( 45 | r#" 46 | func() 47 | "# 48 | ), 49 | None, 50 | Some(&locals), 51 | ) 52 | .unwrap_err(); 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /tests/test_datetime_import.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(Py_LIMITED_API))] 2 | 3 | use pyo3::{prelude::*, types::PyDate}; 4 | use tempfile::Builder; 5 | 6 | #[test] 7 | #[should_panic(expected = "module 'datetime' has no attribute 'datetime_CAPI'")] 8 | fn test_bad_datetime_module_panic() { 9 | // Create an empty temporary directory 10 | // with an empty "datetime" module which we'll put on the sys.path 11 | let tmpdir = Builder::new() 12 | .prefix("pyo3_test_data_check") 13 | .tempdir() 14 | .unwrap(); 15 | std::fs::File::create(tmpdir.path().join("datetime.py")).unwrap(); 16 | 17 | Python::with_gil(|py: Python<'_>| { 18 | let sys = py.import("sys").unwrap(); 19 | sys.getattr("path") 20 | .unwrap() 21 | .call_method1("insert", (0, tmpdir.path().as_os_str())) 22 | .unwrap(); 23 | 24 | // This should panic because the "datetime" module is empty 25 | PyDate::new(py, 2018, 1, 1).unwrap(); 26 | }); 27 | tmpdir.close().unwrap(); 28 | } 29 | -------------------------------------------------------------------------------- /tests/test_default_impls.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | 3 | use pyo3::prelude::*; 4 | 5 | #[path = "../src/tests/common.rs"] 6 | mod common; 7 | 8 | // Test default generated __repr__. 9 | #[pyclass(eq, eq_int)] 10 | #[derive(PartialEq)] 11 | enum TestDefaultRepr { 12 | Var, 13 | } 14 | 15 | #[test] 16 | fn test_default_slot_exists() { 17 | Python::with_gil(|py| { 18 | let test_object = Py::new(py, TestDefaultRepr::Var).unwrap(); 19 | py_assert!( 20 | py, 21 | test_object, 22 | "repr(test_object) == 'TestDefaultRepr.Var'" 23 | ); 24 | }) 25 | } 26 | 27 | #[pyclass(eq, eq_int)] 28 | #[derive(PartialEq)] 29 | enum OverrideSlot { 30 | Var, 31 | } 32 | 33 | #[pymethods] 34 | impl OverrideSlot { 35 | fn __repr__(&self) -> &str { 36 | "overridden" 37 | } 38 | } 39 | 40 | #[test] 41 | fn test_override_slot() { 42 | Python::with_gil(|py| { 43 | let test_object = Py::new(py, OverrideSlot::Var).unwrap(); 44 | py_assert!(py, test_object, "repr(test_object) == 'overridden'"); 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /tests/test_field_cfg.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | 3 | use pyo3::prelude::*; 4 | 5 | #[pyclass] 6 | struct CfgClass { 7 | #[pyo3(get, set)] 8 | #[cfg(any())] 9 | pub a: u32, 10 | #[pyo3(get, set)] 11 | // This is always true 12 | #[cfg(any( 13 | target_family = "unix", 14 | target_family = "windows", 15 | target_family = "wasm" 16 | ))] 17 | pub b: u32, 18 | } 19 | 20 | #[pyclass(eq, eq_int)] 21 | #[derive(PartialEq)] 22 | enum CfgSimpleEnum { 23 | #[cfg(any())] 24 | DisabledVariant, 25 | #[cfg(not(any()))] 26 | EnabledVariant, 27 | } 28 | 29 | #[test] 30 | fn test_cfg() { 31 | Python::with_gil(|py| { 32 | let cfg = CfgClass { b: 3 }; 33 | let py_cfg = Py::new(py, cfg).unwrap(); 34 | assert!(py_cfg.bind(py).getattr("a").is_err()); 35 | let b: u32 = py_cfg.bind(py).getattr("b").unwrap().extract().unwrap(); 36 | assert_eq!(b, 3); 37 | }); 38 | } 39 | 40 | #[test] 41 | fn test_cfg_simple_enum() { 42 | Python::with_gil(|py| { 43 | let simple = py.get_type::(); 44 | pyo3::py_run!( 45 | py, 46 | simple, 47 | r#" 48 | assert hasattr(simple, "EnabledVariant") 49 | assert not hasattr(simple, "DisabledVariant") 50 | "# 51 | ); 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_macro_docs.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | 3 | use pyo3::prelude::*; 4 | use pyo3::types::IntoPyDict; 5 | 6 | #[macro_use] 7 | #[path = "../src/tests/common.rs"] 8 | mod common; 9 | 10 | #[pyclass] 11 | /// The MacroDocs class. 12 | #[doc = concat!("Some macro ", "class ", "docs.")] 13 | /// A very interesting type! 14 | struct MacroDocs {} 15 | 16 | #[pymethods] 17 | impl MacroDocs { 18 | #[doc = concat!("A macro ", "example.")] 19 | /// With mixed doc types. 20 | fn macro_doc(&self) {} 21 | } 22 | 23 | #[test] 24 | fn meth_doc() { 25 | Python::with_gil(|py| { 26 | let d = [("C", py.get_type::())] 27 | .into_py_dict(py) 28 | .unwrap(); 29 | py_assert!( 30 | py, 31 | *d, 32 | "C.__doc__ == 'The MacroDocs class.\\nSome macro class docs.\\nA very interesting type!'" 33 | ); 34 | py_assert!( 35 | py, 36 | *d, 37 | "C.macro_doc.__doc__ == 'A macro example.\\nWith mixed doc types.'" 38 | ); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /tests/test_string.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | 3 | use pyo3::prelude::*; 4 | 5 | #[path = "../src/tests/common.rs"] 6 | mod common; 7 | 8 | #[pyfunction] 9 | fn take_str(_s: &str) {} 10 | 11 | #[test] 12 | fn test_unicode_encode_error() { 13 | Python::with_gil(|py| { 14 | let take_str = wrap_pyfunction!(take_str)(py).unwrap(); 15 | py_expect_exception!( 16 | py, 17 | take_str, 18 | "take_str('\\ud800')", 19 | PyUnicodeEncodeError, 20 | "'utf-8' codec can't encode character '\\ud800' in position 0: surrogates not allowed" 21 | ); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /tests/test_variable_arguments.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | 3 | use pyo3::prelude::*; 4 | use pyo3::types::{PyDict, PyTuple}; 5 | 6 | #[path = "../src/tests/common.rs"] 7 | mod common; 8 | 9 | #[pyclass] 10 | struct MyClass {} 11 | 12 | #[pymethods] 13 | impl MyClass { 14 | #[staticmethod] 15 | #[pyo3(signature = (*args))] 16 | fn test_args(args: Bound<'_, PyTuple>) -> Bound<'_, PyTuple> { 17 | args 18 | } 19 | 20 | #[staticmethod] 21 | #[pyo3(signature = (**kwargs))] 22 | fn test_kwargs(kwargs: Option>) -> Option> { 23 | kwargs 24 | } 25 | } 26 | 27 | #[test] 28 | fn variable_args() { 29 | Python::with_gil(|py| { 30 | let my_obj = py.get_type::(); 31 | py_assert!(py, my_obj, "my_obj.test_args() == ()"); 32 | py_assert!(py, my_obj, "my_obj.test_args(1) == (1,)"); 33 | py_assert!(py, my_obj, "my_obj.test_args(1, 2) == (1, 2)"); 34 | }); 35 | } 36 | 37 | #[test] 38 | fn variable_kwargs() { 39 | Python::with_gil(|py| { 40 | let my_obj = py.get_type::(); 41 | py_assert!(py, my_obj, "my_obj.test_kwargs() == None"); 42 | py_assert!(py, my_obj, "my_obj.test_kwargs(test=1) == {'test': 1}"); 43 | py_assert!( 44 | py, 45 | my_obj, 46 | "my_obj.test_kwargs(test1=1, test2=2) == {'test1':1, 'test2':2}" 47 | ); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /tests/ui/abi3_dict.rs: -------------------------------------------------------------------------------- 1 | //! With abi3, dict not supported until python 3.9 or greater 2 | use pyo3::prelude::*; 3 | 4 | #[pyclass(dict)] 5 | struct TestClass {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /tests/ui/abi3_dict.stderr: -------------------------------------------------------------------------------- 1 | error: `dict` requires Python >= 3.9 when using the `abi3` feature 2 | --> tests/ui/abi3_dict.rs:4:11 3 | | 4 | 4 | #[pyclass(dict)] 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/abi3_inheritance.rs: -------------------------------------------------------------------------------- 1 | use pyo3::exceptions::PyException; 2 | use pyo3::prelude::*; 3 | 4 | #[pyclass(extends=PyException)] 5 | #[derive(Clone)] 6 | struct MyException { 7 | code: u32, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /tests/ui/abi3_nativetype_inheritance.rs: -------------------------------------------------------------------------------- 1 | //! With abi3, we cannot inherit native types. 2 | use pyo3::prelude::*; 3 | use pyo3::types::PyDict; 4 | 5 | #[pyclass(extends=PyDict)] 6 | struct TestClass {} 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /tests/ui/abi3_weakref.rs: -------------------------------------------------------------------------------- 1 | //! With abi3, weakref not supported until python 3.9 or greater 2 | use pyo3::prelude::*; 3 | 4 | #[pyclass(weakref)] 5 | struct TestClass {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /tests/ui/abi3_weakref.stderr: -------------------------------------------------------------------------------- 1 | error: `weakref` requires Python >= 3.9 when using the `abi3` feature 2 | --> tests/ui/abi3_weakref.rs:4:11 3 | | 4 | 4 | #[pyclass(weakref)] 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/ambiguous_associated_items.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass(eq)] 4 | #[derive(PartialEq)] 5 | pub enum SimpleItems { 6 | Error, 7 | Output, 8 | Target, 9 | } 10 | 11 | #[pyclass] 12 | pub enum ComplexItems { 13 | Error(PyObject), 14 | Output(PyObject), 15 | Target(PyObject), 16 | } 17 | 18 | #[derive(IntoPyObject)] 19 | enum DeriveItems { 20 | Error(PyObject), 21 | Output(PyObject), 22 | Target(PyObject), 23 | } 24 | 25 | fn main() {} 26 | -------------------------------------------------------------------------------- /tests/ui/duplicate_pymodule_submodule.rs: -------------------------------------------------------------------------------- 1 | #[pyo3::pymodule] 2 | mod mymodule { 3 | #[pyo3::pymodule(submodule)] 4 | mod submod {} 5 | } 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /tests/ui/duplicate_pymodule_submodule.stderr: -------------------------------------------------------------------------------- 1 | error: `submodule` may only be specified once (it is implicitly always specified for nested modules) 2 | --> tests/ui/duplicate_pymodule_submodule.rs:4:2 3 | | 4 | 4 | mod submod {} 5 | | ^^^ 6 | 7 | error[E0425]: cannot find value `_PYO3_DEF` in module `submod` 8 | --> tests/ui/duplicate_pymodule_submodule.rs:1:1 9 | | 10 | 1 | #[pyo3::pymodule] 11 | | ^^^^^^^^^^^^^^^^^ not found in `submod` 12 | | 13 | = note: this error originates in the attribute macro `pyo3::pymodule` (in Nightly builds, run with -Z macro-backtrace for more info) 14 | help: consider importing this static 15 | | 16 | 3 + use crate::mymodule::_PYO3_DEF; 17 | | 18 | -------------------------------------------------------------------------------- /tests/ui/empty.rs: -------------------------------------------------------------------------------- 1 | // see invalid_pymodule_in_root.rs 2 | -------------------------------------------------------------------------------- /tests/ui/forbid_unsafe.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | #![forbid(unsafe_op_in_unsafe_fn)] 3 | 4 | use pyo3::*; 5 | 6 | #[allow(unexpected_cfgs)] 7 | #[path = "../../src/tests/hygiene/mod.rs"] 8 | mod hygiene; 9 | 10 | mod gh_4394 { 11 | use pyo3::prelude::*; 12 | 13 | #[derive(Eq, Ord, PartialEq, PartialOrd, Clone)] 14 | #[pyclass(get_all)] 15 | pub struct VersionSpecifier { 16 | pub(crate) operator: Operator, 17 | pub(crate) version: Version, 18 | } 19 | 20 | #[derive(Eq, Ord, PartialEq, PartialOrd, Debug, Hash, Clone, Copy)] 21 | #[pyo3::pyclass(eq, eq_int)] 22 | pub enum Operator { 23 | Equal, 24 | } 25 | 26 | #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] 27 | #[pyclass] 28 | pub struct Version; 29 | } 30 | 31 | mod from_py_with { 32 | use pyo3::prelude::*; 33 | use pyo3::types::PyBytes; 34 | 35 | fn bytes_from_py(bytes: &Bound<'_, PyAny>) -> PyResult> { 36 | Ok(bytes.downcast::()?.as_bytes().to_vec()) 37 | } 38 | 39 | #[pyfunction] 40 | fn f(#[pyo3(from_py_with = bytes_from_py)] _bytes: Vec) {} 41 | } 42 | 43 | fn main() {} 44 | -------------------------------------------------------------------------------- /tests/ui/get_set_all.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass(set_all)] 4 | struct Foo; 5 | 6 | #[pyclass(set_all)] 7 | struct Foo2{ 8 | #[pyo3(set)] 9 | field: u8, 10 | } 11 | 12 | #[pyclass(get_all)] 13 | struct Foo3; 14 | 15 | #[pyclass(get_all)] 16 | struct Foo4{ 17 | #[pyo3(get)] 18 | field: u8, 19 | } 20 | 21 | fn main() {} 22 | -------------------------------------------------------------------------------- /tests/ui/get_set_all.stderr: -------------------------------------------------------------------------------- 1 | error: `set_all` on an unit struct does nothing, because unit structs have no fields 2 | --> tests/ui/get_set_all.rs:3:11 3 | | 4 | 3 | #[pyclass(set_all)] 5 | | ^^^^^^^ 6 | 7 | error: useless `set` - the struct is already annotated with `set_all` 8 | --> tests/ui/get_set_all.rs:8:12 9 | | 10 | 8 | #[pyo3(set)] 11 | | ^^^ 12 | 13 | error: `get_all` on an unit struct does nothing, because unit structs have no fields 14 | --> tests/ui/get_set_all.rs:12:11 15 | | 16 | 12 | #[pyclass(get_all)] 17 | | ^^^^^^^ 18 | 19 | error: useless `get` - the struct is already annotated with `get_all` 20 | --> tests/ui/get_set_all.rs:17:12 21 | | 22 | 17 | #[pyo3(get)] 23 | | ^^^ 24 | -------------------------------------------------------------------------------- /tests/ui/immutable_type.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass(immutable_type)] 4 | struct ImmutableType {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/ui/immutable_type.stderr: -------------------------------------------------------------------------------- 1 | error: `immutable_type` requires Python >= 3.10 or >= 3.14 (ABI3) 2 | --> tests/ui/immutable_type.rs:3:11 3 | | 4 | 3 | #[pyclass(immutable_type)] 5 | | ^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/invalid_argument_attributes.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | fn invalid_attribute(#[pyo3(get)] _param: String) {} 5 | 6 | #[pyfunction] 7 | fn from_py_with_no_value(#[pyo3(from_py_with)] _param: String) {} 8 | 9 | #[pyfunction] 10 | fn from_py_with_string(#[pyo3("from_py_with")] _param: String) {} 11 | 12 | #[pyfunction] 13 | fn from_py_with_value_not_found(#[pyo3(from_py_with = func)] _param: String) {} 14 | 15 | #[pyfunction] 16 | fn from_py_with_repeated(#[pyo3(from_py_with = func, from_py_with = func)] _param: String) {} 17 | 18 | fn bytes_from_py(bytes: &Bound<'_, pyo3::types::PyBytes>) -> Vec { 19 | bytes.as_bytes().to_vec() 20 | } 21 | 22 | #[pyfunction] 23 | fn f(#[pyo3(from_py_with = "bytes_from_py")] _bytes: Vec) {} 24 | 25 | fn main() {} 26 | -------------------------------------------------------------------------------- /tests/ui/invalid_async.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | async fn check(){} 5 | 6 | #[pyclass] 7 | pub(crate) struct AsyncRange { 8 | count: i32, 9 | target: i32, 10 | } 11 | #[pymethods] 12 | impl AsyncRange { 13 | async fn __anext__(mut _pyself: PyRefMut<'_, Self>) -> PyResult { 14 | Ok(0) 15 | } 16 | 17 | async fn foo(&self) {} 18 | } 19 | 20 | fn main() {} -------------------------------------------------------------------------------- /tests/ui/invalid_async.stderr: -------------------------------------------------------------------------------- 1 | error: async functions are only supported with the `experimental-async` feature 2 | --> tests/ui/invalid_async.rs:4:1 3 | | 4 | 4 | async fn check(){} 5 | | ^^^^^ 6 | 7 | error: async functions are only supported with the `experimental-async` feature 8 | --> tests/ui/invalid_async.rs:13:5 9 | | 10 | 13 | async fn __anext__(mut _pyself: PyRefMut<'_, Self>) -> PyResult { 11 | | ^^^^^ 12 | -------------------------------------------------------------------------------- /tests/ui/invalid_base_class.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::PyBool; 3 | 4 | #[pyclass(extends=PyBool)] 5 | struct ExtendsBool; 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /tests/ui/invalid_cancel_handle.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | async fn cancel_handle_repeated(#[pyo3(cancel_handle, cancel_handle)] _param: String) {} 5 | 6 | #[pyfunction] 7 | async fn cancel_handle_repeated2( 8 | #[pyo3(cancel_handle)] _param: String, 9 | #[pyo3(cancel_handle)] _param2: String, 10 | ) { 11 | } 12 | 13 | #[pyfunction] 14 | fn cancel_handle_synchronous(#[pyo3(cancel_handle)] _param: String) {} 15 | 16 | #[pyfunction] 17 | async fn cancel_handle_wrong_type(#[pyo3(cancel_handle)] _param: String) {} 18 | 19 | #[pyfunction] 20 | async fn missing_cancel_handle_attribute(_param: pyo3::coroutine::CancelHandle) {} 21 | 22 | #[pyfunction] 23 | async fn cancel_handle_and_from_py_with( 24 | #[pyo3(cancel_handle, from_py_with = cancel_handle)] _param: pyo3::coroutine::CancelHandle, 25 | ) { 26 | } 27 | 28 | fn main() {} 29 | -------------------------------------------------------------------------------- /tests/ui/invalid_closure.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::{PyCFunction, PyDict, PyTuple}; 3 | 4 | fn main() { 5 | let fun: Py = Python::with_gil(|py| { 6 | let local_data = vec![0, 1, 2, 3, 4]; 7 | let ref_: &[u8] = &local_data; 8 | 9 | let closure_fn = 10 | |_args: &Bound<'_, PyTuple>, _kwargs: Option<&Bound<'_, PyDict>>| -> PyResult<()> { 11 | println!("This is five: {:?}", ref_.len()); 12 | Ok(()) 13 | }; 14 | PyCFunction::new_closure(py, None, None, closure_fn) 15 | .unwrap() 16 | .into() 17 | }); 18 | 19 | Python::with_gil(|py| { 20 | fun.call0(py).unwrap(); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /tests/ui/invalid_closure.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `local_data` does not live long enough 2 | --> tests/ui/invalid_closure.rs:7:27 3 | | 4 | 6 | let local_data = vec![0, 1, 2, 3, 4]; 5 | | ---------- binding `local_data` declared here 6 | 7 | let ref_: &[u8] = &local_data; 7 | | ^^^^^^^^^^^ borrowed value does not live long enough 8 | ... 9 | 14 | PyCFunction::new_closure(py, None, None, closure_fn) 10 | | ---------------------------------------------------- argument requires that `local_data` is borrowed for `'static` 11 | ... 12 | 17 | }); 13 | | - `local_data` dropped here while still borrowed 14 | -------------------------------------------------------------------------------- /tests/ui/invalid_frozen_pyclass_borrow.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass(frozen)] 4 | pub struct Foo { 5 | #[pyo3(get)] 6 | field: u32, 7 | } 8 | 9 | #[pymethods] 10 | impl Foo { 11 | fn mut_method(&mut self) {} 12 | } 13 | 14 | fn borrow_mut_fails(foo: Py, py: Python) { 15 | let borrow = foo.bind(py).borrow_mut(); 16 | } 17 | 18 | #[pyclass(subclass)] 19 | struct MutableBase; 20 | 21 | #[pyclass(frozen, extends = MutableBase)] 22 | struct ImmutableChild; 23 | 24 | fn borrow_mut_of_child_fails(child: Py, py: Python) { 25 | let borrow = child.bind(py).borrow_mut(); 26 | } 27 | 28 | fn py_get_of_mutable_class_fails(class: Py) { 29 | class.get(); 30 | } 31 | 32 | fn pyclass_get_of_mutable_class_fails(class: &Bound<'_, MutableBase>) { 33 | class.get(); 34 | } 35 | 36 | #[pyclass(frozen)] 37 | pub struct SetOnFrozenClass { 38 | #[pyo3(set)] 39 | field: u32, 40 | } 41 | 42 | fn main() {} 43 | -------------------------------------------------------------------------------- /tests/ui/invalid_intern_arg.rs: -------------------------------------------------------------------------------- 1 | use pyo3::Python; 2 | 3 | fn main() { 4 | let _foo = if true { "foo" } else { "bar" }; 5 | Python::with_gil(|py| py.import(pyo3::intern!(py, _foo)).unwrap()); 6 | } 7 | -------------------------------------------------------------------------------- /tests/ui/invalid_intern_arg.stderr: -------------------------------------------------------------------------------- 1 | error[E0435]: attempt to use a non-constant value in a constant 2 | --> tests/ui/invalid_intern_arg.rs:5:55 3 | | 4 | 5 | Python::with_gil(|py| py.import(pyo3::intern!(py, _foo)).unwrap()); 5 | | ^^^^ non-constant value 6 | | 7 | help: consider using `let` instead of `static` 8 | --> src/sync.rs 9 | | 10 | - static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text); 11 | + let INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text); 12 | | 13 | 14 | error: lifetime may not live long enough 15 | --> tests/ui/invalid_intern_arg.rs:5:27 16 | | 17 | 5 | Python::with_gil(|py| py.import(pyo3::intern!(py, _foo)).unwrap()); 18 | | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` 19 | | | | 20 | | | return type of closure is pyo3::Bound<'2, PyModule> 21 | | has type `Python<'1>` 22 | -------------------------------------------------------------------------------- /tests/ui/invalid_intopy_with.rs: -------------------------------------------------------------------------------- 1 | use pyo3::{IntoPyObject, IntoPyObjectRef}; 2 | 3 | #[derive(IntoPyObject, IntoPyObjectRef)] 4 | struct InvalidIntoPyWithFn { 5 | #[pyo3(into_py_with = into)] 6 | inner: String, 7 | } 8 | 9 | fn into(_a: String, _py: pyo3::Python<'_>) -> pyo3::PyResult> { 10 | todo!() 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /tests/ui/invalid_intopy_with.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/ui/invalid_intopy_with.rs:5:27 3 | | 4 | 3 | #[derive(IntoPyObject, IntoPyObjectRef)] 5 | | ------------ expected due to this 6 | 4 | struct InvalidIntoPyWithFn { 7 | 5 | #[pyo3(into_py_with = into)] 8 | | ^^^^ expected fn pointer, found fn item 9 | | 10 | = note: expected fn pointer `for<'a> fn(Cow<'a, _>, Python<'py>) -> Result, PyErr>` 11 | found fn item `for<'a> fn(String, Python<'a>) -> Result, PyErr> {into}` 12 | 13 | error[E0308]: mismatched types 14 | --> tests/ui/invalid_intopy_with.rs:5:27 15 | | 16 | 3 | #[derive(IntoPyObject, IntoPyObjectRef)] 17 | | --------------- expected due to this 18 | 4 | struct InvalidIntoPyWithFn { 19 | 5 | #[pyo3(into_py_with = into)] 20 | | ^^^^ expected fn pointer, found fn item 21 | | 22 | = note: expected fn pointer `for<'a> fn(Cow<'a, _>, Python<'py>) -> Result, PyErr>` 23 | found fn item `for<'a> fn(String, Python<'a>) -> Result, PyErr> {into}` 24 | -------------------------------------------------------------------------------- /tests/ui/invalid_property_args.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | struct ClassWithGetter {} 5 | 6 | #[pymethods] 7 | impl ClassWithGetter { 8 | #[getter] 9 | fn getter_with_arg(&self, _py: Python<'_>, _index: u32) {} 10 | } 11 | 12 | #[pyclass] 13 | struct ClassWithSetter {} 14 | 15 | #[pymethods] 16 | impl ClassWithSetter { 17 | #[setter] 18 | fn setter_with_no_arg(&mut self, _py: Python<'_>) {} 19 | } 20 | 21 | #[pymethods] 22 | impl ClassWithSetter { 23 | #[setter] 24 | fn setter_with_too_many_args(&mut self, _py: Python<'_>, _foo: u32, _bar: u32) {} 25 | } 26 | 27 | #[pyclass] 28 | struct TupleGetterSetterNoName(#[pyo3(get, set)] i32); 29 | 30 | #[pyclass] 31 | struct MultipleGet(#[pyo3(get, get)] i32); 32 | 33 | #[pyclass] 34 | struct MultipleSet(#[pyo3(set, set)] i32); 35 | 36 | #[pyclass] 37 | struct MultipleName(#[pyo3(name = "foo", name = "bar")] i32); 38 | 39 | #[pyclass] 40 | struct NameWithoutGetSet(#[pyo3(name = "value")] i32); 41 | 42 | #[pyclass] 43 | struct InvalidGetterType { 44 | #[pyo3(get)] 45 | value: ::std::marker::PhantomData, 46 | } 47 | 48 | fn main() {} 49 | -------------------------------------------------------------------------------- /tests/ui/invalid_pycallargs.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | fn main() { 4 | Python::with_gil(|py| { 5 | let any = py.None().into_bound(py); 6 | any.call1("foo"); 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /tests/ui/invalid_pyclass_generic.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::PyType; 3 | 4 | #[pyclass(generic)] 5 | struct ClassRedefinesClassGetItem { 6 | } 7 | 8 | #[pymethods] 9 | impl ClassRedefinesClassGetItem { 10 | #[new] 11 | fn new() -> ClassRedefinesClassGetItem { 12 | Self {} 13 | } 14 | 15 | #[classmethod] 16 | pub fn __class_getitem__( 17 | cls: &Bound<'_, PyType>, 18 | key: &Bound<'_, PyAny>, 19 | ) -> PyResult { 20 | pyo3::types::PyGenericAlias::new(cls.py(), cls.as_any(), key) 21 | } 22 | } 23 | 24 | fn main() {} 25 | -------------------------------------------------------------------------------- /tests/ui/invalid_pyclass_item.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | fn foo() {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/ui/invalid_pyclass_item.stderr: -------------------------------------------------------------------------------- 1 | error: #[pyclass] only supports structs and enums. 2 | --> tests/ui/invalid_pyclass_item.rs:4:1 3 | | 4 | 4 | fn foo() {} 5 | | ^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/invalid_pyfunction_definition.rs: -------------------------------------------------------------------------------- 1 | #[pyo3::pymodule] 2 | mod pyo3_scratch { 3 | use pyo3::prelude::*; 4 | 5 | #[pyclass] 6 | struct Foo {} 7 | 8 | #[pymethods] 9 | impl Foo { 10 | #[pyfunction] 11 | fn bug() {} 12 | } 13 | } 14 | 15 | fn main() {} 16 | -------------------------------------------------------------------------------- /tests/ui/invalid_pyfunction_definition.stderr: -------------------------------------------------------------------------------- 1 | error: functions inside #[pymethods] do not need to be annotated with #[pyfunction] 2 | --> tests/ui/invalid_pyfunction_definition.rs:11:9 3 | | 4 | 11 | fn bug() {} 5 | | ^^ 6 | -------------------------------------------------------------------------------- /tests/ui/invalid_pyfunction_warn.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyfunction] 4 | #[pyo3(warn)] 5 | fn no_parenthesis_deprecated() {} 6 | 7 | #[pyfunction] 8 | #[pyo3(warn())] 9 | fn no_message_deprecated() {} 10 | 11 | #[pyfunction] 12 | #[pyo3(warn(category = pyo3::exceptions::PyDeprecationWarning))] 13 | fn no_message_deprecated_with_category() {} 14 | 15 | #[pyfunction] 16 | #[pyo3(warn(category = pyo3::exceptions::PyDeprecationWarning, message = ,))] 17 | fn empty_message_deprecated_with_category() {} 18 | 19 | #[pyfunction] 20 | #[pyo3(warn(message = "deprecated function", category = ,))] 21 | fn empty_category_deprecated_with_message() {} 22 | 23 | #[pyfunction] 24 | #[pyo3(warn(message = "deprecated function", random_key))] 25 | fn random_key_deprecated() {} 26 | 27 | #[pyclass] 28 | struct DeprecatedMethodContainer {} 29 | 30 | #[pymethods] 31 | impl DeprecatedMethodContainer { 32 | #[classattr] 33 | #[pyo3(warn(message = "deprecated class attr"))] 34 | fn deprecated_class_attr() -> i32 { 35 | 5 36 | } 37 | } 38 | 39 | #[pymethods] 40 | impl DeprecatedMethodContainer { 41 | #[pyo3(warn(message = "deprecated __traverse__"))] 42 | fn __traverse__(&self, _visit: pyo3::gc::PyVisit<'_>) -> Result<(), pyo3::PyTraverseError> { 43 | Ok(()) 44 | } 45 | } 46 | 47 | fn main() {} 48 | -------------------------------------------------------------------------------- /tests/ui/invalid_pyfunctions.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::{PyDict, PyString, PyTuple}; 3 | 4 | #[pyfunction] 5 | fn generic_function(_value: T) {} 6 | 7 | #[pyfunction] 8 | fn impl_trait_function(_impl_trait: impl AsRef) {} 9 | 10 | #[pyfunction] 11 | fn wildcard_argument(_: i32) {} 12 | 13 | #[pyfunction] 14 | fn destructured_argument((_a, _b): (i32, i32)) {} 15 | 16 | #[pyfunction] 17 | #[pyo3(signature=(*args))] 18 | fn function_with_optional_args(args: Option>) { 19 | let _ = args; 20 | } 21 | 22 | #[pyfunction] 23 | #[pyo3(signature=(**kwargs))] 24 | fn function_with_required_kwargs(kwargs: Bound<'_, PyDict>) { 25 | let _ = kwargs; 26 | } 27 | 28 | #[pyfunction(pass_module)] 29 | fn pass_module_but_no_arguments<'py>() {} 30 | 31 | #[pyfunction(pass_module)] 32 | fn first_argument_not_module<'a, 'py>( 33 | _string: &str, 34 | module: &'a Bound<'py, PyModule>, 35 | ) -> PyResult> { 36 | module.name() 37 | } 38 | 39 | fn main() {} 40 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethod_enum.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | enum ComplexEnum { 5 | Int { int: i32 }, 6 | Str { string: String }, 7 | } 8 | 9 | #[pymethods] 10 | impl ComplexEnum { 11 | fn mutate_in_place(&mut self) { 12 | *self = match self { 13 | ComplexEnum::Int { int } => ComplexEnum::Str { string: int.to_string() }, 14 | ComplexEnum::Str { string } => ComplexEnum::Int { int: string.len() as i32 }, 15 | } 16 | } 17 | } 18 | 19 | #[pyclass] 20 | enum TupleEnum { 21 | Int(i32), 22 | Str(String), 23 | } 24 | 25 | #[pymethods] 26 | impl TupleEnum { 27 | fn mutate_in_place(&mut self) { 28 | *self = match self { 29 | TupleEnum::Int(int) => TupleEnum::Str(int.to_string()), 30 | TupleEnum::Str(string) => TupleEnum::Int(string.len() as i32), 31 | } 32 | } 33 | } 34 | 35 | fn main() {} 36 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethod_names.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | struct TestClass { 5 | num: u32, 6 | } 7 | 8 | #[pymethods] 9 | impl TestClass { 10 | #[pyo3(name = "num")] 11 | #[getter(number)] 12 | fn get_num(&self) -> u32 { self.num } 13 | } 14 | 15 | #[pymethods] 16 | impl TestClass { 17 | #[pyo3(name = "foo")] 18 | #[pyo3(name = "bar")] 19 | fn qux(&self) -> u32 { self.num } 20 | } 21 | 22 | #[pymethods] 23 | impl TestClass { 24 | #[pyo3(name = "makenew")] 25 | #[new] 26 | fn new(&self) -> Self { Self { num: 0 } } 27 | } 28 | 29 | #[pymethods] 30 | impl TestClass { 31 | #[getter(1)] 32 | fn get_one(&self) -> Self { Self { num: 0 } } 33 | } 34 | 35 | #[pymethods] 36 | impl TestClass { 37 | #[getter = 1] 38 | fn get_two(&self) -> Self { Self { num: 0 } } 39 | } 40 | 41 | 42 | fn main() {} 43 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethod_names.stderr: -------------------------------------------------------------------------------- 1 | error: `name` may only be specified once 2 | --> tests/ui/invalid_pymethod_names.rs:11:14 3 | | 4 | 11 | #[getter(number)] 5 | | ^^^^^^ 6 | 7 | error: `name` may only be specified once 8 | --> tests/ui/invalid_pymethod_names.rs:18:12 9 | | 10 | 18 | #[pyo3(name = "bar")] 11 | | ^^^^ 12 | 13 | error: `name` not allowed with `#[new]` 14 | --> tests/ui/invalid_pymethod_names.rs:24:19 15 | | 16 | 24 | #[pyo3(name = "makenew")] 17 | | ^^^^^^^^^ 18 | 19 | error: expected ident or string literal for property name 20 | --> tests/ui/invalid_pymethod_names.rs:31:14 21 | | 22 | 31 | #[getter(1)] 23 | | ^ 24 | 25 | error: expected `#[getter(name)]` to set the name 26 | --> tests/ui/invalid_pymethod_names.rs:37:14 27 | | 28 | 37 | #[getter = 1] 29 | | ^ 30 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethod_receiver.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | struct MyClass {} 5 | 6 | #[pymethods] 7 | impl MyClass { 8 | fn method_with_invalid_self_type(_slf: i32, _py: Python<'_>, _index: u32) {} 9 | } 10 | 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethod_receiver.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `i32: TryFrom>` is not satisfied 2 | --> tests/ui/invalid_pymethod_receiver.rs:8:44 3 | | 4 | 8 | fn method_with_invalid_self_type(_slf: i32, _py: Python<'_>, _index: u32) {} 5 | | ^^^ the trait `From>` is not implemented for `i32` 6 | | 7 | = help: the following other types implement trait `From`: 8 | `i32` implements `From` 9 | `i32` implements `From` 10 | `i32` implements `From` 11 | `i32` implements `From` 12 | `i32` implements `From` 13 | = note: required for `BoundRef<'_, '_, MyClass>` to implement `Into` 14 | = note: required for `i32` to implement `TryFrom>` 15 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethods_buffer.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | struct MyClass {} 5 | 6 | #[pymethods] 7 | impl MyClass { 8 | #[pyo3(name = "__getbuffer__")] 9 | fn getbuffer_must_be_unsafe(&self, _view: *mut pyo3::ffi::Py_buffer, _flags: std::os::raw::c_int) {} 10 | } 11 | 12 | #[pymethods] 13 | impl MyClass { 14 | #[pyo3(name = "__releasebuffer__")] 15 | fn releasebuffer_must_be_unsafe(&self, _view: *mut pyo3::ffi::Py_buffer) {} 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethods_buffer.stderr: -------------------------------------------------------------------------------- 1 | error: `__getbuffer__` must be `unsafe fn` 2 | --> tests/ui/invalid_pymethods_buffer.rs:9:8 3 | | 4 | 9 | fn getbuffer_must_be_unsafe(&self, _view: *mut pyo3::ffi::Py_buffer, _flags: std::os::raw::c_int) {} 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^ 6 | 7 | error: `__releasebuffer__` must be `unsafe fn` 8 | --> tests/ui/invalid_pymethods_buffer.rs:15:8 9 | | 10 | 15 | fn releasebuffer_must_be_unsafe(&self, _view: *mut pyo3::ffi::Py_buffer) {} 11 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 12 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethods_duplicates.rs: -------------------------------------------------------------------------------- 1 | //! These tests are located in a separate file because they cause conflicting implementation 2 | //! errors, which means other errors such as typechecking errors are not reported. 3 | 4 | use pyo3::prelude::*; 5 | 6 | #[pyclass] 7 | struct TwoNew {} 8 | 9 | #[pymethods] 10 | impl TwoNew { 11 | #[new] 12 | fn new_1() -> Self { 13 | Self {} 14 | } 15 | 16 | #[new] 17 | fn new_2() -> Self { 18 | Self {} 19 | } 20 | } 21 | 22 | #[pyclass] 23 | struct DuplicateMethod {} 24 | 25 | #[pymethods] 26 | impl DuplicateMethod { 27 | #[pyo3(name = "func")] 28 | fn func_a(&self) {} 29 | 30 | #[pyo3(name = "func")] 31 | fn func_b(&self) {} 32 | } 33 | 34 | fn main() {} 35 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethods_warn.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | struct WarningMethodContainer {} 5 | 6 | #[pymethods] 7 | impl WarningMethodContainer { 8 | #[pyo3(warn(message = "warn on __traverse__"))] 9 | fn __traverse__(&self) {} 10 | } 11 | 12 | #[pymethods] 13 | impl WarningMethodContainer { 14 | #[classattr] 15 | #[pyo3(warn(message = "warn for class attr"))] 16 | fn a_class_attr(_py: pyo3::Python<'_>) -> i64 { 17 | 5 18 | } 19 | } 20 | 21 | fn main() {} 22 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymethods_warn.stderr: -------------------------------------------------------------------------------- 1 | error: __traverse__ cannot be used with #[pyo3(warn)] 2 | --> tests/ui/invalid_pymethods_warn.rs:8:12 3 | | 4 | 8 | #[pyo3(warn(message = "warn on __traverse__"))] 5 | | ^^^^ 6 | 7 | error: #[classattr] cannot be used with #[pyo3(warn)] 8 | --> tests/ui/invalid_pymethods_warn.rs:15:12 9 | | 10 | 15 | #[pyo3(warn(message = "warn for class attr"))] 11 | | ^^^^ 12 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_args.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pymodule(some_arg)] 4 | fn module(m: &Bound<'_, PyModule>) -> PyResult<()> { 5 | Ok(()) 6 | } 7 | 8 | fn main(){} 9 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_args.stderr: -------------------------------------------------------------------------------- 1 | error: expected one of: `name`, `crate`, `module`, `submodule`, `gil_used` 2 | --> tests/ui/invalid_pymodule_args.rs:3:12 3 | | 4 | 3 | #[pymodule(some_arg)] 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_glob.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | 3 | use pyo3::prelude::*; 4 | 5 | #[pyfunction] 6 | fn foo() -> usize { 7 | 0 8 | } 9 | 10 | #[pymodule] 11 | mod module { 12 | #[pymodule_export] 13 | use super::*; 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_glob.stderr: -------------------------------------------------------------------------------- 1 | error: #[pymodule] cannot import glob statements 2 | --> tests/ui/invalid_pymodule_glob.rs:13:16 3 | | 4 | 13 | use super::*; 5 | | ^ 6 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_in_root.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pymodule] 4 | #[path = "empty.rs"] // to silence error related to missing file 5 | mod invalid_pymodule_in_root_module; 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_in_root.stderr: -------------------------------------------------------------------------------- 1 | error[E0658]: non-inline modules in proc macro input are unstable 2 | --> tests/ui/invalid_pymodule_in_root.rs:5:1 3 | | 4 | 5 | mod invalid_pymodule_in_root_module; 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: see issue #54727 for more information 8 | 9 | error: `#[pymodule]` can only be used on inline modules 10 | --> tests/ui/invalid_pymodule_in_root.rs:5:1 11 | | 12 | 5 | mod invalid_pymodule_in_root_module; 13 | | ^^^ 14 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_trait.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pymodule] 4 | mod module { 5 | #[pymodule_export] 6 | trait Foo {} 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_trait.stderr: -------------------------------------------------------------------------------- 1 | error: `#[pymodule_export]` may only be used on `use` or `const` statements 2 | --> tests/ui/invalid_pymodule_trait.rs:5:5 3 | | 4 | 5 | #[pymodule_export] 5 | | ^ 6 | 7 | error: cannot find attribute `pymodule_export` in this scope 8 | --> tests/ui/invalid_pymodule_trait.rs:5:7 9 | | 10 | 5 | #[pymodule_export] 11 | | ^^^^^^^^^^^^^^^ 12 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_two_pymodule_init.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pymodule] 4 | mod module { 5 | use pyo3::prelude::*; 6 | 7 | #[pymodule_init] 8 | fn init(_m: &Bound<'_, PyModule>) -> PyResult<()> { 9 | Ok(()) 10 | } 11 | 12 | #[pymodule_init] 13 | fn init2(_m: &Bound<'_, PyModule>) -> PyResult<()> { 14 | Ok(()) 15 | } 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /tests/ui/invalid_pymodule_two_pymodule_init.stderr: -------------------------------------------------------------------------------- 1 | error: only one `#[pymodule_init]` may be specified 2 | --> tests/ui/invalid_pymodule_two_pymodule_init.rs:13:5 3 | | 4 | 13 | fn init2(_m: &Bound<'_, PyModule>) -> PyResult<()> { 5 | | ^^ 6 | -------------------------------------------------------------------------------- /tests/ui/invalid_result_conversion.rs: -------------------------------------------------------------------------------- 1 | //! Testing https://github.com/PyO3/pyo3/issues/1106. A result type that 2 | //! *doesn't* implement `From for PyErr` won't be automatically 3 | //! converted when using `#[pyfunction]`. 4 | use pyo3::prelude::*; 5 | 6 | use std::fmt; 7 | 8 | /// A basic error type for the tests. It's missing `From for PyErr`, 9 | /// though, so it shouldn't work. 10 | #[derive(Debug)] 11 | struct MyError { 12 | pub descr: &'static str, 13 | } 14 | 15 | impl fmt::Display for MyError { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | write!(f, "My error message: {}", self.descr) 18 | } 19 | } 20 | 21 | #[pyfunction] 22 | fn should_not_work() -> Result<(), MyError> { 23 | Err(MyError { 24 | descr: "something went wrong", 25 | }) 26 | } 27 | 28 | fn main() { 29 | Python::with_gil(|py| { 30 | wrap_pyfunction!(should_not_work)(py); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /tests/ui/invalid_result_conversion.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `PyErr: From` is not satisfied 2 | --> tests/ui/invalid_result_conversion.rs:22:25 3 | | 4 | 22 | fn should_not_work() -> Result<(), MyError> { 5 | | ^^^^^^ the trait `From` is not implemented for `PyErr` 6 | | 7 | = help: the following other types implement trait `From`: 8 | `PyErr` implements `From` 9 | `PyErr` implements `From` 10 | `PyErr` implements `From>` 11 | `PyErr` implements `From>` 12 | `PyErr` implements `From` 13 | `PyErr` implements `From` 14 | `PyErr` implements `From` 15 | `PyErr` implements `From>` 16 | and $N others 17 | = note: required for `MyError` to implement `Into` 18 | -------------------------------------------------------------------------------- /tests/ui/missing_intopy.rs: -------------------------------------------------------------------------------- 1 | struct Blah; 2 | 3 | #[pyo3::pyfunction] 4 | fn blah() -> Blah { 5 | Blah 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /tests/ui/not_send.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | fn test_not_send_allow_threads(py: Python<'_>) { 4 | py.allow_threads(|| { drop(py); }); 5 | } 6 | 7 | fn main() { 8 | Python::with_gil(|py| { 9 | test_not_send_allow_threads(py); 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /tests/ui/not_send2.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::PyString; 3 | 4 | fn main() { 5 | Python::with_gil(|py| { 6 | let string = PyString::new(py, "foo"); 7 | 8 | py.allow_threads(|| { 9 | println!("{:?}", string); 10 | }); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /tests/ui/pyclass_generic_enum.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass(generic)] 4 | enum NotGenericForEnum { 5 | A, 6 | B, 7 | } 8 | 9 | #[pyclass(generic)] 10 | enum NoGenericForComplexEnum { 11 | A { x: f64 }, 12 | B { y: f64, z: f64 }, 13 | } 14 | 15 | fn main() {} 16 | -------------------------------------------------------------------------------- /tests/ui/pyclass_generic_enum.stderr: -------------------------------------------------------------------------------- 1 | error: enums do not support #[pyclass(generic)] 2 | --> tests/ui/pyclass_generic_enum.rs:3:11 3 | | 4 | 3 | #[pyclass(generic)] 5 | | ^^^^^^^ 6 | 7 | error: enums do not support #[pyclass(generic)] 8 | --> tests/ui/pyclass_generic_enum.rs:9:11 9 | | 10 | 9 | #[pyclass(generic)] 11 | | ^^^^^^^ 12 | -------------------------------------------------------------------------------- /tests/ui/pyclass_probe.rs: -------------------------------------------------------------------------------- 1 | #![deny(unused_imports)] 2 | use pyo3::prelude::*; 3 | 4 | #[pyclass] 5 | pub struct Probe {} 6 | 7 | #[pymethods] 8 | impl Probe { 9 | #[new] 10 | fn new() -> Self { 11 | Self {} 12 | } 13 | } 14 | 15 | #[pymodule] 16 | fn probe(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { 17 | m.add_class::()?; 18 | Ok(()) 19 | } 20 | 21 | #[pyclass] 22 | struct Check5029(); 23 | 24 | macro_rules! impl_methods { 25 | ($name:ident) => { 26 | #[pymethods] 27 | impl Check5029 { 28 | fn $name(&self, _value: Option<&str>) -> PyResult<()> { 29 | Ok(()) 30 | } 31 | } 32 | }; 33 | } 34 | 35 | impl_methods!(some_method); 36 | 37 | fn main() {} 38 | -------------------------------------------------------------------------------- /tests/ui/pyclass_send.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use std::os::raw::c_void; 3 | 4 | #[pyclass] 5 | struct NotSyncNotSend(*mut c_void); 6 | 7 | #[pyclass] 8 | struct SendNotSync(*mut c_void); 9 | unsafe impl Send for SendNotSync {} 10 | 11 | #[pyclass] 12 | struct SyncNotSend(*mut c_void); 13 | unsafe impl Sync for SyncNotSend {} 14 | 15 | // None of the `unsendable` forms below should fail to compile 16 | 17 | #[pyclass(unsendable)] 18 | struct NotSyncNotSendUnsendable(*mut c_void); 19 | 20 | #[pyclass(unsendable)] 21 | struct SendNotSyncUnsendable(*mut c_void); 22 | unsafe impl Send for SendNotSyncUnsendable {} 23 | 24 | #[pyclass(unsendable)] 25 | struct SyncNotSendUnsendable(*mut c_void); 26 | unsafe impl Sync for SyncNotSendUnsendable {} 27 | 28 | fn main() {} 29 | -------------------------------------------------------------------------------- /tests/ui/pymodule_missing_docs.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | //! Some crate docs 3 | 4 | use pyo3::prelude::*; 5 | 6 | /// Some module documentation 7 | #[pymodule] 8 | pub fn python_module(_m: &Bound<'_, PyModule>) -> PyResult<()> { 9 | Ok(()) 10 | } 11 | 12 | /// Some module documentation 13 | #[pymodule] 14 | pub mod declarative_python_module {} 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /tests/ui/reject_generics.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | 3 | #[pyclass] 4 | struct ClassWithGenerics { 5 | a: A, 6 | } 7 | 8 | #[pyclass] 9 | struct ClassWithLifetimes<'a> { 10 | a: &'a str, 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /tests/ui/reject_generics.stderr: -------------------------------------------------------------------------------- 1 | error: #[pyclass] cannot have generic parameters. For an explanation, see https://pyo3.rs/v0.25.0/class.html#no-generic-parameters 2 | --> tests/ui/reject_generics.rs:4:25 3 | | 4 | 4 | struct ClassWithGenerics { 5 | | ^ 6 | 7 | error: #[pyclass] cannot have lifetime parameters. For an explanation, see https://pyo3.rs/v0.25.0/class.html#no-lifetime-parameters 8 | --> tests/ui/reject_generics.rs:9:27 9 | | 10 | 9 | struct ClassWithLifetimes<'a> { 11 | | ^^ 12 | -------------------------------------------------------------------------------- /tests/ui/static_ref.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::PyList; 3 | 4 | #[pyfunction] 5 | fn static_ref(list: &'static Bound<'_, PyList>) -> usize { 6 | list.len() 7 | } 8 | 9 | #[pyfunction] 10 | fn static_py(list: &Bound<'static, PyList>) -> usize { 11 | list.len() 12 | } 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /tests/ui/wrong_aspyref_lifetimes.rs: -------------------------------------------------------------------------------- 1 | use pyo3::{types::PyDict, Bound, Py, Python}; 2 | 3 | fn main() { 4 | let dict: Py = Python::with_gil(|py| PyDict::new(py).unbind()); 5 | 6 | // Should not be able to get access to Py contents outside of with_gil. 7 | let dict: &Bound<'_, PyDict> = Python::with_gil(|py| dict.bind(py)); 8 | 9 | let _py: Python = dict.py(); // Obtain a Python<'p> without GIL. 10 | } 11 | -------------------------------------------------------------------------------- /tests/ui/wrong_aspyref_lifetimes.stderr: -------------------------------------------------------------------------------- 1 | error: lifetime may not live long enough 2 | --> tests/ui/wrong_aspyref_lifetimes.rs:7:58 3 | | 4 | 7 | let dict: &Bound<'_, PyDict> = Python::with_gil(|py| dict.bind(py)); 5 | | --- ^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` 6 | | | | 7 | | | return type of closure is &'2 pyo3::Bound<'_, PyDict> 8 | | has type `Python<'1>` 9 | --------------------------------------------------------------------------------