Repository: PyO3/pyo3
Branch: main
Commit: baca1031feed
Files: 766
Total size: 4.6 MB
Directory structure:
gitextract_jnohosl3/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.md
│ ├── actions/
│ │ └── fetch-merge-base/
│ │ └── action.yml
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── benches.yml
│ ├── build.yml
│ ├── cache-cleanup.yml
│ ├── changelog.yml
│ ├── ci-cache-warmup.yml
│ ├── ci.yml
│ ├── coverage-pr-base.yml
│ ├── netlify-build.yml
│ ├── netlify-deploy.yml
│ └── release.yml
├── .gitignore
├── .netlify/
│ ├── internal_banner.html
│ └── redirect.sh
├── .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
├── build.rs
├── codecov.yml
├── emscripten/
│ ├── .gitignore
│ ├── Makefile
│ └── 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/
│ │ ├── .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/
│ │ │ └── multiple-python-versions.md
│ │ ├── building-and-distribution.md
│ │ ├── changelog.md
│ │ ├── class/
│ │ │ ├── call.md
│ │ │ ├── numeric.md
│ │ │ ├── object.md
│ │ │ ├── protocols.md
│ │ │ └── thread-safety.md
│ │ ├── class.md
│ │ ├── contributing.md
│ │ ├── conversions/
│ │ │ ├── tables.md
│ │ │ └── traits.md
│ │ ├── conversions.md
│ │ ├── debugging.md
│ │ ├── ecosystem/
│ │ │ ├── async-await.md
│ │ │ ├── logging.md
│ │ │ └── tracing.md
│ │ ├── ecosystem.md
│ │ ├── exception.md
│ │ ├── faq.md
│ │ ├── features.md
│ │ ├── free-threading.md
│ │ ├── function/
│ │ │ ├── error-handling.md
│ │ │ └── signature.md
│ │ ├── function-calls.md
│ │ ├── function.md
│ │ ├── getting-started.md
│ │ ├── index.md
│ │ ├── migration.md
│ │ ├── module.md
│ │ ├── parallelism.md
│ │ ├── performance.md
│ │ ├── python-from-rust/
│ │ │ ├── calling-existing-code.md
│ │ │ └── function-calls.md
│ │ ├── python-from-rust.md
│ │ ├── python-typing-hints.md
│ │ ├── rust-from-python.md
│ │ ├── trait-bounds.md
│ │ ├── type-stub.md
│ │ └── types.md
│ └── theme/
│ ├── tabs.css
│ └── tabs.js
├── newsfragments/
│ ├── .gitignore
│ ├── 5349.added.md
│ ├── 5349.changed.md
│ ├── 5668.added.md
│ ├── 5668.fixed.md
│ ├── 5668.removed.md
│ ├── 5753.changed.md
│ ├── 5770.added.md
│ ├── 5782.added.md
│ ├── 5797.changed.md
│ ├── 5803.changed.md
│ ├── 5809.packaging.md
│ ├── 5824.changed.md
│ ├── 5828.added.md
│ ├── 5830.changed.md
│ ├── 5837.fixed.md
│ ├── 5839.changed.md
│ ├── 5841.changed.md
│ ├── 5847.fixed.md
│ ├── 5849.added.md
│ ├── 5857.added.md
│ ├── 5865.packaging.md
│ ├── 5866.changed.md
│ ├── 5883.changed.md
│ ├── 5887.added.md
│ ├── 5891.added.md
│ ├── 5893.removed.md
│ ├── 5896.changed.md
│ └── 5897.changed.md
├── noxfile.py
├── pyo3-benches/
│ ├── Cargo.toml
│ ├── benches/
│ │ ├── bench_any.rs
│ │ ├── bench_attach.rs
│ │ ├── bench_bigint.rs
│ │ ├── bench_call.rs
│ │ ├── bench_comparisons.rs
│ │ ├── bench_critical_sections.rs
│ │ ├── bench_decimal.rs
│ │ ├── bench_dict.rs
│ │ ├── bench_err.rs
│ │ ├── bench_extract.rs
│ │ ├── bench_frompyobject.rs
│ │ ├── bench_intern.rs
│ │ ├── bench_intopyobject.rs
│ │ ├── bench_list.rs
│ │ ├── bench_py.rs
│ │ ├── bench_pyclass.rs
│ │ ├── bench_pystring_from_fmt.rs
│ │ ├── bench_set.rs
│ │ └── bench_tuple.rs
│ └── build.rs
├── pyo3-build-config/
│ ├── Cargo.toml
│ ├── build.rs
│ └── src/
│ ├── errors.rs
│ ├── impl_.rs
│ └── lib.rs
├── pyo3-ffi/
│ ├── ACKNOWLEDGEMENTS
│ ├── Cargo.toml
│ ├── 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
│ ├── codecs.rs
│ ├── compat/
│ │ ├── mod.rs
│ │ ├── py_3_10.rs
│ │ ├── py_3_13.rs
│ │ ├── py_3_14.rs
│ │ ├── py_3_15.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_/
│ │ ├── macros.rs
│ │ └── 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-ffi-check/
│ ├── Cargo.toml
│ ├── README.md
│ ├── build.rs
│ ├── macro/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── src/
│ │ └── main.rs
│ └── wrapper.h
├── pyo3-introspection/
│ ├── Cargo.toml
│ ├── src/
│ │ ├── introspection.rs
│ │ ├── lib.rs
│ │ ├── model.rs
│ │ └── stubs.rs
│ └── tests/
│ └── test.rs
├── pyo3-macros/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── pyo3-macros-backend/
│ ├── Cargo.toml
│ └── src/
│ ├── attributes.rs
│ ├── combine_errors.rs
│ ├── derive_attributes.rs
│ ├── frompyobject.rs
│ ├── intopyobject.rs
│ ├── introspection.rs
│ ├── konst.rs
│ ├── lib.rs
│ ├── method.rs
│ ├── module.rs
│ ├── params.rs
│ ├── py_expr.rs
│ ├── pyclass.rs
│ ├── pyfunction/
│ │ └── signature.rs
│ ├── pyfunction.rs
│ ├── pyimpl.rs
│ ├── pymethod.rs
│ ├── quotes.rs
│ └── utils.rs
├── pyo3-runtime/
│ ├── README.md
│ ├── pyproject.toml
│ ├── src/
│ │ └── pyo3_runtime/
│ │ └── __init__.py
│ └── tests/
│ └── __init__.py
├── pyproject.toml
├── pytests/
│ ├── Cargo.toml
│ ├── MANIFEST.in
│ ├── MODULE_DOC.md
│ ├── 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
│ │ ├── exception.rs
│ │ ├── lib.rs
│ │ ├── misc.rs
│ │ ├── objstore.rs
│ │ ├── othermod.rs
│ │ ├── path.rs
│ │ ├── pyclasses.rs
│ │ ├── pyfunctions.rs
│ │ ├── sequence.rs
│ │ └── subclassing.rs
│ ├── stubs/
│ │ ├── __init__.pyi
│ │ ├── awaitable.pyi
│ │ ├── buf_and_str.pyi
│ │ ├── comparisons.pyi
│ │ ├── consts.pyi
│ │ ├── datetime.pyi
│ │ ├── dict_iter.pyi
│ │ ├── enums.pyi
│ │ ├── exception.pyi
│ │ ├── misc.pyi
│ │ ├── objstore.pyi
│ │ ├── othermod.pyi
│ │ ├── path.pyi
│ │ ├── pyclasses.pyi
│ │ ├── pyfunctions.pyi
│ │ ├── sequence.pyi
│ │ └── subclassing.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_attaching_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
│ ├── byteswriter.rs
│ ├── call.rs
│ ├── conversion.rs
│ ├── conversions/
│ │ ├── anyhow.rs
│ │ ├── bigdecimal.rs
│ │ ├── bytes.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
│ │ │ ├── cstring.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/
│ │ ├── cancel.rs
│ │ └── waker.rs
│ ├── coroutine.rs
│ ├── err/
│ │ ├── cast_error.rs
│ │ ├── downcast_error.rs
│ │ ├── err_state.rs
│ │ ├── impls.rs
│ │ └── mod.rs
│ ├── exceptions.rs
│ ├── ffi/
│ │ ├── mod.rs
│ │ └── tests.rs
│ ├── ffi_ptr_ext.rs
│ ├── fmt.rs
│ ├── impl_/
│ │ ├── callback.rs
│ │ ├── concat.rs
│ │ ├── coroutine.rs
│ │ ├── deprecated.rs
│ │ ├── exceptions.rs
│ │ ├── extract_argument.rs
│ │ ├── freelist.rs
│ │ ├── frompyobject.rs
│ │ ├── introspection.rs
│ │ ├── panic.rs
│ │ ├── pycell.rs
│ │ ├── pyclass/
│ │ │ ├── assertions.rs
│ │ │ ├── doc.rs
│ │ │ ├── lazy_type_object.rs
│ │ │ └── probes.rs
│ │ ├── pyclass.rs
│ │ ├── pyclass_init.rs
│ │ ├── pyfunction.rs
│ │ ├── pymethods.rs
│ │ ├── pymodule.rs
│ │ ├── trampoline.rs
│ │ ├── unindent.rs
│ │ └── wrap.rs
│ ├── impl_.rs
│ ├── inspect.rs
│ ├── instance.rs
│ ├── internal/
│ │ ├── get_slot.rs
│ │ └── state.rs
│ ├── internal.rs
│ ├── internal_tricks.rs
│ ├── interpreter_lifecycle.rs
│ ├── lib.rs
│ ├── macros.rs
│ ├── marker.rs
│ ├── marshal.rs
│ ├── panic.rs
│ ├── prelude.rs
│ ├── py_result_ext.rs
│ ├── pybacked.rs
│ ├── pycell/
│ │ └── impl_.rs
│ ├── pycell.rs
│ ├── pyclass/
│ │ ├── create_type_object.rs
│ │ ├── gc.rs
│ │ └── guard.rs
│ ├── pyclass.rs
│ ├── pyclass_init.rs
│ ├── sealed.rs
│ ├── sync/
│ │ ├── critical_section.rs
│ │ └── once_lock.rs
│ ├── sync.rs
│ ├── test_utils.rs
│ ├── tests/
│ │ ├── 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
│ │ ├── mutex.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_init.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_pybuffer_drop_without_interpreter.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_utils/
│ └── mod.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
├── deprecated_pyfn.rs
├── deprecated_pyfn.stderr
├── 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_annotation.rs
├── invalid_annotation.stderr
├── invalid_annotation_return.rs
├── invalid_annotation_return.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_doc.rs
├── invalid_pyclass_doc.stderr
├── invalid_pyclass_enum.rs
├── invalid_pyclass_enum.stderr
├── invalid_pyclass_generic.rs
├── invalid_pyclass_generic.stderr
├── invalid_pyclass_init.rs
├── invalid_pyclass_init.stderr
├── invalid_pyclass_item.rs
├── invalid_pyclass_item.stderr
├── invalid_pyfunction_argument.rs
├── invalid_pyfunction_argument.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
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: 🐛 Bug Report
description: Create a bug report
type: Bug
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out this bug report!
Please fill out the form below...
- type: textarea
id: description
attributes:
label: Bug Description
description: Please provide a clear and concise description of what the bug is.
placeholder: The bug is...
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to Reproduce
description: Provide steps to reproduce this bug.
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
id: debug
attributes:
label: Backtrace
description: If your bug produces a backtrace, please include it here.
render: shell
- type: input
id: os_version
attributes:
label: Your operating system and version
validations:
required: true
- type: input
id: py_version
attributes:
label: Your Python version (`python --version`)
placeholder: ex. Python 3.10.0
validations:
required: true
- type: input
id: rust_version
attributes:
label: Your Rust version (`rustc --version`)
placeholder: ex. rustc 1.55.0 (c8dfcfe04 2021-09-06)
validations:
required: true
- type: input
id: pyo3_version
attributes:
label: Your PyO3 version
placeholder: ex. 0.14.0
validations:
required: true
- type: textarea
id: install_method
attributes:
label: How did you install python? Did you use a virtualenv?
placeholder: |
apt
pyenv
pacman
brew
python.org installer
microsoft store
etc...
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Additional Info
description: Any additional info that you think might be useful or relevant to this bug
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: ❓ Question
url: https://github.com/PyO3/pyo3/discussions
about: Ask and answer questions about PyO3 on Discussions
- name: 🔧 Troubleshooting
url: https://github.com/PyO3/pyo3/discussions
about: For troubleshooting help, see the Discussions
- name: 👋 Chat
url: https://discord.gg/33kcChzH7f
about: Engage with PyO3's users and developers on Discord
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: 💡 Feature request
about: Suggest an idea for this project
type: Feature
---
================================================
FILE: .github/actions/fetch-merge-base/action.yml
================================================
name: 'Fetch Merge Base'
description: 'Fetches the merge base between two branches, deepening the git checkout until complete'
inputs:
base_ref:
description: 'The base branch reference'
required: true
head_ref:
description: 'The head branch reference'
required: true
outputs:
merge_base:
description: 'The merge base commit SHA'
value: ${{ steps.fetch_merge_base.outputs.merge_base }}
runs:
using: "composite"
steps:
- name: Fetch Merge Base
id: fetch_merge_base
shell: bash
run: |
# fetch the merge commit between the PR base and head
git fetch -u --progress --depth=1 origin "+$BASE_REF:$BASE_REF" "+$HEAD_REF:$HEAD_REF"
while [ -z "$(git merge-base "$BASE_REF" "$HEAD_REF")" ]; do
git fetch -u -q --deepen="10" origin "$BASE_REF" "$HEAD_REF";
done
MERGE_BASE=$(git merge-base "$BASE_REF" "$HEAD_REF")
echo "merge_base=$MERGE_BASE" >> $GITHUB_OUTPUT
env:
BASE_REF: "${{ inputs.base_ref }}"
HEAD_REF: "${{ inputs.head_ref }}"
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/pyo3-benches/"
schedule:
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/pyo3-ffi-check/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
# dependabot default labels
- "dependencies"
- "github-actions"
# additional labels
- "CI-skip-changelog"
================================================
FILE: .github/pull_request_template.md
================================================
Thank you for contributing to PyO3!
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).
Please consider adding the following to your pull request:
- an entry for this PR in newsfragments - see [https://pyo3.rs/main/contributing.html#documenting-changes]
- or start the PR title with `docs:` if this is a docs-only change to skip the check
- or start the PR title with `ci:` if this is a ci-only change to skip the check
- docs to all new functions and / or detail in the guide
- tests for all new or changed functions
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 locally, you can run `nox`. See `nox --list-sessions` for a list of supported actions.
================================================
FILE: .github/workflows/benches.yml
================================================
name: benches
on:
push:
branches:
- "main"
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}-benches
cancel-in-progress: true
env:
UV_PYTHON: "3.14t"
jobs:
benchmarks:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6.0.2
- uses: astral-sh/setup-uv@v7
with:
# codspeed action needs to be run from within the final Python environment
activate-environment: true
save-cache: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- uses: dtolnay/rust-toolchain@stable
with:
components: rust-src
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
.
pyo3-benches
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
cache-all-crates: "true"
cache-workspace-crates: "true"
- uses: taiki-e/install-action@v2
with:
tool: cargo-codspeed
- name: Run the benchmarks
uses: CodSpeedHQ/action@v4
with:
run: uvx nox -s codspeed
token: ${{ secrets.CODSPEED_TOKEN }}
mode: simulation
================================================
FILE: .github/workflows/build.yml
================================================
on:
workflow_call:
inputs:
os:
required: true
type: string
python-version:
required: true
type: string
python-architecture:
required: true
type: string
rust:
required: true
type: string
rust-target:
required: true
type: string
MSRV:
required: true
type: string
verbose:
type: boolean
default: false
env:
NOX_DEFAULT_VENV_BACKEND: uv
jobs:
build:
continue-on-error: ${{ endsWith(inputs.python-version, '-dev') || contains(fromJSON('["3.7", "3.8"]'), inputs.python-version) || contains(fromJSON('["beta", "nightly"]'), inputs.rust) }}
runs-on: ${{ inputs.os }}
if: ${{ !(startsWith(inputs.python-version, 'graalpy') && startsWith(inputs.os, 'windows')) }}
steps:
- uses: actions/checkout@v6.0.2
with:
# For PRs, we need to run on the real PR head, not the resultant merge of the PR into the target branch.
#
# This is necessary for coverage reporting to make sense; we then get exactly the coverage change
# between the base branch and the real PR head.
#
# If it were run on the merge commit the problem is that the coverage potentially does not align
# with the commit diff, because the merge may affect line numbers.
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
# installs using setup-python do not work for arm macOS 3.9 and below
- if: ${{ !(inputs.os == 'macos-latest' && contains(fromJSON('["3.7", "3.8", "3.9"]'), inputs.python-version) && inputs.python-architecture == 'x64') }}
name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version }}
architecture: ${{ inputs.python-architecture }}
# PyPy can have FFI changes within Python versions, which creates pain in CI
check-latest: ${{ startsWith(inputs.python-version, 'pypy') }}
# workaround for the above, only available for 3.9
- if: ${{ inputs.os == 'macos-latest' && contains(fromJSON('["3.9"]'), inputs.python-version) && inputs.python-architecture == 'x64' }}
name: Set up Python ${{ inputs.python-version }}
uses: astral-sh/setup-uv@v7
with:
python-version: cpython-${{ inputs.python-version }}-macos-x86-64
save-cache: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- name: Install nox
run: python -m pip install --upgrade pip && pip install nox[uv]
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ inputs.rust }}
targets: ${{ inputs.rust-target }}
# rust-src needed to correctly format errors, see #1865
components: rust-src,llvm-tools-preview
# On windows 32 bit, we are running on an x64 host, so we need to specifically set the target
# NB we don't do this for *all* jobs because it breaks coverage of proc macros to have an
# explicit target set.
- name: Set Rust target for Windows 32-bit
if: inputs.os == 'windows-latest' && inputs.python-architecture == 'x86'
shell: bash
run: |
echo "CARGO_BUILD_TARGET=i686-pc-windows-msvc" >> $GITHUB_ENV
# windows on arm image contains x86-64 libclang
- name: Install LLVM and Clang
if: inputs.os == 'windows-11-arm'
uses: KyleMayes/install-llvm-action@v2
with:
# to match windows-2022 images
version: "18"
- name: Install zoneinfo backport for Python 3.7 / 3.8
if: contains(fromJSON('["3.7", "3.8"]'), inputs.python-version)
run: python -m pip install backports.zoneinfo
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- if: inputs.os == 'ubuntu-latest'
name: Prepare LD_LIBRARY_PATH (Ubuntu only)
run: echo LD_LIBRARY_PATH=${pythonLocation}/lib >> $GITHUB_ENV
- if: inputs.rust == inputs.MSRV
name: Prepare MSRV package versions
run: nox -s set-msrv-package-versions
- if: inputs.rust != 'stable'
name: Ignore changed error messages when using trybuild
run: echo "TRYBUILD=overwrite" >> "$GITHUB_ENV"
- uses: dorny/paths-filter@v4
if: ${{ inputs.rust == 'stable' && !startsWith(inputs.python-version, 'graalpy') }}
id: ffi-changes
with:
base: ${{ github.event.merge_group.base_ref }}
ref: ${{ github.event.merge_group.head_ref }}
filters: |
changed:
- 'pyo3-ffi/**'
- 'pyo3-ffi-check/**'
- '.github/workflows/ci.yml'
- '.github/workflows/build.yml'
- name: Run pyo3-ffi-check
# TODO: investigate graalpy failures
if: ${{ endsWith(inputs.python-version, '-dev') || (steps.ffi-changes.outputs.changed == 'true' && inputs.rust == 'stable' && !startsWith(inputs.python-version, 'graalpy')) }}
run: nox -s ffi-check
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Prepare coverage environment
run: |
cargo llvm-cov clean --workspace --profraw-only
nox -s set-coverage-env
- name: Build docs
run: nox -s docs
- name: Run Rust tests
run: nox -s test-rust
- name: Test python examples and tests
shell: bash
run: nox -s test-py
env:
CARGO_TARGET_DIR: ${{ github.workspace }}/target
- name: Generate coverage report
# needs investigation why llvm-cov fails on windows-11-arm
continue-on-error: ${{ inputs.os == 'windows-11-arm' }}
run: cargo llvm-cov
--package=pyo3
--package=pyo3-build-config
--package=pyo3-macros-backend
--package=pyo3-macros
--package=pyo3-ffi
report --codecov --output-path coverage.json
- name: Upload coverage report
uses: codecov/codecov-action@v5
# needs investigation why llvm-cov fails on windows-11-arm
continue-on-error: ${{ inputs.os == 'windows-11-arm' }}
with:
files: coverage.json
name: ${{ inputs.os }}/${{ inputs.python-version }}/${{ inputs.rust }}
token: ${{ secrets.CODECOV_TOKEN }}
env:
CARGO_TERM_VERBOSE: ${{ inputs.verbose }}
RUST_BACKTRACE: 1
RUSTFLAGS: "-D warnings"
RUSTDOCFLAGS: "-D warnings"
================================================
FILE: .github/workflows/cache-cleanup.yml
================================================
name: CI Cache Cleanup
on:
pull_request_target:
types:
- closed
jobs:
cleanup:
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache
echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR
do
gh actions-cache delete -R $REPO -B $BRANCH --confirm -- $cacheKey
done
echo "Done"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
================================================
FILE: .github/workflows/changelog.yml
================================================
name: changelog
on:
pull_request:
types: [opened, synchronize, labeled, unlabeled, edited]
jobs:
check:
name: Check changelog entry
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: '3.14'
- run: python -m pip install --upgrade pip && pip install nox[uv]
- run: nox -s check-changelog
================================================
FILE: .github/workflows/ci-cache-warmup.yml
================================================
name: CI Cache Warmup
on:
push:
branches:
- main
jobs:
cross-compilation-windows:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-gnu,x86_64-pc-windows-msvc
components: rust-src
- uses: actions/cache/restore@v5
with:
# https://github.com/PyO3/maturin/discussions/1953
path: ~/.cache/cargo-xwin
key: cargo-xwin-cache
- name: Test cross compile to Windows
run: |
set -ex
sudo apt-get install -y mingw-w64 llvm
pip install nox
nox -s test-cross-compilation-windows
- uses: actions/cache/save@v5
with:
path: ~/.cache/cargo-xwin
key: cargo-xwin-cache
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
pull_request:
merge_group:
types: [checks_requested]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
jobs:
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- run: python -m pip install --upgrade pip && pip install nox[uv]
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Check python formatting and lints (ruff)
run: nox -s ruff
- name: Check rust formatting (rustfmt)
run: nox -s rustfmt
- name: Check markdown formatting (rumdl)
run: nox -s rumdl
resolve:
runs-on: ubuntu-latest
outputs:
MSRV: ${{ steps.resolve-msrv.outputs.MSRV }}
verbose: ${{ runner.debug == '1' }}
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: resolve MSRV
id: resolve-msrv
run: echo MSRV=`python -c 'import tomllib; print(tomllib.load(open("Cargo.toml", "rb"))["workspace"]["package"]["rust-version"])'` >> $GITHUB_OUTPUT
semver-checks:
if: github.event_name == 'pull_request'
needs: [fmt]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: Fetch merge base
id: fetch_merge_base
uses: ./.github/actions/fetch-merge-base
with:
base_ref: "refs/heads/${{ github.event.pull_request.base.ref }}"
head_ref: "refs/pull/${{ github.event.pull_request.number }}/merge"
- uses: obi1kenobi/cargo-semver-checks-action@v2
with:
baseline-rev: ${{ steps.fetch_merge_base.outputs.merge_base }}
check-msrv:
needs: [fmt, resolve]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ needs.resolve.outputs.MSRV }}
components: rust-src
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- run: python -m pip install --upgrade pip && pip install nox[uv]
# This is a smoke test to confirm that CI will run on MSRV (including dev dependencies)
- name: Check with MSRV package versions
run: |
nox -s set-msrv-package-versions
nox -s check-all
env:
CARGO_BUILD_TARGET: x86_64-unknown-linux-gnu
clippy:
needs: [fmt]
runs-on: ubuntu-24.04-arm
strategy:
# If one platform fails, allow the rest to keep testing if `CI-no-fail-fast` label is present
fail-fast: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-no-fail-fast') }}
matrix:
rust: [stable]
target:
[
"aarch64-apple-darwin",
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"s390x-unknown-linux-gnu",
"wasm32-wasip1",
"x86_64-pc-windows-msvc",
"i686-pc-windows-msvc",
"aarch64-pc-windows-msvc",
]
include:
# Run beta clippy as a way to detect any incoming lints which may affect downstream users
- rust: beta
target: "x86_64-unknown-linux-gnu"
name: clippy/${{ matrix.target }}/${{ matrix.rust }}
continue-on-error: ${{ matrix.rust != 'stable' }}
steps:
- uses: actions/checkout@v6.0.2
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
components: clippy,rust-src
- uses: astral-sh/setup-uv@v7
with:
save-cache: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- run: uvx nox -s clippy-all
env:
CARGO_BUILD_TARGET: ${{ matrix.target }}
build-pr:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-build-full') && github.event_name == 'pull_request' }}
name: python${{ matrix.python-version }}-${{ matrix.platform.python-architecture }} ${{ matrix.platform.os }} rust-${{ matrix.rust }}
needs: [fmt, resolve]
uses: ./.github/workflows/build.yml
with:
os: ${{ matrix.platform.os }}
python-version: ${{ matrix.python-version }}
python-architecture: ${{ matrix.platform.python-architecture }}
rust: ${{ matrix.rust }}
rust-target: ${{ matrix.platform.rust-target }}
MSRV: ${{ needs.resolve.outputs.MSRV }}
verbose: ${{ needs.resolve.outputs.verbose == 'true' }}
secrets: inherit
strategy:
# If one platform fails, allow the rest to keep testing if `CI-no-fail-fast` label is present
fail-fast: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-no-fail-fast') }}
matrix:
rust: [stable]
python-version: ["3.14"]
platform:
[
{
os: "macos-latest",
python-architecture: "arm64",
rust-target: "aarch64-apple-darwin",
},
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
},
{
os: "ubuntu-24.04-arm",
python-architecture: "arm64",
rust-target: "aarch64-unknown-linux-gnu",
},
{
os: "windows-latest",
python-architecture: "x64",
rust-target: "x86_64-pc-windows-msvc",
},
{
os: "windows-latest",
python-architecture: "x86",
rust-target: "i686-pc-windows-msvc",
},
{
os: "windows-11-arm",
python-architecture: "arm64",
rust-target: "aarch64-pc-windows-msvc",
},
]
include:
# Test nightly Rust on PRs so that PR authors have a chance to fix nightly
# failures, as nightly does not block merge.
- rust: nightly
python-version: "3.14"
platform:
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}
# Also test free-threaded Python just for latest Python version, on ubuntu
# (run for all OSes on build-full)
- rust: stable
python-version: "3.14t"
platform:
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}
build-full:
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
name: python${{ matrix.python-version }}-${{ matrix.platform.python-architecture }} ${{ matrix.platform.os }} rust-${{ matrix.rust }}
needs: [fmt, resolve]
uses: ./.github/workflows/build.yml
with:
os: ${{ matrix.platform.os }}
python-version: ${{ matrix.python-version }}
python-architecture: ${{ matrix.platform.python-architecture }}
rust: ${{ matrix.rust }}
rust-target: ${{ matrix.platform.rust-target }}
MSRV: ${{ needs.resolve.outputs.MSRV }}
verbose: ${{ needs.resolve.outputs.verbose == 'true' }}
secrets: inherit
strategy:
# If one platform fails, allow the rest to keep testing if `CI-no-fail-fast` label is present
fail-fast: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-no-fail-fast') }}
matrix:
rust: [stable]
python-version:
[
"3.7",
"3.8",
"3.9",
"3.10",
"3.11",
"3.12",
"3.13",
"3.14",
"3.14t",
"3.15-dev",
"3.15t-dev",
"pypy3.11",
"graalpy25.0",
]
platform:
[
{
os: "macos-latest",
python-architecture: "arm64",
rust-target: "aarch64-apple-darwin",
},
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
},
{
os: "windows-latest",
python-architecture: "x64",
rust-target: "x86_64-pc-windows-msvc",
},
]
include:
# Test minimal supported Rust version
- rust: ${{ needs.resolve.outputs.MSRV }}
python-version: "3.14"
platform:
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}
# Test the `nightly` feature
- rust: nightly
python-version: "3.14"
platform:
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}
# Run rust beta to help catch toolchain regressions
- rust: beta
python-version: "3.14"
platform:
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}
# Test 32-bit Windows and x64 macOS only with the latest Python version
- rust: stable
python-version: "3.14"
platform:
{
os: "windows-latest",
python-architecture: "x86",
rust-target: "i686-pc-windows-msvc",
}
- rust: stable
python-version: "3.14"
platform:
{
os: "macos-latest",
python-architecture: "x64",
rust-target: "x86_64-apple-darwin",
}
# ubuntu-latest (24.04) no longer supports 3.7, so run on 22.04
- rust: stable
python-version: "3.7"
platform:
{
os: "ubuntu-22.04",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}
# arm64 macOS Python not available on GitHub Actions until 3.10, test older versions on x64
- rust: stable
python-version: "3.7"
platform:
{
os: "macos-15-intel",
python-architecture: "x64",
rust-target: "x86_64-apple-darwin",
}
- rust: stable
python-version: "3.8"
platform:
{
os: "macos-15-intel",
python-architecture: "x64",
rust-target: "x86_64-apple-darwin",
}
- rust: stable
python-version: "3.9"
platform:
{
os: "macos-15-intel",
python-architecture: "x64",
rust-target: "x86_64-apple-darwin",
}
# test latest Python on arm64 linux & windows runners
- rust: stable
python-version: "3.14"
platform:
{
os: "ubuntu-24.04-arm",
python-architecture: "arm64",
rust-target: "aarch64-unknown-linux-gnu",
}
- rust: stable
python-version: "3.14"
platform:
{
os: "windows-11-arm",
python-architecture: "arm64",
rust-target: "aarch64-pc-windows-msvc",
}
exclude:
# ubuntu-latest (24.04) no longer supports 3.7
- python-version: "3.7"
platform: { os: "ubuntu-latest" }
# arm64 macOS Python not available on GitHub Actions until 3.10
- rust: stable
python-version: "3.7"
platform:
{
os: "macos-latest",
python-architecture: "arm64",
rust-target: "aarch64-apple-darwin",
}
- rust: stable
python-version: "3.8"
platform:
{
os: "macos-latest",
python-architecture: "arm64",
rust-target: "aarch64-apple-darwin",
}
- rust: stable
python-version: "3.9"
platform:
{
os: "macos-latest",
python-architecture: "arm64",
rust-target: "aarch64-apple-darwin",
}
valgrind:
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
needs: [fmt]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@valgrind
- run: python -m pip install --upgrade pip && pip install nox[uv]
- run: nox -s test-rust -- release skip-full
env:
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: valgrind --leak-check=no --error-exitcode=1
RUST_BACKTRACE: 1
TRYBUILD: overwrite
careful:
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
needs: [fmt]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- uses: taiki-e/install-action@cargo-careful
- run: python -m pip install --upgrade pip && pip install nox[uv]
- run: nox -s test-rust -- careful skip-full
env:
RUST_BACKTRACE: 1
TRYBUILD: overwrite
docsrs:
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
needs: [fmt]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- run: cargo rustdoc --lib --no-default-features --features full,jiff-02 -Zunstable-options --config "build.rustdocflags=[\"--cfg\", \"docsrs\"]"
emscripten:
name: emscripten
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
needs: [fmt]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: 3.14
id: setup-python
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-emscripten
components: rust-src
- uses: actions/setup-node@v6
with:
node-version: 24
- run: python -m pip install --upgrade pip && pip install nox[uv]
- uses: actions/cache/restore@v5
id: cache
with:
path: |
.nox/emscripten
key: emscripten-${{ hashFiles('emscripten/*') }}-${{ hashFiles('noxfile.py') }}-${{ steps.setup-python.outputs.python-path }}
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
run: nox -s build-emscripten
- name: Test
run: nox -s test-emscripten
- uses: actions/cache/save@v5
if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
with:
path: |
.nox/emscripten
key: emscripten-${{ hashFiles('emscripten/*') }}-${{ hashFiles('noxfile.py') }}-${{ steps.setup-python.outputs.python-path }}
wasm32-wasip1:
name: wasm32-wasip1
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
needs: [fmt]
runs-on: ubuntu-latest
env:
WASI_SDK_PATH: "/opt/wasi-sdk"
CPYTHON_PATH: "${{ github.workspace }}/wasi/cpython"
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: 3.14
id: setup-python
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-wasip1
components: rust-src
- name: "Install wasmtime"
uses: bytecodealliance/actions/wasmtime/setup@v1
- name: "Install WASI SDK"
run: |
mkdir ${{ env.WASI_SDK_PATH }} && \
curl -s -S --location https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-24/wasi-sdk-24.0-x86_64-linux.tar.gz | \
tar --strip-components 1 --directory ${{ env.WASI_SDK_PATH }} --extract --gunzip
$WASI_SDK_PATH/bin/clang --version
- uses: actions/cache/restore@v5
id: cache-wasip1-python
with:
path: ${{ env.CPYTHON_PATH }}/cross-build/
key: wasm32-wasip1-python
- uses: actions/checkout@v6.0.2
with:
repository: python/cpython
ref: 3.14
path: ${{ env.CPYTHON_PATH }}
fetch-depth: 1
- name: Build
run: |
cd ${{ env.CPYTHON_PATH }}
cat >> Tools/wasm/wasi/config.site-wasm32-wasi <<'EOF'
# Force-disable POSIX dynamic loading for WASI
ac_cv_func_dlopen=no
ac_cv_lib_dl_dlopen=no
EOF
python Tools/wasm/wasi build --quiet -- --config-cache
cp cross-build/wasm32-wasip1/libpython3.14.a \
cross-build/wasm32-wasip1/Modules/_hacl/libHacl_HMAC.a \
cross-build/wasm32-wasip1/Modules/_decimal/libmpdec/libmpdec.a \
cross-build/wasm32-wasip1/Modules/expat/libexpat.a \
cross-build/wasm32-wasip1/build/lib.wasi-wasm32-3.14/
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- name: Test
env:
PYO3_CROSS_LIB_DIR: ${{ env.CPYTHON_PATH }}/cross-build/wasm32-wasip1/build/lib.wasi-wasm32-3.14/
CARGO_BUILD_TARGET: wasm32-wasip1
CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime run --dir ${{ env.CPYTHON_PATH }}::/ --env PYTHONPATH=/lib
RUSTFLAGS: >
-C link-arg=-L${{ env.WASI_SDK_PATH }}/share/wasi-sysroot/lib/wasm32-wasi
-C link-arg=-lwasi-emulated-signal
-C link-arg=-lwasi-emulated-process-clocks
-C link-arg=-lwasi-emulated-getpid
-C link-arg=-lmpdec
-C link-arg=-lHacl_HMAC
-C link-arg=-lexpat
run: RUSTDOCFLAGS=$RUSTFLAGS cargo test
- uses: actions/cache/save@v5
if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
with:
path: ${{ env.CPYTHON_PATH }}/cross-build/
key: ${{ steps.cache-wasip1-python.outputs.cache-primary-key }}
test-debug:
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
needs: [fmt]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- uses: dtolnay/rust-toolchain@stable
with:
components: rust-src
- name: Install python3 standalone debug build with nox
run: |
PBS_RELEASE="20241219"
PBS_PYTHON_VERSION="3.13.1"
PBS_ARCHIVE="cpython-${PBS_PYTHON_VERSION}+${PBS_RELEASE}-x86_64-unknown-linux-gnu-debug-full.tar.zst"
wget "https://github.com/indygreg/python-build-standalone/releases/download/${PBS_RELEASE}/${PBS_ARCHIVE}"
tar -I zstd -xf "${PBS_ARCHIVE}"
ls -l $(pwd)/python/install/bin
ls -l $(pwd)/python/install/lib
echo PATH=$(pwd)/python/install/bin:$PATH >> $GITHUB_ENV
echo LD_LIBRARY_PATH=$(pwd)/python/install/lib:$LD_LIBRARY_PATH >> $GITHUB_ENV
echo PYTHONHOME=$(pwd)/python/install >> $GITHUB_ENV
echo PYO3_PYTHON=$(pwd)/python/install/bin/python3 >> $GITHUB_ENV
- run: python3 -m sysconfig
- run: python3 -m pip install --upgrade pip && pip install nox[uv]
- run: |
PYO3_CONFIG_FILE=$(mktemp)
cat > $PYO3_CONFIG_FILE << EOF
implementation=CPython
version=3.13
shared=true
abi3=false
lib_name=python3.13d
lib_dir=${{ github.workspace }}/python/install/lib
executable=${{ github.workspace }}/python/install/bin/python3
pointer_width=64
build_flags=Py_DEBUG,Py_REF_DEBUG
suppress_build_script_link_lines=false
EOF
echo PYO3_CONFIG_FILE=$PYO3_CONFIG_FILE >> $GITHUB_ENV
- run: python3 -m nox -s test
test-version-limits:
needs: [fmt]
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- uses: dtolnay/rust-toolchain@stable
- run: python3 -m pip install --upgrade pip && pip install nox[uv]
- run: python3 -m nox -s test-version-limits
check-feature-powerset:
needs: [fmt, resolve]
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
runs-on: ubuntu-latest
name: check-feature-powerset ${{ matrix.rust }}
strategy:
# run on stable and MSRV to check that all combinations of features are expected to build fine on our supported
# range of compilers
matrix:
rust: ["stable"]
include:
- rust: ${{ needs.resolve.outputs.MSRV }}
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- uses: taiki-e/install-action@v2
with:
tool: cargo-hack,cargo-minimal-versions
- run: python3 -m pip install --upgrade pip && pip install nox[uv]
- run: python3 -m nox -s check-feature-powerset -- ${{ matrix.rust != 'stable' && 'minimal-versions' || '' }}
test-cross-compilation:
needs: [fmt]
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
runs-on: ${{ matrix.os }}
name: test-cross-compilation ${{ matrix.os }} -> ${{ matrix.target }}
strategy:
# If one platform fails, allow the rest to keep testing if `CI-no-fail-fast` label is present
fail-fast: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-no-fail-fast') }}
matrix:
include:
# ubuntu "cross compile" to itself
- os: "ubuntu-latest"
target: "x86_64-unknown-linux-gnu"
flags: "-i python3.13"
manylinux: auto
# ubuntu x86_64 -> aarch64
- os: "ubuntu-latest"
target: "aarch64-unknown-linux-gnu"
flags: "-i python3.13"
manylinux: auto
# ubuntu x86_64 -> windows x86_64
- os: "ubuntu-latest"
target: "x86_64-pc-windows-gnu"
# TODO: remove pyo3/generate-import-lib feature when maturin supports cross compiling to Windows without it
flags: "-i python3.13 --features pyo3/generate-import-lib"
# windows x86_64 -> aarch64
- os: "windows-latest"
target: "aarch64-pc-windows-msvc"
flags: "-i python3.13 --features pyo3/generate-import-lib"
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: Swatinem/rust-cache@v2
with:
workspaces: examples/maturin-starter
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
key: ${{ matrix.target }}
- name: Setup cross-compiler
if: ${{ matrix.target == 'x86_64-pc-windows-gnu' }}
run: sudo apt-get install -y mingw-w64 llvm
- name: Compile version-specific library
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: ${{ matrix.manylinux }}
args: --release -m examples/maturin-starter/Cargo.toml ${{ matrix.flags }}
- name: Compile abi3 library
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: ${{ matrix.manylinux }}
args: --release -m examples/maturin-starter/Cargo.toml --features abi3 ${{ matrix.flags }}
test-cross-compilation-windows:
needs: [fmt]
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-gnu,x86_64-pc-windows-msvc
components: rust-src
# load cache (prepared in ci-cache-warmup.yml)
- uses: actions/cache/restore@v5
with:
path: ~/.cache/cargo-xwin
key: cargo-xwin-cache
- name: Test cross compile to Windows
run: |
set -ex
sudo apt-get install -y mingw-w64 llvm
pip install nox
nox -s test-cross-compilation-windows
test-introspection:
needs: [fmt]
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-test-introspection') || contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
strategy:
matrix:
platform:
[
{
os: "macos-latest",
python-architecture: "arm64",
rust-target: "aarch64-apple-darwin",
},
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
},
{
os: "windows-latest",
python-architecture: "x64",
rust-target: "x86_64-pc-windows-msvc",
},
{
os: "windows-latest",
python-architecture: "x86",
rust-target: "i686-pc-windows-msvc",
},
]
runs-on: ${{ matrix.platform.os }}
steps:
- uses: actions/checkout@v6.0.2
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.platform.rust-target }}
- uses: actions/setup-python@v6
with:
python-version: "3.14"
architecture: ${{ matrix.platform.python-architecture }}
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
- run: python -m pip install --upgrade pip && pip install nox[uv]
- run: nox -s test-introspection
env:
CARGO_BUILD_TARGET: ${{ matrix.platform.rust-target }}
test-introspection-pr:
needs: [fmt]
if: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-build-full') && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- run: python -m pip install --upgrade pip && pip install nox[uv]
- run: nox -s test-introspection
mypy-pytests:
needs: [fmt]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: dtolnay/rust-toolchain@stable
with:
components: rust-src
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- run: python -m pip install --upgrade pip && pip install nox[uv]
- run: nox -s mypy
working-directory: pytests
conclusion:
needs:
- fmt
- check-msrv
- clippy
- build-pr
- build-full
- valgrind
- careful
- docsrs
- emscripten
- test-debug
- test-version-limits
- check-feature-powerset
- test-cross-compilation
- test-cross-compilation-windows
- test-introspection
if: always()
runs-on: ubuntu-latest
steps:
- name: Result
run: |
jq -C <<< "${needs}"
# Check if all needs were successful or skipped.
"$(jq -r 'all(.result as $result | (["success", "skipped"] | contains([$result])))' <<< "${needs}")"
env:
needs: ${{ toJson(needs) }}
================================================
FILE: .github/workflows/coverage-pr-base.yml
================================================
# This runs as a separate job because it needs to run on the `pull_request_target` event
# in order to access the CODECOV_TOKEN secret.
#
# This is safe because this doesn't run arbitrary code from PRs.
name: Set Codecov PR base
on:
# See safety note / doc at the top of this file.
pull_request_target:
jobs:
coverage-pr-base:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: '3.14'
- name: Fetch merge base
id: fetch_merge_base
uses: ./.github/actions/fetch-merge-base
with:
base_ref: "refs/heads/${{ github.event.pull_request.base.ref }}"
head_ref: "refs/pull/${{ github.event.pull_request.number }}/head"
- name: Set PR base on codecov
run: |
pip install codecov-cli
codecovcli pr-base-picking \
--base-sha ${{ steps.fetch_merge_base.outputs.merge_base }} \
--pr ${{ github.event.number }} \
--slug PyO3/pyo3 \
--token ${{ secrets.CODECOV_TOKEN }} \
--service github
================================================
FILE: .github/workflows/netlify-build.yml
================================================
name: netlify-build
on:
push:
branches:
- main
pull_request:
release:
types: [published]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
jobs:
guide-build:
runs-on: ubuntu-latest
outputs:
tag_name: ${{ steps.prepare_tag.outputs.tag_name }}
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-python@v6
with:
python-version: "3.14"
- uses: dtolnay/rust-toolchain@nightly
- name: Setup mdBook
uses: taiki-e/install-action@v2
with:
tool: mdbook@0.5, mdbook-tabs@0.3, lychee
- name: Prepare tag
id: prepare_tag
run: |
TAG_NAME="${GITHUB_REF##*/}"
echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT
- name: Restore lychee cache
id: restore-cache
uses: actions/cache/restore@v5
with:
path: .lycheecache
key: lychee
# This builds the book in target/guide/.
- name: Build the guide
run: |
python -m pip install --upgrade pip && pip install nox[uv]
nox -s ${{ github.event_name == 'release' && 'build-guide' || 'check-guide' }}
env:
PYO3_VERSION_TAG: ${{ steps.prepare_tag.outputs.tag_name }}
# allows lychee to get better rate limits from github
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Save lychee cache
uses: actions/cache/save@v5
if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }}
with:
path: .lycheecache
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
# We store the versioned guides on GitHub's gh-pages branch for convenience
# (the full gh-pages branch is pulled in the build-netlify-site step)
- name: Deploy the guide
if: ${{ github.event_name == 'release' }}
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./target/guide/
destination_dir: ${{ steps.prepare_tag.outputs.tag_name }}
full_commit_message: "Upload documentation for ${{ steps.prepare_tag.outputs.tag_name }}"
- name: Get current PyO3 version
run: |
PYO3_VERSION=$(cargo search pyo3 --limit 1 | head -1 | tr -s ' ' | cut -d ' ' -f 3 | tr -d '"')
echo "PYO3_VERSION=${PYO3_VERSION}" >> $GITHUB_ENV
- name: Build the site
run: |
python -m pip install --upgrade pip && pip install nox[uv] towncrier requests
nox -s build-netlify-site -- ${{ (github.ref != 'refs/heads/main' && '--preview') || '' }}
# Upload the built site as an artifact for deploy workflow to consume
- name: Upload Build Artifact
uses: actions/upload-artifact@v7
with:
name: site
path: ./netlify_build
================================================
FILE: .github/workflows/netlify-deploy.yml
================================================
# This runs as a separate job because it needs to run on the `workflow_run` event
# in order to access the netlify secrets.
#
# This is safe because this doesn't run arbitrary code from PRs.
name: netlify-deploy
on:
workflow_run:
workflows: ["netlify-build"]
types:
- completed
env:
CARGO_TERM_COLOR: always
jobs:
deploy:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success'
environment: netlify
steps:
- name: Download Build Artifact
uses: actions/download-artifact@v8
with:
name: site
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
- name: Install netlify-cli
run: |
npm install -g netlify-cli
- name: Deploy to Netlify
run: |
ls -la
DEBUG=* netlify deploy \
--site ${{ secrets.NETLIFY_SITE_ID }} \
--auth ${{ secrets.NETLIFY_TOKEN }} \
${{ ((github.event.workflow_run.head_repository.full_name == 'PyO3/pyo3') && (github.event.workflow_run.head_branch == 'main') && '--prod') || '' }} \
--json | tee deploy_output.json
# credit: https://www.raulmelo.me/en/blog/deploying-netlify-github-actions-guide
- name: Generate URL Preview
id: url_preview
if: ${{ github.event.workflow_run.event == 'pull_request' }}
run: |
NETLIFY_PREVIEW_URL=$(jq -r '.deploy_url' deploy_output.json)
echo "NETLIFY_PREVIEW_URL=$NETLIFY_PREVIEW_URL" >> "$GITHUB_OUTPUT"
- name: Post Netlify Preview Status to PR
if: ${{ github.event.workflow_run.event == 'pull_request' }}
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const previewUrl = '${{ steps.url_preview.outputs.NETLIFY_PREVIEW_URL }}';
const commitSha = '${{ github.event.workflow_run.head_sha }}';
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: commitSha,
state: 'success',
target_url: previewUrl,
description: 'click to view Netlify preview deploy',
context: 'netlify-deploy / preview'
});
================================================
FILE: .github/workflows/release.yml
================================================
name: Release Rust Crate
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
version:
description: The version to build
jobs:
release:
permissions:
id-token: write
runs-on: ubuntu-latest
environment: release
steps:
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v5.0.1
with:
# The tag to build or the tag received by the tag event
ref: ${{ github.event.inputs.version || github.ref }}
persist-credentials: false
- uses: astral-sh/setup-uv@v7
with:
save-cache: false
- uses: rust-lang/crates-io-auth-action@v1
id: auth
- name: Publish to crates.io
run: uvx nox -s publish
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
================================================
FILE: .gitignore
================================================
target
Cargo.lock
/doc
/gh-pages
build/
*.py[co]
__pycache__/
.cache
.pytest_cache/
dist/
.tox/
.mypy_cache/
.hypothesis/
.eggs/
venv*
guide/book/
guide/src/LICENSE-APACHE
guide/src/LICENSE-MIT
*.so
*.out
*.egg-info
extensions/stamps/
pip-wheel-metadata
valgrind-python.supp
*.pyd
lcov.info
coverage.json
netlify_build/
.nox/
.vscode/
.lycheecache
================================================
FILE: .netlify/internal_banner.html
================================================
================================================
FILE: .netlify/redirect.sh
================================================
# Add redirect for each documented version
set +x # these loops get very spammy and fill the deploy log
for d in netlify_build/v*; do
version="${d/netlify_build\/v/}"
echo "/v$version/doc/* https://docs.rs/pyo3/$version/:splat" >> netlify_build/_redirects
if [ $version != $PYO3_VERSION ]; then
# for old versions, mark the files in the latest version as the canonical URL
for file in $(find $d -type f); do
file_path="${file/$d\//}"
# remove index.html and/or .html suffix to match the page URL on the
# final netlfiy site
url_path="$file_path"
if [[ $file_path == index.html ]]; then
url_path=""
elif [[ $file_path == *.html ]]; then
url_path="${file_path%.html}"
fi
echo "/v$version/$url_path" >> netlify_build/_headers
if test -f "netlify_build/v$PYO3_VERSION/$file_path"; then
echo " Link: ; rel=\"canonical\"" >> netlify_build/_headers
else
# this file doesn't exist in the latest guide, don't index it
echo " X-Robots-Tag: noindex" >> netlify_build/_headers
fi
done
fi
done
# Add latest redirect
echo "/latest/* /v${PYO3_VERSION}/:splat 302" >> netlify_build/_redirects
# some backwards compatbiility redirects
echo "/latest/building_and_distribution/* /latest/building-and-distribution/:splat 302" >> netlify_build/_redirects
echo "/latest/building-and-distribution/multiple_python_versions/* /latest/building-and-distribution/multiple-python-versions:splat 302" >> netlify_build/_redirects
echo "/latest/function/error_handling/* /latest/function/error-handling/:splat 302" >> netlify_build/_redirects
echo "/latest/getting_started/* /latest/getting-started/:splat 302" >> netlify_build/_redirects
echo "/latest/python_from_rust/* /latest/python-from-rust/:splat 302" >> netlify_build/_redirects
echo "/latest/python_typing_hints/* /latest/python-typing-hints/:splat 302" >> netlify_build/_redirects
echo "/latest/trait_bounds/* /latest/trait-bounds/:splat 302" >> netlify_build/_redirects
## Add landing page redirect
if [ "${CONTEXT}" == "deploy-preview" ]; then
echo "/ /main/" >> netlify_build/_redirects
else
echo "/ /v${PYO3_VERSION}/ 302" >> netlify_build/_redirects
fi
set -x
================================================
FILE: .towncrier.template.md
================================================
{% for section_text, section in sections.items() %}{%- if section %}{{section_text}}{% endif -%}
{% if section %}
{% for category in ['packaging', 'added', 'changed', 'removed', 'fixed' ] if category in section %}
### {{ definitions[category]['name'] }}
{% if definitions[category]['showcontent'] %}
{% for text, pull_requests in section[category].items() %}
- {{ text }} {{ pull_requests|join(', ') }}
{% endfor %}
{% else %}
- {{ section[category]['']|join(', ') }}
{% endif %}
{% endfor %}
{% else %}
No significant changes.
{% endif %}
{% endfor %}
================================================
FILE: Architecture.md
================================================
# PyO3: Architecture
This document roughly describes the high-level architecture of PyO3.
If you want to become familiar with the codebase you are in the right place!
## Overview
PyO3 provides a bridge between Rust and Python, based on the [Python/C API].
Thus, PyO3 has low-level bindings of these API as its core.
On top of that, we have higher-level bindings to operate Python objects safely.
Also, to define Python classes and functions in Rust code, we have `trait PyClass` and a set of
protocol traits (e.g., `PyIterProtocol`) for supporting object protocols (i.e., `__dunder__` methods).
Since implementing `PyClass` requires lots of boilerplate, we have a proc-macro `#[pyclass]`.
To summarize, there are six main parts to the PyO3 codebase.
1. [Low-level bindings of Python/C API.](#1-low-level-bindings-of-python-capi)
- [`pyo3-ffi`] and [`src/ffi`]
2. [Bindings to Python objects.](#2-bindings-to-python-objects)
- [`src/instance.rs`] and [`src/types`]
3. [`PyClass` and related functionalities.](#3-pyclass-and-related-functionalities)
- [`src/pycell.rs`], [`src/pyclass.rs`], and more
4. [Procedural macros to simplify usage for users.](#4-procedural-macros-to-simplify-usage-for-users)
- [`src/impl_`], [`pyo3-macros`] and [`pyo3-macros-backend`]
5. [`build.rs` and `pyo3-build-config`](#5-buildrs-and-pyo3-build-config)
- [`build.rs`](https://github.com/PyO3/pyo3/tree/main/build.rs)
- [`pyo3-build-config`]
## 1. Low-level bindings of Python/C API
[`pyo3-ffi`] contains wrappers of the [Python/C API]. This is currently done by hand rather than
automated tooling because:
- it gives us best control about how to adapt C conventions to Rust, and
- there are many Python interpreter versions we support in a single set of files.
We aim to provide straight-forward Rust wrappers resembling the file structure of [`cpython/Include`](https://github.com/python/cpython/tree/main/Include).
We are continuously updating the module to match the latest CPython version which PyO3 supports (i.e. as of time of writing Python 3.13). The tracking issue is [#1289](https://github.com/PyO3/pyo3/issues/1289), and contribution is welcome.
In the [`pyo3-ffi`] crate, there is lots of conditional compilation such as `#[cfg(Py_LIMITED_API)]`,
`#[cfg(Py_3_7)]`, and `#[cfg(PyPy)]`.
`Py_LIMITED_API` corresponds to `#define Py_LIMITED_API` macro in Python/C API.
With `Py_LIMITED_API`, we can build a Python-version-agnostic binary called an
[abi3 wheel](https://pyo3.rs/latest/building-and-distribution.html#py_limited_apiabi3).
`Py_3_7` means that the API is available from Python >= 3.7.
There are also `Py_3_8`, `Py_3_9`, and so on.
`PyPy` means that the API definition is for PyPy.
Those flags are set in [`build.rs`](#6-buildrs-and-pyo3-build-config).
## 2. Bindings to Python objects
[`src/types`] contains bindings to [built-in types](https://docs.python.org/3/library/stdtypes.html)
of Python, such as `dict` and `list`.
For historical reasons, Python's `object` is called `PyAny` in PyO3 and located in [`src/types/any.rs`].
Currently, `PyAny` is a straightforward wrapper of `ffi::PyObject`, defined as:
```rust
#[repr(transparent)]
pub struct PyAny(UnsafeCell);
```
Concrete Python objects are implemented by wrapping `PyAny`, e.g.,:
```rust
#[repr(transparent)]
pub struct PyDict(PyAny);
```
These types are not intended to be accessed directly, and instead are used through the `Py` and `Bound` smart pointers.
We have some macros in [`src/types/mod.rs`] which make it easier to implement APIs for concrete Python types.
## 3. `PyClass` and related functionalities
[`src/pycell.rs`], [`src/pyclass.rs`], and [`src/type_object.rs`] contain types and
traits to make `#[pyclass]` work.
Also, [`src/pyclass_init.rs`] and [`src/impl_/pyclass.rs`] have related functionalities.
To realize object-oriented programming in C, all Python objects have `ob_base: PyObject` as their
first field in their structure definition. Thanks to this guarantee, casting `*mut A` to `*mut PyObject`
is valid if `A` is a Python object.
To ensure this guarantee, we have a wrapper struct `PyClassObject` in [`src/pycell/impl_.rs`] which is roughly:
```rust
#[repr(C)]
pub struct PyClassObject {
ob_base: crate::ffi::PyObject,
inner: T,
}
```
Thus, when copying a Rust struct to a Python object, we first allocate `PyClassObject` on the Python heap and then
move `T` into it.
The primary way to interact with Python objects implemented in Rust is through the `Bound<'py, T>` smart pointer.
By having the `'py` lifetime of the `Python<'py>` token, this ties the lifetime of the `Bound<'py, T>` smart pointer to the lifetime for which the thread is attached to the Python interpreter and allows PyO3 to call Python APIs at maximum efficiency.
`Bound<'py, T>` requires that `T` implements `PyClass`.
This trait is somewhat complex and derives many traits, but the most important one is `PyTypeInfo`
in [`src/type_object.rs`].
`PyTypeInfo` is also implemented for built-in types.
In Python, all objects have their types, and types are also objects of `type`.
For example, you can see `type({})` shows `dict` and `type(type({}))` shows `type` in Python REPL.
`T: PyTypeInfo` implies that `T` has a corresponding type object.
### Protocol methods
Python has some built-in special methods called dunder methods, such as `__iter__`.
They are called "slots" in the [abstract objects layer](https://docs.python.org/3/c-api/abstract.html) in
Python/C API.
We provide a way to implement those protocols similarly, by recognizing special
names in `#[pymethods]`, with a few new ones for slots that can not be
implemented in Python, such as GC support.
## 4. Procedural macros to simplify usage for users.
[`pyo3-macros`] provides five proc-macro APIs: `pymodule`, `pyfunction`, `pyclass`,
`pymethods`, and `#[derive(FromPyObject)]`.
[`pyo3-macros-backend`] has the actual implementations of these APIs.
[`src/impl_`] contains `#[doc(hidden)]` functionality used in code generated by these proc-macros,
such as parsing function arguments.
## 5. `build.rs` and `pyo3-build-config`
PyO3 supports a wide range of OSes, interpreters and use cases. The correct environment must be
detected at build time in order to set up relevant conditional compilation correctly. This logic
is captured in the [`pyo3-build-config`] crate, which is a `build-dependency` of `pyo3` and
`pyo3-macros`, and can also be used by downstream users in the same way.
In [`pyo3-build-config`]'s `build.rs` the build environment is detected and inlined into the crate
as a "config file". This works in all cases except for cross-compiling, where it is necessary to
capture this from the `pyo3` `build.rs` to get some extra environment variables that Cargo doesn't
set for build dependencies.
The `pyo3` `build.rs` also runs some safety checks such as ensuring the Python version detected is
actually supported.
Some of the functionality of `pyo3-build-config`:
- Find the interpreter for build and detect the Python version.
- We have to set some version flags like `#[cfg(Py_3_7)]`.
- If the interpreter is PyPy, we set `#[cfg(PyPy)`.
- If the `PYO3_CONFIG_FILE` environment variable is set then that file's contents will be used
instead of any detected configuration.
- If the `PYO3_NO_PYTHON` environment variable is set then the interpreter detection is bypassed
entirely and only abi3 extensions can be built.
- Check if we are building a Python extension.
- If we are building an extension (e.g., Python library installable by `pip`),
we don't link `libpython` on most platforms (to allow for statically-linked Python interpreters).
The `PYO3_BUILD_EXTENSION_MODULE` environment variable suppresses linking.
- Cross-compiling configuration
- If `TARGET` architecture and `HOST` architecture differ, we can find cross compile information
from environment variables (`PYO3_CROSS_LIB_DIR`, `PYO3_CROSS_PYTHON_VERSION` and
`PYO3_CROSS_PYTHON_IMPLEMENTATION`) or system files.
When cross compiling extension modules it is often possible to make it work without any
additional user input.
- On Windows, `pyo3-ffi` uses Rust's `raw-dylib` linking feature to link against the Python DLL
directly without needing import libraries (`.lib` files). The build script emits a `pyo3_dll`
cfg with the target DLL name, and the `extern_libpython!` macro expands to the appropriate
`#[link(name = "...", kind = "raw-dylib")]` attribute. This enables cross compiling Python
extensions for Windows without having to install any Windows Python libraries.
[python/c api]: https://docs.python.org/3/c-api/
[`pyo3-macros`]: https://github.com/PyO3/pyo3/tree/main/pyo3-macros
[`pyo3-macros-backend`]: https://github.com/PyO3/pyo3/tree/main/pyo3-macros-backend
[`pyo3-build-config`]: https://github.com/PyO3/pyo3/tree/main/pyo3-build-config
[`pyo3-ffi`]: https://github.com/PyO3/pyo3/tree/main/pyo3-ffi
[`src/class`]: https://github.com/PyO3/pyo3/tree/main/src/class
[`src/ffi`]: https://github.com/PyO3/pyo3/tree/main/src/ffi
[`src/types`]: https://github.com/PyO3/pyo3/tree/main/src/types
[`src/impl_`]: https://github.com/PyO3/pyo3/blob/main/src/impl_
[`src/instance.rs`]: https://github.com/PyO3/pyo3/tree/main/src/instance.rs
[`src/pycell.rs`]: https://github.com/PyO3/pyo3/tree/main/src/pycell.rs
[`src/pyclass.rs`]: https://github.com/PyO3/pyo3/tree/main/src/pyclass.rs
[`src/pyclass_init.rs`]: https://github.com/PyO3/pyo3/tree/main/src/pyclass_init.rs
[`src/pyclass_slot.rs`]: https://github.com/PyO3/pyo3/tree/main/src/pyclass_slot.rs
[`src/type_object.rs`]: https://github.com/PyO3/pyo3/tree/main/src/type_object.rs
[`src/class/methods.rs`]: https://github.com/PyO3/pyo3/tree/main/src/class/methods.rs
[`src/class/impl_.rs`]: https://github.com/PyO3/pyo3/tree/main/src/class/impl_.rs
[`src/types/any.rs`]: https://github.com/PyO3/pyo3/tree/main/src/types/any.rs
[`src/types/mod.rs`]: https://github.com/PyO3/pyo3/tree/main/src/types/mod.rs
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file. For help with updating to new
PyO3 versions, please see the [migration guide](https://pyo3.rs/latest/migration.html).
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
To see unreleased changes, please see the [CHANGELOG on the main branch guide](https://pyo3.rs/main/changelog.html).
## [0.28.2] - 2026-02-18
### Fixed
- Fix complex enum `__qualname__` not using python name [#5815](https://github.com/PyO3/pyo3/pull/5815)
- Fix FFI definition `PyType_GetTypeDataSize` (was incorrectly named `PyObject_GetTypeDataSize`). [#5819](https://github.com/PyO3/pyo3/pull/5819)
- Fix memory corruption when subclassing native types with `abi3` feature on Python 3.12+ (newly enabled in PyO3 0.28.0). [#5823](https://github.com/PyO3/pyo3/pull/5823)
## [0.28.1] - 2026-02-14
### Fixed
- Fix `*args` / `**kwargs` support in` experimental-async` feature (regressed in 0.28.0). [#5771](https://github.com/PyO3/pyo3/pull/5771)
- Fix `clippy::declare_interior_mutable_const` warning inside `#[pyclass]` generated code on enums. [#5772](https://github.com/PyO3/pyo3/pull/5772)
- Fix `ambiguous_associated_items` compilation error when deriving `FromPyObject` or using `#[pyclass(from_py_object)]` macro on enums with `Error` variant. [#5784](https://github.com/PyO3/pyo3/pull/5784)
- Fix `__qualname__` for complex `#[pyclass]` enum variants to include the enum name. [#5796](https://github.com/PyO3/pyo3/pull/5796)
- Fix missing `std::sync::atomic::Ordering` import for targets without atomic64. [#5808](https://github.com/PyO3/pyo3/pull/5808)
## [0.28.0] - 2026-02-01
### Packaging
- Bump MSRV to Rust 1.83. [#5531](https://github.com/PyO3/pyo3/pull/5531)
- Bump minimum supported `quote` version to 1.0.37. [#5531](https://github.com/PyO3/pyo3/pull/5531)
- Bump supported GraalPy version to 25.0. [#5542](https://github.com/PyO3/pyo3/pull/5542)
- Drop `memoffset` dependency. [#5545](https://github.com/PyO3/pyo3/pull/5545)
- Support for free-threaded Python is now opt-out rather than opt-in. [#5564](https://github.com/PyO3/pyo3/pull/5564)
- Bump `target-lexicon` dependency to 0.13.3. [#5571](https://github.com/PyO3/pyo3/pull/5571)
- Drop `indoc` and `unindent` dependencies. [#5608](https://github.com/PyO3/pyo3/pull/5608)
### Added
- Add `__init__` support in `#[pymethods]`. [#4951](https://github.com/PyO3/pyo3/pull/4951)
- Expose `PySuper` on PyPy, GraalPy and ABI3 [#4951](https://github.com/PyO3/pyo3/pull/4951)
- Add `PyString::from_fmt` and `py_format!` macro. [#5199](https://github.com/PyO3/pyo3/pull/5199)
- Add `#[pyclass(new = "from_fields")]` option. [#5421](https://github.com/PyO3/pyo3/pull/5421)
- Add `pyo3::buffer::PyUntypedBuffer`, a type-erased form of `PyBuffer`. [#5458](https://github.com/PyO3/pyo3/pull/5458)
- Add `PyBytes::new_with_writer` [#5517](https://github.com/PyO3/pyo3/pull/5517)
- Add `PyClass::NAME`. [#5579](https://github.com/PyO3/pyo3/pull/5579)
- Add `pyo3_build_config::add_libpython_rpath_link_args`. [#5624](https://github.com/PyO3/pyo3/pull/5624)
- Add `PyBackedStr::clone_ref` and `PyBackedBytes::clone_ref` methods. [#5654](https://github.com/PyO3/pyo3/pull/5654)
- Add `PyCapsule::new_with_pointer` and `PyCapsule::new_with_pointer_and_destructor` for creating capsules with raw pointers. [#5689](https://github.com/PyO3/pyo3/pull/5689)
- Add `#[deleter]` attribute to implement property deleters in `#[methods]`. [#5699](https://github.com/PyO3/pyo3/pull/5699)
- Add `IntoPyObject` and `FromPyObject` implementations for `uuid::NonNilUuid`. [#5707](https://github.com/PyO3/pyo3/pull/5707)
- Add `PyBackedStr::as_str` and `PyBackedStr::as_py_str` methods. [#5723](https://github.com/PyO3/pyo3/pull/5723)
- Add support for subclassing native types (`PyDict`, exceptions, ...) when building for abi3 on Python 3.12+. [#5733](https://github.com/PyO3/pyo3/pull/5733)
- Add support for subclassing `PyList` when building for Python 3.12+. [#5734](https://github.com/PyO3/pyo3/pull/5734)
- FFI definitions:
- Add FFI definitions `PyEval_GetFrameBuiltins`, `PyEval_GetFrameGlobals` and `PyEval_GetFrameLocals` on Python 3.13 and up. [#5590](https://github.com/PyO3/pyo3/pull/5590)
- Add FFI definitions `PyObject_New`, `PyObject_NewVar`, `PyObject_GC_Resize`, `PyObject_GC_New`, and `PyObject_GC_NewVar`. [#5591](https://github.com/PyO3/pyo3/pull/5591)
- Added FFI definitions and an unsafe Rust API wrapping `Py_BEGIN_CRITICAL_SECTION_MUTEX` and `Py_BEGIN_CRITICAL_SECTION_MUTEX2`. [#5642](https://github.com/PyO3/pyo3/pull/5642)
- Add FFI definition `PyDict_GetItemStringRef` on Python 3.13 and up. [#5659](https://github.com/PyO3/pyo3/pull/5659)
- Add FFI definition `PyIter_NextItem` on Python 3.14 and up, and `compat::PyIter_NextItem` for older versions. [#5661](https://github.com/PyO3/pyo3/pull/5661)
- Add FFI definitions `PyThreadState_GetInterpreter` and `PyThreadState_GetID` on Python 3.9+, `PyThreadState_EnterTracing` and `PyThreadState_LeaveTracing` on Python 3.11+, `PyThreadState_GetUnchecked` on Python 3.13+, and `compat::PyThreadState_GetUnchecked`. [#5711](https://github.com/PyO3/pyo3/pull/5711)
- Add FFI definitions `PyImport_ImportModuleAttr` and `PyImport_ImportModuleAttrString` on Python 3.14+. [#5737](https://github.com/PyO3/pyo3/pull/5737)
- Add FFI definitions for the `PyABIInfo` and `PyModExport` APIs available in Python 3.15. [#5746](https://github.com/PyO3/pyo3/pull/5746)
- `experimental-inspect`:
- Emit base classes. [#5331](https://github.com/PyO3/pyo3/pull/5331)
- Emit `@typing.final` on final classes. [#5552](https://github.com/PyO3/pyo3/pull/5552)
- Generate nested classes for complex enums. [#5708](https://github.com/PyO3/pyo3/pull/5708)
- Emit `async` keyword for async functions. [#5731](https://github.com/PyO3/pyo3/pull/5731)
### Changed
- Call `sys.unraisablehook` instead of `PyErr_Print` if panicking on null FFI pointer in `Bound`, `Borrowed` and `Py` constructors. [#5496](https://github.com/PyO3/pyo3/pull/5496)
- Use PEP-489 multi-phase initialization for `#[pymodule]`. [#5525](https://github.com/PyO3/pyo3/pull/5525)
- Deprecate implicit by-value implementation of `FromPyObject` for `#[pyclass]`. [#5550](https://github.com/PyO3/pyo3/pull/5550)
- Deprecate `PyTypeInfo::NAME` and `PyTypeInfo::MODULE`. [#5579](https://github.com/PyO3/pyo3/pull/5579)
- Deprecate `Py::from_{owned,borrowed}[or_{err,opt}]` constructors from raw pointer. [#5585](https://github.com/PyO3/pyo3/pull/5585)
- Deprecate FFI definitions `PyEval_AcquireLock` and `PyEval_ReleaseLock`. [#5590](https://github.com/PyO3/pyo3/pull/5590)
- Relax `'py: 'a` bound in `Py::extract`. [#5594](https://github.com/PyO3/pyo3/pull/5594)
- Add a `T: PyTypeCheck` bound to the `IntoPyObject` implementations on `Bound`, `Borrowed` and `Py`. [#5640](https://github.com/PyO3/pyo3/pull/5640)
- The `with_critical_section` and `with_critical_section2` functions are moved to `pyo3::sync::critical_section`. [#5642](https://github.com/PyO3/pyo3/pull/5642)
- Use `PyIter_NextItem` in `PyIterator::next` implementation. [#5661](https://github.com/PyO3/pyo3/pull/5661)
- `IntoPyObject` for simple enums now uses a singleton value, allowing identity (python `is`) comparisons. [#5665](https://github.com/PyO3/pyo3/pull/5665)
- Allow any `Sequence[int]` in `FromPyObject` on `Cow<[u8]>` and change the error type to `PyErr`. [#5667](https://github.com/PyO3/pyo3/pull/5667)
- `async` pymethods now borrow `self` only for the duration of awaiting the future, not the entire method call. [#5684](https://github.com/PyO3/pyo3/pull/5684)
- Change `CastError` formatted message to directly describe the "is not an instance of" failure condition. [#5693](https://github.com/PyO3/pyo3/pull/5693)
- Add `#[inline]` hints to many methods on `PyBackedStr`. [#5723](https://github.com/PyO3/pyo3/pull/5723)
- Remove redundant internal counters from `BoundSetIterator` and `BoundFrozenSetIterator`. [#5725](https://github.com/PyO3/pyo3/pull/5725)
- Implement `PyIterator::size_hint` on abi3 builds (previously was only on unlimited API builds). [#5727](https://github.com/PyO3/pyo3/pull/5727)
- Deprecate FFI definition `PyImport_ImportModuleNoBlock` (deprecated in Python 3.13). [#5737](https://github.com/PyO3/pyo3/pull/5737)
- `#[new]` can now return arbitrary Python objects. [#5739](https://github.com/PyO3/pyo3/pull/5739)
- `experimental-inspect`:
- Introduce `TypeHint` and make use of it to encode type hint annotations. [#5438](https://github.com/PyO3/pyo3/pull/5438)
- Rename `PyType{Info,Check}::TYPE_INFO` into `PyType{Info,Check}::TYPE_HINT`. [#5438](https://github.com/PyO3/pyo3/pull/5438) [#5619](https://github.com/PyO3/pyo3/pull/5619) [#5641](https://github.com/PyO3/pyo3/pull/5641)
- Fill annotations on function arguments and return values for all types supported natively by PyO3. [#5634](https://github.com/PyO3/pyo3/pull/5634) [#5637](https://github.com/PyO3/pyo3/pull/5637) [#5639](https://github.com/PyO3/pyo3/pull/5639)
- Use `_typeshed.Incomplete` instead of `typing.Any` as default type hint, to make it easier to spot incomplete trait implementations. [#5744](https://github.com/PyO3/pyo3/pull/5744)
- Use general Python expression syntax for type hints. [#5671](https://github.com/PyO3/pyo3/pull/5671)
### Removed
- Remove all functionality deprecated in PyO3 0.25 and 0.26. [#5740](https://github.com/PyO3/pyo3/pull/5740)
- FFI definitions:
- Remove FFI definition `PyEval_GetCallStats` (removed from CPython in Python 3.7). [#5590](https://github.com/PyO3/pyo3/pull/5590)
- Remove FFI definitions `PyEval_AcquireLock` and `PyEval_ReleaseLock` on Python 3.13 and up. [#5590](https://github.com/PyO3/pyo3/pull/5590)
- Remove private FFI definitions `_PyObject_New`, `_PyObject_NewVar`, `_PyObject_GC_Resize`, `_PyObject_GC_New`, and `_PyObject_GC_NewVar`. [#5591](https://github.com/PyO3/pyo3/pull/5591)
- Remove private FFI definitions `_PyDict_SetItem_KnownHash`, `_PyDict_Next`, `_PyDict_NewPresized`, `_PyDict_Contains_KnownHash`, and `_PyDict_Contains`. [#5659](https://github.com/PyO3/pyo3/pull/5659)
- Remove private FFI definitions `_PyFrameEvalFunction`, `_PyInterpreterState_GetEvalFrameFunc` and `_PyInterpreterState_SetEvalFrameFunc`. [#5711](https://github.com/PyO3/pyo3/pull/5711)
- Remove private FFI definitions `_PyImport_IsInitialized`, `_PyImport_SetModule`, `_PyImport_SetModuleString`, `_PyImport_AcquireLock`, `_PyImport_ReleaseLock`, `_PyImport_FindBuiltin`, `_PyImport_FindExtensionObject`, `_PyImport_FixupBuiltin`, and `_PyImport_FixupExtensionObject`. [#5737](https://github.com/PyO3/pyo3/pull/5737)
### Fixed
- Fix `PyModuleMethods::add_submodule()` to use the last segment of the submodule name as the attribute name on the parent module instead of using the full name. [#5375](https://github.com/PyO3/pyo3/pull/5375)
- Link with libpython for Cygwin extension modules. [#5571](https://github.com/PyO3/pyo3/pull/5571)
- Link against the limited API DLL for Cygwin when abi3 is used. [#5574](https://github.com/PyO3/pyo3/pull/5574)
- Handle errors in `PyIterator` when calling `size_hint` [#5604](https://github.com/PyO3/pyo3/pull/5604)
- Link with libpython for iOS extension modules. [#5605](https://github.com/PyO3/pyo3/pull/5605)
- Correct `IntoPyObject` output type of `PyBackedStr` to be `PyString`, not `PyAny`. [#5655](https://github.com/PyO3/pyo3/pull/5655)
- Fix `async` functions to return `None` rather than empty tuple `()`. [#5685](https://github.com/PyO3/pyo3/pull/5685)
- Fix compile error when using references to `#[pyclass]` types (e.g. `&MyClass`) as arguments to async `#[pyfunction]`s. [#5725](https://github.com/PyO3/pyo3/pull/5725)
- FFI definitions:
- Fix FFI definition `PyMemberDescrObject.d_member` to use `PyMemberDef` for Python 3.11+ (was incorrectly `PyGetSetDef`). [#5647](https://github.com/PyO3/pyo3/pull/5647)
- Mark FFI definition `PyThreadState_GetFrame` available with abi3 in 3.10+. [#5711](https://github.com/PyO3/pyo3/pull/5711)
- Fix FFI definition `PyImport_GetModule` on PyPy. [#5737](https://github.com/PyO3/pyo3/pull/5737)
- `experimental-inspect`:
- fix `__new__` return type to be the built object type and not `None`. [#5555](https://github.com/PyO3/pyo3/pull/5555)
- fix imports of decorators. [#5618](https://github.com/PyO3/pyo3/pull/5618)
- fix the return type annotation of `PyResult<()>` (must be `None` and not `tuple`) [#5674](https://github.com/PyO3/pyo3/pull/5674)
## [0.27.2] - 2025-11-30
### Changed
- Disable subclassing `PyDict` on GraalPy (unsupported for now, may crash at runtime). [#5653](https://github.com/PyO3/pyo3/pull/5653)
### Fixed
- Fix crash when compiling on Rust 1.92+ with both debug assertions and optimizations enabled. [#5638](https://github.com/PyO3/pyo3/pull/5638)
- Fix FFI definition of `PyDictObject` on PyPy. [#5653](https://github.com/PyO3/pyo3/pull/5653)
## [0.27.1] - 2025-10-21
### Fixed
- Fix `clippy:declare_interior_mutable_const` warning from `#[pyfunction]`. [#5538](https://github.com/PyO3/pyo3/pull/5538)
- Expose `pyo3::types::PySendResult` in public API. [#5539](https://github.com/PyO3/pyo3/pull/5539)
## [0.27.0] - 2025-10-19
### Packaging
- Extend range of supported versions of `hashbrown` optional dependency to include version 0.16. [#5428](https://github.com/PyO3/pyo3/pull/5428)
- Bump optional `num-bigint` dependency minimum version to 0.4.4. [#5471](https://github.com/PyO3/pyo3/pull/5471)
- Test against Python 3.14 final release. [#5499](https://github.com/PyO3/pyo3/pull/5499)
- Drop support for PyPy 3.9 and 3.10. [#5516](https://github.com/PyO3/pyo3/pull/5516)
- Provide a better error message when building an outdated PyO3 for a too-new Python version. [#5519](https://github.com/PyO3/pyo3/pull/5519)
### Added
- Add `FromPyObjectOwned` as convenient trait bound for `FromPyObject` when the data is not borrowed from Python. [#4390](https://github.com/PyO3/pyo3/pull/4390)
- Add `Borrowed::extract`, same as `PyAnyMethods::extract`, but does not restrict the lifetime by deref. [#4390](https://github.com/PyO3/pyo3/pull/4390)
- `experimental-inspect`: basic support for `#[derive(IntoPyObject)]` (no struct fields support yet). [#5365](https://github.com/PyO3/pyo3/pull/5365)
- `experimental-inspect`: support `#[pyo3(get, set)]` and `#[pyclass(get_all, set_all)]`. [#5370](https://github.com/PyO3/pyo3/pull/5370)
- Add `PyTypeCheck::classinfo_object` that returns an object that can be used as parameter in `isinstance` or `issubclass`. [#5387](https://github.com/PyO3/pyo3/pull/5387)
- Implement `PyTypeInfo` on `datetime.*` types even when the limited API is enabled. [#5388](https://github.com/PyO3/pyo3/pull/5388)
- Implement `PyTypeInfo` on `PyIterator`, `PyMapping` and `PySequence`. [#5402](https://github.com/PyO3/pyo3/pull/5402)
- Implement `PyTypeInfo` on `PyCode` when using the stable ABI. [#5403](https://github.com/PyO3/pyo3/pull/5403)
- Implement `PyTypeInfo` on `PyWeakrefReference` when using the stable ABI. [#5404](https://github.com/PyO3/pyo3/pull/5404)
- Add `pyo3::sync::RwLockExt` trait, analogous to `pyo3::sync::MutexExt` for readwrite locks. [#5435](https://github.com/PyO3/pyo3/pull/5435)
- Add `PyString::from_bytes`. [#5437](https://github.com/PyO3/pyo3/pull/5437)
- Implement `AsRef<[u8]>` for `PyBytes`. [#5445](https://github.com/PyO3/pyo3/pull/5445)
- Add `CastError` and `CastIntoError`. [#5468](https://github.com/PyO3/pyo3/pull/5468)
- Add `PyCapsuleMethods::pointer_checked` and `PyCapsuleMethods::is_valid_checked`. [#5474](https://github.com/PyO3/pyo3/pull/5474)
- Add `Borrowed::cast`, `Borrowed::cast_exact` and `Borrowed::cast_unchecked`. [#5475](https://github.com/PyO3/pyo3/pull/5475)
- Add conversions for `jiff::civil::ISOWeekDate`. [#5478](https://github.com/PyO3/pyo3/pull/5478)
- Add conversions for `&Cstr`, `Cstring` and `Cow`. [#5482](https://github.com/PyO3/pyo3/pull/5482)
- add `#[pyclass(skip_from_py_object)]` option, to opt-out of the `FromPyObject: PyClass + Clone` blanket impl. [#5488](https://github.com/PyO3/pyo3/pull/5488)
- Add `PyErr::add_note`. [#5489](https://github.com/PyO3/pyo3/pull/5489)
- Add `FromPyObject` impl for `Cow` & `Cow`. [#5497](https://github.com/PyO3/pyo3/pull/5497)
- Add `#[pyclass(from_py_object)]` pyclass option, to opt-in to the extraction of pyclasses by value (requires `Clone`). [#5506](https://github.com/PyO3/pyo3/pull/5506)
### Changed
- Rework `FromPyObject` trait for flexibility and performance: [#4390](https://github.com/PyO3/pyo3/pull/4390)
- Add a second lifetime to `FromPyObject`, to allow borrowing data from Python objects (e.g. `&str` from Python `str`).
- Replace `extract_bound` with `extract`, which takes `Borrowed<'a, 'py, PyAny>`.
- Optimize `FromPyObject` implementations for `Vec` and `[u8; N]` from `bytes` and `bytearray`. [#5244](https://github.com/PyO3/pyo3/pull/5244)
- Deprecate `#[pyfn]` attribute. [#5384](https://github.com/PyO3/pyo3/pull/5384)
- Fetch type name dynamically on cast errors instead of using `PyTypeCheck::NAME`. [#5387](https://github.com/PyO3/pyo3/pull/5387)
- Deprecate `PyTypeCheck::NAME` in favour of `PyTypeCheck::classinfo_object` which provides the type information at runtime. [#5387](https://github.com/PyO3/pyo3/pull/5387)
- `PyClassGuard(Mut)` and `PyRef(Mut)` extraction now returns an opaque Rust error [#5413](https://github.com/PyO3/pyo3/pull/5413)
- Fetch type name dynamically when exporting types implementing `PyTypeInfo` with `#[pymodule_use]`. [#5414](https://github.com/PyO3/pyo3/pull/5414)
- Improve `Debug` representation of `PyBuffer`. [#5442](https://github.com/PyO3/pyo3/pull/5442)
- `experimental-inspect`: change the way introspection data is emitted in the binaries to avoid a pointer indirection and simplify parsing. [#5450](https://github.com/PyO3/pyo3/pull/5450)
- Optimize `Py::drop` for the case when attached to the Python interpreter. [#5454](https://github.com/PyO3/pyo3/pull/5454)
- Replace `DowncastError` and `DowncastIntoError` with `CastError` and `CastIntoError`. [#5468](https://github.com/PyO3/pyo3/pull/5468)
- Enable fast-path for 128-bit integer conversions on `GraalPy`. [#5471](https://github.com/PyO3/pyo3/pull/5471)
- Deprecate `PyAnyMethods::downcast` functions in favour of `Bound::cast` functions. [#5472](https://github.com/PyO3/pyo3/pull/5472)
- Make `PyTypeCheck` an `unsafe trait`. [#5473](https://github.com/PyO3/pyo3/pull/5473)
- Deprecate unchecked `PyCapsuleMethods`: `pointer()`, `reference()`, and `is_valid()`. [#5474](https://github.com/PyO3/pyo3/pull/5474)
- Reduce lifetime of return value in `PyCapsuleMethods::reference`. [#5474](https://github.com/PyO3/pyo3/pull/5474)
- `PyCapsuleMethods::name` now returns `CapsuleName` wrapper instead of `&CStr`. [#5474](https://github.com/PyO3/pyo3/pull/5474)
- Deprecate `import_exception_bound` in favour of `import_exception`. [#5480](https://github.com/PyO3/pyo3/pull/5480)
- `PyList::get_item_unchecked`, `PyTuple::get_item_unchecked`, and `PyTuple::get_borrowed_item_unchecked` no longer check for null values at the provided index. [#5494](https://github.com/PyO3/pyo3/pull/5494)
- Allow converting naive datetime into chrono `DateTime`. [#5507](https://github.com/PyO3/pyo3/pull/5507)
### Removed
- Removed `FromPyObjectBound` trait. [#4390](https://github.com/PyO3/pyo3/pull/4390)
### Fixed
- Fix compilation failure on `wasm32-wasip2`. [#5368](https://github.com/PyO3/pyo3/pull/5368)
- Fix `OsStr` conversion for non-utf8 strings on Windows. [#5444](https://github.com/PyO3/pyo3/pull/5444)
- Fix issue with `cargo vendor` caused by gitignored build artifact `emscripten/pybuilddir.txt`. [#5456](https://github.com/PyO3/pyo3/pull/5456)
- Stop leaking `PyMethodDef` instances inside `#[pyfunction]` macro generated code. [#5459](https://github.com/PyO3/pyo3/pull/5459)
- Don't export definition of FFI struct `PyObjectObFlagsAndRefcnt` on 32-bit Python 3.14 (doesn't exist). [#5499](https://github.com/PyO3/pyo3/pull/5499)
- Fix failure to build for `abi3` interpreters on Windows using maturin's built-in sysconfig in combination with the `generate-import-lib` feature. [#5503](https://github.com/PyO3/pyo3/pull/5503)
- Fix FFI definitions `PyModule_ExecDef` and `PyModule_FromDefAndSpec2` on PyPy. [#5529](https://github.com/PyO3/pyo3/pull/5529)
## [0.26.0] - 2025-08-29
### Packaging
- Bump hashbrown dependency to 0.15. [#5152](https://github.com/PyO3/pyo3/pull/5152)
- Update MSRV to 1.74. [#5171](https://github.com/PyO3/pyo3/pull/5171)
- Set the same maximum supported version for alternative interpreters as for CPython. [#5192](https://github.com/PyO3/pyo3/pull/5192)
- Add optional `bytes` dependency to add conversions for `bytes::Bytes`. [#5252](https://github.com/PyO3/pyo3/pull/5252)
- Publish new crate `pyo3-introspection` to pair with the `experimental-inspect` feature. [#5300](https://github.com/PyO3/pyo3/pull/5300)
- The `PYO3_BUILD_EXTENSION_MODULE` now causes the same effect as the `extension-module` feature. Eventually we expect maturin and setuptools-rust to set this environment variable automatically. Users with their own build systems will need to do the same. [#5343](https://github.com/PyO3/pyo3/pull/5343)
### Added
- Add `#[pyo3(warn(message = "...", category = ...))]` attribute for automatic warnings generation for `#[pyfunction]` and `#[pymethods]`. [#4364](https://github.com/PyO3/pyo3/pull/4364)
- Add `PyMutex`, available on Python 3.13 and newer. [#4523](https://github.com/PyO3/pyo3/pull/4523)
- Add FFI definition `PyMutex_IsLocked`, available on Python 3.14 and newer. [#4523](https://github.com/PyO3/pyo3/pull/4523)
- Add `PyString::from_encoded_object`. [#5017](https://github.com/PyO3/pyo3/pull/5017)
- `experimental-inspect`: add basic input type annotations. [#5089](https://github.com/PyO3/pyo3/pull/5089)
- Add FFI function definitions for `PyFrameObject` from CPython 3.13. [#5154](https://github.com/PyO3/pyo3/pull/5154)
- `experimental-inspect`: tag modules created using `#[pymodule]` or `#[pymodule_init]` functions as incomplete. [#5207](https://github.com/PyO3/pyo3/pull/5207)
- `experimental-inspect`: add basic return type support. [#5208](https://github.com/PyO3/pyo3/pull/5208)
- Add `PyCode::compile` and `PyCodeMethods::run` to create and execute code objects. [#5217](https://github.com/PyO3/pyo3/pull/5217)
- Add `PyOnceLock` type for thread-safe single-initialization. [#5223](https://github.com/PyO3/pyo3/pull/5223)
- Add `PyClassGuard(Mut)` pyclass holders. In the future they will replace `PyRef(Mut)`. [#5233](https://github.com/PyO3/pyo3/pull/5233)
- `experimental-inspect`: allow annotations in `#[pyo3(signature)]` signature attribute. [#5241](https://github.com/PyO3/pyo3/pull/5241)
- Implement `MutexExt` for parking_lot's/lock_api `ReentrantMutex`. [#5258](https://github.com/PyO3/pyo3/pull/5258)
- `experimental-inspect`: support class associated constants. [#5272](https://github.com/PyO3/pyo3/pull/5272)
- Add `Bound::cast` family of functions superseding the `PyAnyMethods::downcast` family. [#5289](https://github.com/PyO3/pyo3/pull/5289)
- Add FFI definitions `Py_Version` and `Py_IsFinalizing`. [#5317](https://github.com/PyO3/pyo3/pull/5317)
- `experimental-inspect`: add output type annotation for `#[pyclass]`. [#5320](https://github.com/PyO3/pyo3/pull/5320)
- `experimental-inspect`: support `#[pyclass(eq, eq_int, ord, hash, str)]`. [#5338](https://github.com/PyO3/pyo3/pull/5338)
- `experimental-inspect`: add basic support for `#[derive(FromPyObject)]` (no struct fields support yet). [#5339](https://github.com/PyO3/pyo3/pull/5339)
- Add `Python::try_attach`. [#5342](https://github.com/PyO3/pyo3/pull/5342)
### Changed
- Use `Py_TPFLAGS_DISALLOW_INSTANTIATION` instead of a `__new__` which always fails for a `#[pyclass]` without a `#[new]` on Python 3.10 and up. [#4568](https://github.com/PyO3/pyo3/pull/4568)
- `PyModule::from_code` now defaults `file_name` to `` if empty. [#4777](https://github.com/PyO3/pyo3/pull/4777)
- Deprecate `PyString::from_object` in favour of `PyString::from_encoded_object`. [#5017](https://github.com/PyO3/pyo3/pull/5017)
- When building with `abi3` for a Python version newer than pyo3 supports, automatically fall back to an abi3 build for the latest supported version. [#5144](https://github.com/PyO3/pyo3/pull/5144)
- Change `is_instance_of` trait bound from `PyTypeInfo` to `PyTypeCheck`. [#5146](https://github.com/PyO3/pyo3/pull/5146)
- Many PyO3 proc macros now report multiple errors instead of only the first one. [#5159](https://github.com/PyO3/pyo3/pull/5159)
- Change `MutexExt` return type to be an associated type. [#5201](https://github.com/PyO3/pyo3/pull/5201)
- Use `PyCallArgs` for `Py::call` and friends so they're equivalent to their `Bound` counterpart. [#5206](https://github.com/PyO3/pyo3/pull/5206)
- Rename `Python::with_gil` to `Python::attach`. [#5209](https://github.com/PyO3/pyo3/pull/5209)
- Rename `Python::allow_threads` to `Python::detach` [#5221](https://github.com/PyO3/pyo3/pull/5221)
- Deprecate `GILOnceCell` type in favour of `PyOnceLock`. [#5223](https://github.com/PyO3/pyo3/pull/5223)
- Rename `pyo3::prepare_freethreaded_python` to `Python::initialize`. [#5247](https://github.com/PyO3/pyo3/pull/5247)
- Convert `PyMemoryError` into/from `io::ErrorKind::OutOfMemory`. [#5256](https://github.com/PyO3/pyo3/pull/5256)
- Deprecate `GILProtected`. [#5285](https://github.com/PyO3/pyo3/pull/5285)
- Move `#[pyclass]` docstring formatting from import time to compile time. [#5286](https://github.com/PyO3/pyo3/pull/5286)
- `Python::attach` will now panic if the Python interpreter is in the process of shutting down. [#5317](https://github.com/PyO3/pyo3/pull/5317)
- Add fast-path to `PyTypeInfo::type_object` for `#[pyclass]` types. [#5324](https://github.com/PyO3/pyo3/pull/5324)
- Deprecate `PyObject` type alias for `Py`. [#5325](https://github.com/PyO3/pyo3/pull/5325)
- Rename `Python::with_gil_unchecked` to `Python::attach_unchecked`. [#5340](https://github.com/PyO3/pyo3/pull/5340)
- Rename `Python::assume_gil_acquired` to `Python::assume_attached`. [#5354](https://github.com/PyO3/pyo3/pull/5354)
### Removed
- Remove FFI definition of internals of `PyFrameObject`. [#5154](https://github.com/PyO3/pyo3/pull/5154)
- Remove `Eq` and `PartialEq` implementations on `PyGetSetDef` FFI definition. [#5196](https://github.com/PyO3/pyo3/pull/5196)
- Remove private FFI definitions `_Py_IsCoreInitialized` and `_Py_InitializeMain`. [#5317](https://github.com/PyO3/pyo3/pull/5317)
### Fixed
- Use critical section in `PyByteArray::to_vec` on freethreaded build to replicate GIL-enabled "soundness". [#4742](https://github.com/PyO3/pyo3/pull/4742)
- Fix precision loss when converting `bigdecimal` into Python. [#5198](https://github.com/PyO3/pyo3/pull/5198)
- Don't treat win7 target as a cross-compilation. [#5210](https://github.com/PyO3/pyo3/pull/5210)
- WASM targets no longer require exception handling support for Python < 3.14. [#5239](https://github.com/PyO3/pyo3/pull/5239)
- Fix segfault when dropping `PyBuffer` after the Python interpreter has been finalized. [#5242](https://github.com/PyO3/pyo3/pull/5242)
- `experimental-inspect`: better automated imports generation. [#5251](https://github.com/PyO3/pyo3/pull/5251)
- `experimental-inspect`: fix introspection of `__richcmp__`, `__concat__`, `__repeat__`, `__inplace_concat__` and `__inplace_repeat__`. [#5273](https://github.com/PyO3/pyo3/pull/5273)
- fixed a leaked borrow, when converting a mutable sub class into a frozen base class using `PyRef::into_super` [#5281](https://github.com/PyO3/pyo3/pull/5281)
- Fix FFI definition `Py_Exit` (never returns, was `()` return value, now `!`). [#5317](https://github.com/PyO3/pyo3/pull/5317)
- `experimental-inspect`: fix handling of module members gated behind `#[cfg(...)]` attributes. [#5318](https://github.com/PyO3/pyo3/pull/5318)
## [0.25.1] - 2025-06-12
### Packaging
- Add support for Windows on ARM64. [#5145](https://github.com/PyO3/pyo3/pull/5145)
- Add `chrono-local` feature for optional conversions for chrono's `Local` timezone & `DateTime` instances. [#5174](https://github.com/PyO3/pyo3/pull/5174)
### Added
- Add FFI definition `PyBytes_AS_STRING`. [#5121](https://github.com/PyO3/pyo3/pull/5121)
- Add support for module associated consts introspection. [#5150](https://github.com/PyO3/pyo3/pull/5150)
### Changed
- Enable "vectorcall" FFI definitions on GraalPy. [#5121](https://github.com/PyO3/pyo3/pull/5121)
- Use `Py_Is` function on GraalPy [#5121](https://github.com/PyO3/pyo3/pull/5121)
### Fixed
- Report a better compile error for `async` declarations when not using `experimental-async` feature. [#5156](https://github.com/PyO3/pyo3/pull/5156)
- Fix implementation of `FromPyObject` for `uuid::Uuid` on big-endian architectures. [#5161](https://github.com/PyO3/pyo3/pull/5161)
- Fix segmentation faults on 32-bit x86 with Python 3.14. [#5180](https://github.com/PyO3/pyo3/pull/5180)
## [0.25.0] - 2025-05-14
### Packaging
- Support Python 3.14.0b1. [#4811](https://github.com/PyO3/pyo3/pull/4811)
- Bump supported GraalPy version to 24.2. [#5116](https://github.com/PyO3/pyo3/pull/5116)
- Add optional `bigdecimal` dependency to add conversions for `bigdecimal::BigDecimal`. [#5011](https://github.com/PyO3/pyo3/pull/5011)
- Add optional `time` dependency to add conversions for `time` types. [#5057](https://github.com/PyO3/pyo3/pull/5057)
- Remove `cfg-if` dependency. [#5110](https://github.com/PyO3/pyo3/pull/5110)
- Add optional `ordered_float` dependency to add conversions for `ordered_float::NotNan` and `ordered_float::OrderedFloat`. [#5114](https://github.com/PyO3/pyo3/pull/5114)
### Added
- Add initial type stub generation to the `experimental-inspect` feature. [#3977](https://github.com/PyO3/pyo3/pull/3977)
- Add `#[pyclass(generic)]` option to support runtime generic typing. [#4926](https://github.com/PyO3/pyo3/pull/4926)
- Implement `OnceExt` & `MutexExt` for `parking_lot` & `lock_api`. Use the new extension traits by enabling the `arc_lock`, `lock_api`, or `parking_lot` cargo features. [#5044](https://github.com/PyO3/pyo3/pull/5044)
- Implement `From`/`Into` for `Borrowed` -> `Py`. [#5054](https://github.com/PyO3/pyo3/pull/5054)
- Add `PyTzInfo` constructors. [#5055](https://github.com/PyO3/pyo3/pull/5055)
- Add FFI definition `PY_INVALID_STACK_EFFECT`. [#5064](https://github.com/PyO3/pyo3/pull/5064)
- Implement `AsRef>` for `Py`, `Bound` and `Borrowed`. [#5071](https://github.com/PyO3/pyo3/pull/5071)
- Add FFI definition `PyModule_Add` and `compat::PyModule_Add`. [#5085](https://github.com/PyO3/pyo3/pull/5085)
- Add FFI definitions `Py_HashBuffer`, `Py_HashPointer`, and `PyObject_GenericHash`. [#5086](https://github.com/PyO3/pyo3/pull/5086)
- Support `#[pymodule_export]` on `const` items in declarative modules. [#5096](https://github.com/PyO3/pyo3/pull/5096)
- Add `#[pyclass(immutable_type)]` option (on Python 3.14+ with `abi3`, or 3.10+ otherwise) for immutable type objects. [#5101](https://github.com/PyO3/pyo3/pull/5101)
- Support `#[pyo3(rename_all)]` support on `#[derive(IntoPyObject)]`. [#5112](https://github.com/PyO3/pyo3/pull/5112)
- Add `PyRange` wrapper. [#5117](https://github.com/PyO3/pyo3/pull/5117)
### Changed
- Enable use of `datetime` types with `abi3` feature enabled. [#4970](https://github.com/PyO3/pyo3/pull/4970)
- Deprecate `timezone_utc` in favor of `PyTzInfo::utc`. [#5055](https://github.com/PyO3/pyo3/pull/5055)
- Reduce visibility of some CPython implementation details: [#5064](https://github.com/PyO3/pyo3/pull/5064)
- The FFI definition `PyCodeObject` is now an opaque struct on all Python versions.
- The FFI definition `PyFutureFeatures` is now only defined up until Python 3.10 (it was present in CPython headers but unused in 3.11 and 3.12).
- Change `PyAnyMethods::is` to take `other: &Bound`. [#5071](https://github.com/PyO3/pyo3/pull/5071)
- Change `Py::is` to take `other: &Py`. [#5071](https://github.com/PyO3/pyo3/pull/5071)
- Change `PyVisit::call` to take `T: Into>>`. [#5071](https://github.com/PyO3/pyo3/pull/5071)
- Expose `PyDateTime_DATE_GET_TZINFO` and `PyDateTime_TIME_GET_TZINFO` on PyPy 3.10 and later. [#5079](https://github.com/PyO3/pyo3/pull/5079)
- Add `#[track_caller]` to `with_gil` and `with_gil_unchecked`. [#5109](https://github.com/PyO3/pyo3/pull/5109)
- Use `std::thread::park()` instead of `libc::pause()` or `sleep(9999999)`. [#5115](https://github.com/PyO3/pyo3/pull/5115)
### Removed
- Remove all functionality deprecated in PyO3 0.23. [#4982](https://github.com/PyO3/pyo3/pull/4982)
- Remove deprecated `IntoPy` and `ToPyObject` traits. [#5010](https://github.com/PyO3/pyo3/pull/5010)
- Remove private types from `pyo3-ffi` (i.e. starting with `_Py`) which are not referenced by public APIs: `_PyLocalMonitors`, `_Py_GlobalMonitors`, `_PyCoCached`, `_PyCoLineInstrumentationData`, `_PyCoMonitoringData`, `_PyCompilerSrcLocation`, `_PyErr_StackItem`. [#5064](https://github.com/PyO3/pyo3/pull/5064)
- Remove FFI definition `PyCode_GetNumFree` (PyO3 cannot support it due to knowledge of the code object). [#5064](https://github.com/PyO3/pyo3/pull/5064)
- Remove `AsPyPointer` trait. [#5071](https://github.com/PyO3/pyo3/pull/5071)
- Remove support for the deprecated string form of `from_py_with`. [#5097](https://github.com/PyO3/pyo3/pull/5097)
- Remove FFI definitions of private static variables: `_PyMethodWrapper_Type`, `_PyCoroWrapper_Type`, `_PyImport_FrozenBootstrap`, `_PyImport_FrozenStdlib`, `_PyImport_FrozenTest`, `_PyManagedBuffer_Type`, `_PySet_Dummy`, `_PyWeakref_ProxyType`, and `_PyWeakref_CallableProxyType`. [#5105](https://github.com/PyO3/pyo3/pull/5105)
- Remove FFI definitions `PyASCIIObjectState`, `PyUnicode_IS_ASCII`, `PyUnicode_IS_COMPACT`, and `PyUnicode_IS_COMPACT_ASCII` on Python 3.14 and newer. [#5133](https://github.com/PyO3/pyo3/pull/5133)
### Fixed
- Correctly pick up the shared state for conda-based Python installation when reading information from sysconfigdata. [#5037](https://github.com/PyO3/pyo3/pull/5037)
- Fix compile failure with `#[derive(IntoPyObject, FromPyObject)]` when using `#[pyo3()]` options recognised by only one of the two derives. [#5070](https://github.com/PyO3/pyo3/pull/5070)
- Fix various compile errors from missing FFI definitions using certain feature combinations on PyPy and GraalPy. [#5091](https://github.com/PyO3/pyo3/pull/5091)
- Fallback on `backports.zoneinfo` for python <3.9 when converting timezones into python. [#5120](https://github.com/PyO3/pyo3/pull/5120)
## [0.24.2] - 2025-04-21
### Fixed
- Fix `unused_imports` lint of `#[pyfunction]` and `#[pymethods]` expanded in `macro_rules` context. [#5030](https://github.com/PyO3/pyo3/pull/5030)
- Fix size of `PyCodeObject::_co_instrumentation_version` ffi struct member on Python 3.13 for systems where `uintptr_t` is not 64 bits. [#5048](https://github.com/PyO3/pyo3/pull/5048)
- Fix struct-type complex enum variant fields incorrectly exposing raw identifiers as `r#ident` in Python bindings. [#5050](https://github.com/PyO3/pyo3/pull/5050)
## [0.24.1] - 2025-03-31
### Added
- Add `abi3-py313` feature. [#4969](https://github.com/PyO3/pyo3/pull/4969)
- Add `PyAnyMethods::getattr_opt`. [#4978](https://github.com/PyO3/pyo3/pull/4978)
- Add `PyInt::new` constructor for all supported number types (i32, u32, i64, u64, isize, usize). [#4984](https://github.com/PyO3/pyo3/pull/4984)
- Add `pyo3::sync::with_critical_section2`. [#4992](https://github.com/PyO3/pyo3/pull/4992)
- Implement `PyCallArgs` for `Borrowed<'_, 'py, PyTuple>`, `&Bound<'py, PyTuple>`, and `&Py`. [#5013](https://github.com/PyO3/pyo3/pull/5013)
### Fixed
- Fix `is_type_of` for native types not using same specialized check as `is_type_of_bound`. [#4981](https://github.com/PyO3/pyo3/pull/4981)
- Fix `Probe` class naming issue with `#[pymethods]`. [#4988](https://github.com/PyO3/pyo3/pull/4988)
- Fix compile failure with required `#[pyfunction]` arguments taking `Option<&str>` and `Option<&T>` (for `#[pyclass]` types). [#5002](https://github.com/PyO3/pyo3/pull/5002)
- Fix `PyString::from_object` causing of bounds reads with `encoding` and `errors` parameters which are not nul-terminated. [#5008](https://github.com/PyO3/pyo3/pull/5008)
- Fix compile error when additional options follow after `crate` for `#[pyfunction]`. [#5015](https://github.com/PyO3/pyo3/pull/5015)
## [0.24.0] - 2025-03-09
### Packaging
- Add supported CPython/PyPy versions to cargo package metadata. [#4756](https://github.com/PyO3/pyo3/pull/4756)
- Bump `target-lexicon` dependency to 0.13. [#4822](https://github.com/PyO3/pyo3/pull/4822)
- Add optional `jiff` dependency to add conversions for `jiff` datetime types. [#4823](https://github.com/PyO3/pyo3/pull/4823)
- Add optional `uuid` dependency to add conversions for `uuid::Uuid`. [#4864](https://github.com/PyO3/pyo3/pull/4864)
- Bump minimum supported `inventory` version to 0.3.5. [#4954](https://github.com/PyO3/pyo3/pull/4954)
### Added
- Add `PyIterator::send` method to allow sending values into a python generator. [#4746](https://github.com/PyO3/pyo3/pull/4746)
- Add `PyCallArgs` trait for passing arguments into the Python calling protocol. This enabled using a faster calling convention for certain types, improving performance. [#4768](https://github.com/PyO3/pyo3/pull/4768)
- Add `#[pyo3(default = ...']` option for `#[derive(FromPyObject)]` to set a default value for extracted fields of named structs. [#4829](https://github.com/PyO3/pyo3/pull/4829)
- Add `#[pyo3(into_py_with = ...)]` option for `#[derive(IntoPyObject, IntoPyObjectRef)]`. [#4850](https://github.com/PyO3/pyo3/pull/4850)
- Add FFI definitions `PyThreadState_GetFrame` and `PyFrame_GetBack`. [#4866](https://github.com/PyO3/pyo3/pull/4866)
- Optimize `last` for `BoundListIterator`, `BoundTupleIterator` and `BorrowedTupleIterator`. [#4878](https://github.com/PyO3/pyo3/pull/4878)
- Optimize `Iterator::count()` for `PyDict`, `PyList`, `PyTuple` & `PySet`. [#4878](https://github.com/PyO3/pyo3/pull/4878)
- Optimize `nth`, `nth_back`, `advance_by` and `advance_back_by` for `BoundTupleIterator` [#4897](https://github.com/PyO3/pyo3/pull/4897)
- Add support for `types.GenericAlias` as `pyo3::types::PyGenericAlias`. [#4917](https://github.com/PyO3/pyo3/pull/4917)
- Add `MutextExt` trait to help avoid deadlocks with the GIL while locking a `std::sync::Mutex`. [#4934](https://github.com/PyO3/pyo3/pull/4934)
- Add `#[pyo3(rename_all = "...")]` option for `#[derive(FromPyObject)]`. [#4941](https://github.com/PyO3/pyo3/pull/4941)
### Changed
- Optimize `nth`, `nth_back`, `advance_by` and `advance_back_by` for `BoundListIterator`. [#4810](https://github.com/PyO3/pyo3/pull/4810)
- Use `DerefToPyAny` in blanket implementations of `From>` and `From>` for `PyObject`. [#4593](https://github.com/PyO3/pyo3/pull/4593)
- Map `io::ErrorKind::IsADirectory`/`NotADirectory` to the corresponding Python exception on Rust 1.83+. [#4747](https://github.com/PyO3/pyo3/pull/4747)
- `PyAnyMethods::call` and friends now require `PyCallArgs` for their positional arguments. [#4768](https://github.com/PyO3/pyo3/pull/4768)
- Expose FFI definitions for `PyObject_Vectorcall(Method)` on the stable abi on 3.12+. [#4853](https://github.com/PyO3/pyo3/pull/4853)
- `#[pyo3(from_py_with = ...)]` now take a path rather than a string literal [#4860](https://github.com/PyO3/pyo3/pull/4860)
- Format Python traceback in impl Debug for PyErr. [#4900](https://github.com/PyO3/pyo3/pull/4900)
- Convert `PathBuf` & `Path` into Python `pathlib.Path` instead of `PyString`. [#4925](https://github.com/PyO3/pyo3/pull/4925)
- Relax parsing of exotic Python versions. [#4949](https://github.com/PyO3/pyo3/pull/4949)
- PyO3 threads now hang instead of `pthread_exit` trying to acquire the GIL when the interpreter is shutting down. This mimics the [Python 3.14](https://github.com/python/cpython/issues/87135) behavior and avoids undefined behavior and crashes. [#4874](https://github.com/PyO3/pyo3/pull/4874)
### Removed
- Remove implementations of `Deref` for `PyAny` and other "native" types. [#4593](https://github.com/PyO3/pyo3/pull/4593)
- Remove implicit default of trailing optional arguments (see #2935) [#4729](https://github.com/PyO3/pyo3/pull/4729)
- Remove the deprecated implicit eq fallback for simple enums. [#4730](https://github.com/PyO3/pyo3/pull/4730)
### Fixed
- Correct FFI definition of `PyIter_Send` to return a `PySendResult`. [#4746](https://github.com/PyO3/pyo3/pull/4746)
- Fix a thread safety issue in the runtime borrow checker used by mutable pyclass instances on the free-threaded build. [#4948](https://github.com/PyO3/pyo3/pull/4948)
## [0.23.5] - 2025-02-22
### Packaging
- Add support for PyPy3.11 [#4760](https://github.com/PyO3/pyo3/pull/4760)
### Fixed
- Fix thread-unsafe implementation of freelist pyclasses on the free-threaded build. [#4902](https://github.com/PyO3/pyo3/pull/4902)
- Re-enable a workaround for situations where CPython incorrectly does not add `__builtins__` to `__globals__` in code executed by `Python::py_run` (was removed in PyO3 0.23.0). [#4921](https://github.com/PyO3/pyo3/pull/4921)
## [0.23.4] - 2025-01-10
### Added
- Add `PyList::locked_for_each`, which uses a critical section to lock the list on the free-threaded build. [#4789](https://github.com/PyO3/pyo3/pull/4789)
- Add `pyo3_build_config::add_python_framework_link_args` build script API to set rpath when using macOS system Python. [#4833](https://github.com/PyO3/pyo3/pull/4833)
### Changed
- Use `datetime.fold` to distinguish ambiguous datetimes when converting to and from `chrono::DateTime` (rather than erroring). [#4791](https://github.com/PyO3/pyo3/pull/4791)
- Optimize PyList iteration on the free-threaded build. [#4789](https://github.com/PyO3/pyo3/pull/4789)
### Fixed
- Fix unnecessary internal `py.allow_threads` GIL-switch when attempting to access contents of a `PyErr` which originated from Python (could lead to unintended deadlocks). [#4766](https://github.com/PyO3/pyo3/pull/4766)
- Fix thread-unsafe access of dict internals in `BoundDictIterator` on the free-threaded build. [#4788](https://github.com/PyO3/pyo3/pull/4788)
* Fix unnecessary critical sections in `BoundDictIterator` on the free-threaded build. [#4788](https://github.com/PyO3/pyo3/pull/4788)
- Fix time-of-check to time-of-use issues with list iteration on the free-threaded build. [#4789](https://github.com/PyO3/pyo3/pull/4789)
- Fix `chrono::DateTime` to-Python conversion when `Tz` is `chrono_tz::Tz`. [#4790](https://github.com/PyO3/pyo3/pull/4790)
- Fix `#[pyclass]` not being able to be named `Probe`. [#4794](https://github.com/PyO3/pyo3/pull/4794)
- Fix not treating cross-compilation from x64 to aarch64 on Windows as a cross-compile. [#4800](https://github.com/PyO3/pyo3/pull/4800)
- Fix missing struct fields on GraalPy when subclassing builtin classes. [#4802](https://github.com/PyO3/pyo3/pull/4802)
- Fix generating import lib for PyPy when `abi3` feature is enabled. [#4806](https://github.com/PyO3/pyo3/pull/4806)
- Fix generating import lib for python3.13t when `abi3` feature is enabled. [#4808](https://github.com/PyO3/pyo3/pull/4808)
- Fix compile failure for raw identifiers like `r#box` in `derive(FromPyObject)`. [#4814](https://github.com/PyO3/pyo3/pull/4814)
- Fix compile failure for `#[pyclass]` enum variants with more than 12 fields. [#4832](https://github.com/PyO3/pyo3/pull/4832)
## [0.23.3] - 2024-12-03
### Packaging
- Bump optional `python3-dll-a` dependency to 0.2.11. [#4749](https://github.com/PyO3/pyo3/pull/4749)
### Fixed
- Fix unresolved symbol link failures on Windows when compiling for Python 3.13t with `abi3` features enabled. [#4733](https://github.com/PyO3/pyo3/pull/4733)
- Fix unresolved symbol link failures on Windows when compiling for Python 3.13t using the `generate-import-lib` feature. [#4749](https://github.com/PyO3/pyo3/pull/4749)
- Fix compile-time regression in PyO3 0.23.0 where changing `PYO3_CONFIG_FILE` would not reconfigure PyO3 for the new interpreter. [#4758](https://github.com/PyO3/pyo3/pull/4758)
## [0.23.2] - 2024-11-25
### Added
- Add `IntoPyObjectExt` trait. [#4708](https://github.com/PyO3/pyo3/pull/4708)
### Fixed
- Fix compile failures when building for free-threaded Python when the `abi3` or `abi3-pyxx` features are enabled. [#4719](https://github.com/PyO3/pyo3/pull/4719)
- Fix `ambiguous_associated_items` lint error in `#[pyclass]` and `#[derive(IntoPyObject)]` macros. [#4725](https://github.com/PyO3/pyo3/pull/4725)
## [0.23.1] - 2024-11-16
Re-release of 0.23.0 with fixes to docs.rs build.
## [0.23.0] - 2024-11-15
### Packaging
- Drop support for PyPy 3.7 and 3.8. [#4582](https://github.com/PyO3/pyo3/pull/4582)
- Extend range of supported versions of `hashbrown` optional dependency to include version 0.15. [#4604](https://github.com/PyO3/pyo3/pull/4604)
- Bump minimum version of `eyre` optional dependency to 0.6.8. [#4617](https://github.com/PyO3/pyo3/pull/4617)
- Bump minimum version of `hashbrown` optional dependency to 0.14.5. [#4617](https://github.com/PyO3/pyo3/pull/4617)
- Bump minimum version of `indexmap` optional dependency to 2.5.0. [#4617](https://github.com/PyO3/pyo3/pull/4617)
- Bump minimum version of `num-complex` optional dependency to 0.4.6. [#4617](https://github.com/PyO3/pyo3/pull/4617)
- Bump minimum version of `chrono-tz` optional dependency to 0.10. [#4617](https://github.com/PyO3/pyo3/pull/4617)
- Support free-threaded Python 3.13t. [#4588](https://github.com/PyO3/pyo3/pull/4588)
### Added
- Add `IntoPyObject` (fallible) conversion trait to convert from Rust to Python values. [#4060](https://github.com/PyO3/pyo3/pull/4060)
- Add `#[pyclass(str="")]` option to generate `__str__` based on a `Display` implementation or format string. [#4233](https://github.com/PyO3/pyo3/pull/4233)
- Implement `PartialEq` for `Bound<'py, PyInt>` with `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `i8`, `i16`, `i32`, `i64`, `i128` and `isize`. [#4317](https://github.com/PyO3/pyo3/pull/4317)
- Implement `PartialEq` and `PartialEq` for `Bound<'py, PyFloat>`. [#4348](https://github.com/PyO3/pyo3/pull/4348)
- Add `as_super` and `into_super` methods for `Bound`. [#4351](https://github.com/PyO3/pyo3/pull/4351)
- Add FFI definitions `PyCFunctionFast` and `PyCFunctionFastWithKeywords` [#4415](https://github.com/PyO3/pyo3/pull/4415)
- Add FFI definitions for `PyMutex` on Python 3.13 and newer. [#4421](https://github.com/PyO3/pyo3/pull/4421)
- Add `PyDict::locked_for_each` to iterate efficiently on freethreaded Python. [#4439](https://github.com/PyO3/pyo3/pull/4439)
- Add FFI definitions `PyObject_GetOptionalAttr`, `PyObject_GetOptionalAttrString`, `PyObject_HasAttrWithError`, `PyObject_HasAttrStringWithError`, `Py_CONSTANT_*` constants, `Py_GetConstant`, `Py_GetConstantBorrowed`, and `PyType_GetModuleByDef` on Python 3.13 and newer. [#4447](https://github.com/PyO3/pyo3/pull/4447)
- Add FFI definitions for the Python critical section API available on Python 3.13 and newer. [#4477](https://github.com/PyO3/pyo3/pull/4477)
- Add derive macro for `IntoPyObject`. [#4495](https://github.com/PyO3/pyo3/pull/4495)
- Add `Borrowed::as_ptr`. [#4520](https://github.com/PyO3/pyo3/pull/4520)
- Add FFI definition for `PyImport_AddModuleRef`. [#4529](https://github.com/PyO3/pyo3/pull/4529)
- Add `PyAnyMethods::try_iter`. [#4553](https://github.com/PyO3/pyo3/pull/4553)
- Add `pyo3::sync::with_critical_section`, a wrapper around the Python Critical Section API added in Python 3.13. [#4587](https://github.com/PyO3/pyo3/pull/4587)
- Add `#[pymodule(gil_used = false)]` option to declare that a module supports the free-threaded build. [#4588](https://github.com/PyO3/pyo3/pull/4588)
- Add `PyModule::gil_used` method to declare that a module supports the free-threaded build. [#4588](https://github.com/PyO3/pyo3/pull/4588)
- Add FFI definition `PyDateTime_CAPSULE_NAME`. [#4634](https://github.com/PyO3/pyo3/pull/4634)
- Add `PyMappingProxy` type to represent the `mappingproxy` Python class. [#4644](https://github.com/PyO3/pyo3/pull/4644)
- Add FFI definitions `PyList_Extend` and `PyList_Clear`. [#4667](https://github.com/PyO3/pyo3/pull/4667)
- Add derive macro for `IntoPyObjectRef`. [#4674](https://github.com/PyO3/pyo3/pull/4674)
- Add `pyo3::sync::OnceExt` and `pyo3::sync::OnceLockExt` traits. [#4676](https://github.com/PyO3/pyo3/pull/4676)
### Changed
- Prefer `IntoPyObject` over `IntoPy>>` for `#[pyfunction]` and `#[pymethods]` return types. [#4060](https://github.com/PyO3/pyo3/pull/4060)
- Report multiple errors from `#[pyclass]` and `#[pyo3(..)]` attributes. [#4243](https://github.com/PyO3/pyo3/pull/4243)
- Nested declarative `#[pymodule]` are automatically treated as submodules (no `PyInit_` entrypoint is created). [#4308](https://github.com/PyO3/pyo3/pull/4308)
- Deprecate `PyAnyMethods::is_ellipsis` (`Py::is_ellipsis` was deprecated in PyO3 0.20). [#4322](https://github.com/PyO3/pyo3/pull/4322)
- Deprecate `PyLong` in favor of `PyInt`. [#4347](https://github.com/PyO3/pyo3/pull/4347)
- Rename `IntoPyDict::into_py_dict_bound` to `IntoPyDict::into_py_dict`. [#4388](https://github.com/PyO3/pyo3/pull/4388)
- `PyModule::from_code` now expects `&CStr` as arguments instead of `&str`. [#4404](https://github.com/PyO3/pyo3/pull/4404)
- Use "fastcall" Python calling convention for `#[pyfunction]`s when compiling on abi3 for Python 3.10 and up. [#4415](https://github.com/PyO3/pyo3/pull/4415)
- Remove `Copy` and `Clone` from `PyObject` struct FFI definition. [#4434](https://github.com/PyO3/pyo3/pull/4434)
- `Python::eval` and `Python::run` now take a `&CStr` instead of `&str`. [#4435](https://github.com/PyO3/pyo3/pull/4435)
- Deprecate `IPowModulo`, `PyClassAttributeDef`, `PyGetterDef`, `PyMethodDef`, `PyMethodDefType`, and `PySetterDef` from PyO3's public API. [#4441](https://github.com/PyO3/pyo3/pull/4441)
- `IntoPyObject` impls for `Vec`, `&[u8]`, `[u8; N]`, `Cow<[u8]>` and `SmallVec<[u8; N]>` now convert into Python `bytes` rather than a `list` of integers. [#4442](https://github.com/PyO3/pyo3/pull/4442)
- Emit a compile-time error when attempting to subclass a class that doesn't allow subclassing. [#4453](https://github.com/PyO3/pyo3/pull/4453)
- `IntoPyDict::into_py_dict` is now fallible due to `IntoPyObject` migration. [#4493](https://github.com/PyO3/pyo3/pull/4493)
- The `abi3` feature will now override config files provided via `PYO3_BUILD_CONFIG`. [#4497](https://github.com/PyO3/pyo3/pull/4497)
- Disable the `GILProtected` struct on free-threaded Python. [#4504](https://github.com/PyO3/pyo3/pull/4504)
- Updated FFI definitions for functions and struct fields that have been deprecated or removed from CPython. [#4534](https://github.com/PyO3/pyo3/pull/4534)
- Disable `PyListMethods::get_item_unchecked` on free-threaded Python. [#4539](https://github.com/PyO3/pyo3/pull/4539)
- Add `GILOnceCell::import`. [#4542](https://github.com/PyO3/pyo3/pull/4542)
- Deprecate `PyAnyMethods::iter` in favour of `PyAnyMethods::try_iter`. [#4553](https://github.com/PyO3/pyo3/pull/4553)
- The `#[pyclass]` macro now requires a types to be `Sync`. (Except for `#[pyclass(unsendable)]` types). [#4566](https://github.com/PyO3/pyo3/pull/4566)
- `PyList::new` and `PyTuple::new` are now fallible due to `IntoPyObject` migration. [#4580](https://github.com/PyO3/pyo3/pull/4580)
- `PyErr::matches` is now fallible due to `IntoPyObject` migration. [#4595](https://github.com/PyO3/pyo3/pull/4595)
- Deprecate `ToPyObject` in favour of `IntoPyObject` [#4595](https://github.com/PyO3/pyo3/pull/4595)
- Deprecate `PyWeakrefMethods::get_option`. [#4597](https://github.com/PyO3/pyo3/pull/4597)
- Seal `PyWeakrefMethods` trait. [#4598](https://github.com/PyO3/pyo3/pull/4598)
- Remove `PyNativeTypeInitializer` and `PyObjectInit` from the PyO3 public API. [#4611](https://github.com/PyO3/pyo3/pull/4611)
- Deprecate `IntoPy` in favor of `IntoPyObject` [#4618](https://github.com/PyO3/pyo3/pull/4618)
- Eagerly normalize exceptions in `PyErr::take()` and `PyErr::fetch()` on Python 3.11 and older. [#4655](https://github.com/PyO3/pyo3/pull/4655)
- Move `IntoPy::type_output` to `IntoPyObject::type_output`. [#4657](https://github.com/PyO3/pyo3/pull/4657)
- Change return type of `PyMapping::keys`, `PyMapping::values` and `PyMapping::items` to `Bound<'py, PyList>` instead of `Bound<'py, PySequence>`. [#4661](https://github.com/PyO3/pyo3/pull/4661)
- Complex enums now allow field types that either implement `IntoPyObject` by reference or by value together with `Clone`. This makes `Py` available as field type. [#4694](https://github.com/PyO3/pyo3/pull/4694)
### Removed
- Remove all functionality deprecated in PyO3 0.20. [#4322](https://github.com/PyO3/pyo3/pull/4322)
- Remove all functionality deprecated in PyO3 0.21. [#4323](https://github.com/PyO3/pyo3/pull/4323)
- Deprecate `PyUnicode` in favour of `PyString`. [#4370](https://github.com/PyO3/pyo3/pull/4370)
- Remove deprecated `gil-refs` feature. [#4378](https://github.com/PyO3/pyo3/pull/4378)
- Remove private FFI definitions `_Py_IMMORTAL_REFCNT`, `_Py_IsImmortal`, `_Py_TPFLAGS_STATIC_BUILTIN`, `_Py_Dealloc`, `_Py_IncRef`, `_Py_DecRef`. [#4447](https://github.com/PyO3/pyo3/pull/4447)
- Remove private FFI definitions `_Py_c_sum`, `_Py_c_diff`, `_Py_c_neg`, `_Py_c_prod`, `_Py_c_quot`, `_Py_c_pow`, `_Py_c_abs`. [#4521](https://github.com/PyO3/pyo3/pull/4521)
- Remove `_borrowed` methods of `PyWeakRef` and `PyWeakRefProxy`. [#4528](https://github.com/PyO3/pyo3/pull/4528)
- Removed private FFI definition `_PyErr_ChainExceptions`. [#4534](https://github.com/PyO3/pyo3/pull/4534)
### Fixed
- Fix invalid library search path `lib_dir` when cross-compiling. [#4389](https://github.com/PyO3/pyo3/pull/4389)
- Fix FFI definition `Py_Is` for PyPy on 3.10 to call the function defined by PyPy. [#4447](https://github.com/PyO3/pyo3/pull/4447)
- Fix compile failure when using `#[cfg]` attributes for simple enum variants. [#4509](https://github.com/PyO3/pyo3/pull/4509)
- Fix compiler warning for `non_snake_case` method names inside `#[pymethods]` generated code. [#4567](https://github.com/PyO3/pyo3/pull/4567)
- Fix compile error with `#[derive(FromPyObject)]` generic struct with trait bounds. [#4645](https://github.com/PyO3/pyo3/pull/4645)
- Fix compile error for `#[classmethod]` and `#[staticmethod]` on magic methods. [#4654](https://github.com/PyO3/pyo3/pull/4654)
- Fix compile warning for `unsafe_op_in_unsafe_fn` in generated macro code. [#4674](https://github.com/PyO3/pyo3/pull/4674)
- Fix incorrect deprecation warning for `#[pyclass] enum`s with custom `__eq__` implementation. [#4692](https://github.com/PyO3/pyo3/pull/4692)
- Fix `non_upper_case_globals` lint firing for generated `__match_args__` on complex enums. [#4705](https://github.com/PyO3/pyo3/pull/4705)
## [0.22.5] - 2024-10-15
### Fixed
- Fix regression in 0.22.4 of naming collision in `__clear__` slot and `clear` method generated code. [#4619](https://github.com/PyO3/pyo3/pull/4619)
## [0.22.4] - 2024-10-12
### Added
- Add FFI definition `PyWeakref_GetRef` and `compat::PyWeakref_GetRef`. [#4528](https://github.com/PyO3/pyo3/pull/4528)
### Changed
- Deprecate `_borrowed` methods on `PyWeakRef` and `PyWeakrefProxy` (just use the owning forms). [#4590](https://github.com/PyO3/pyo3/pull/4590)
### Fixed
- Revert removal of private FFI function `_PyLong_NumBits` on Python 3.13 and later. [#4450](https://github.com/PyO3/pyo3/pull/4450)
- Fix `__traverse__` functions for base classes not being called by subclasses created with `#[pyclass(extends = ...)]`. [#4563](https://github.com/PyO3/pyo3/pull/4563)
- Fix regression in 0.22.3 failing compiles under `#![forbid(unsafe_code)]`. [#4574](https://github.com/PyO3/pyo3/pull/4574)
- Fix `create_exception` macro triggering lint and compile errors due to interaction with `gil-refs` feature. [#4589](https://github.com/PyO3/pyo3/pull/4589)
- Workaround possible use-after-free in `_borrowed` methods on `PyWeakRef` and `PyWeakrefProxy` by leaking their contents. [#4590](https://github.com/PyO3/pyo3/pull/4590)
- Fix crash calling `PyType_GetSlot` on static types before Python 3.10. [#4599](https://github.com/PyO3/pyo3/pull/4599)
## [0.22.3] - 2024-09-15
### Added
- Add `pyo3::ffi::compat` namespace with compatibility shims for C API functions added in recent versions of Python.
- Add FFI definition `PyDict_GetItemRef` on Python 3.13 and newer, and `compat::PyDict_GetItemRef` for all versions. [#4355](https://github.com/PyO3/pyo3/pull/4355)
- Add FFI definition `PyList_GetItemRef` on Python 3.13 and newer, and `pyo3_ffi::compat::PyList_GetItemRef` for all versions. [#4410](https://github.com/PyO3/pyo3/pull/4410)
- Add FFI definitions `compat::Py_NewRef` and `compat::Py_XNewRef`. [#4445](https://github.com/PyO3/pyo3/pull/4445)
- Add FFI definitions `compat::PyObject_CallNoArgs` and `compat::PyObject_CallMethodNoArgs`. [#4461](https://github.com/PyO3/pyo3/pull/4461)
- Add `GilOnceCell>::clone_ref`. [#4511](https://github.com/PyO3/pyo3/pull/4511)
### Changed
- Improve error messages for `#[pyfunction]` defined inside `#[pymethods]`. [#4349](https://github.com/PyO3/pyo3/pull/4349)
- Improve performance of calls to Python by using the vectorcall calling convention where possible. [#4456](https://github.com/PyO3/pyo3/pull/4456)
- Mention the type name in the exception message when trying to instantiate a class with no constructor defined. [#4481](https://github.com/PyO3/pyo3/pull/4481)
### Removed
- Remove private FFI definition `_Py_PackageContext`. [#4420](https://github.com/PyO3/pyo3/pull/4420)
### Fixed
- Fix compile failure in declarative `#[pymodule]` under presence of `#![no_implicit_prelude]`. [#4328](https://github.com/PyO3/pyo3/pull/4328)
- Fix use of borrowed reference in `PyDict::get_item` (unsafe in free-threaded Python). [#4355](https://github.com/PyO3/pyo3/pull/4355)
- Fix `#[pyclass(eq)]` macro hygiene issues for structs and enums. [#4359](https://github.com/PyO3/pyo3/pull/4359)
- Fix hygiene/span issues of `#[pyfunction]` and `#[pymethods]` generated code which affected expansion in `macro_rules` context. [#4382](https://github.com/PyO3/pyo3/pull/4382)
- Fix `unsafe_code` lint error in `#[pyclass]` generated code. [#4396](https://github.com/PyO3/pyo3/pull/4396)
- Fix async functions returning a tuple only returning the first element to Python. [#4407](https://github.com/PyO3/pyo3/pull/4407)
- Fix use of borrowed reference in `PyList::get_item` (unsafe in free-threaded Python). [#4410](https://github.com/PyO3/pyo3/pull/4410)
- Correct FFI definition `PyArg_ParseTupleAndKeywords` to take `*const *const c_char` instead of `*mut *mut c_char` on Python 3.13 and up. [#4420](https://github.com/PyO3/pyo3/pull/4420)
- Fix a soundness bug with `PyClassInitializer`: panic if adding subclass to existing instance via `PyClassInitializer::from(Py).add_subclass(SubClass)`. [#4454](https://github.com/PyO3/pyo3/pull/4454)
- Fix illegal reference counting op inside implementation of `__traverse__` handlers. [#4479](https://github.com/PyO3/pyo3/pull/4479)
## [0.22.2] - 2024-07-17
### Packaging
- Require opt-in to freethreaded Python using the `UNSAFE_PYO3_BUILD_FREE_THREADED=1` environment variable (it is not yet supported by PyO3). [#4327](https://github.com/PyO3/pyo3/pull/4327)
### Changed
- Use FFI function calls for reference counting on all abi3 versions. [#4324](https://github.com/PyO3/pyo3/pull/4324)
- `#[pymodule(...)]` now directly accepts all relevant `#[pyo3(...)]` options. [#4330](https://github.com/PyO3/pyo3/pull/4330)
### Fixed
- Fix compile failure in declarative `#[pymodule]` under presence of `#![no_implicit_prelude]`. [#4328](https://github.com/PyO3/pyo3/pull/4328)
- Fix compile failure due to c-string literals on Rust < 1.79. [#4353](https://github.com/PyO3/pyo3/pull/4353)
## [0.22.1] - 2024-07-06
### Added
- Add `#[pyo3(submodule)]` option for declarative `#[pymodule]`s. [#4301](https://github.com/PyO3/pyo3/pull/4301)
- Implement `PartialEq` for `Bound<'py, PyBool>`. [#4305](https://github.com/PyO3/pyo3/pull/4305)
### Fixed
- Return `NotImplemented` instead of raising `TypeError` from generated equality method when comparing different types. [#4287](https://github.com/PyO3/pyo3/pull/4287)
- Handle full-path `#[pyo3::prelude::pymodule]` and similar for `#[pyclass]` and `#[pyfunction]` in declarative modules. [#4288](https://github.com/PyO3/pyo3/pull/4288)
- Fix 128-bit int regression on big-endian platforms with Python <3.13. [#4291](https://github.com/PyO3/pyo3/pull/4291)
- Stop generating code that will never be covered with declarative modules. [#4297](https://github.com/PyO3/pyo3/pull/4297)
- Fix invalid deprecation warning for trailing optional on `#[setter]` function. [#4304](https://github.com/PyO3/pyo3/pull/4304)
## [0.22.0] - 2024-06-24
### Packaging
- Update `heck` dependency to 0.5. [#3966](https://github.com/PyO3/pyo3/pull/3966)
- Extend range of supported versions of `chrono-tz` optional dependency to include version 0.10. [#4061](https://github.com/PyO3/pyo3/pull/4061)
- Update MSRV to 1.63. [#4129](https://github.com/PyO3/pyo3/pull/4129)
- Add optional `num-rational` feature to add conversions with Python's `fractions.Fraction`. [#4148](https://github.com/PyO3/pyo3/pull/4148)
- Support Python 3.13. [#4184](https://github.com/PyO3/pyo3/pull/4184)
### Added
- Add `PyWeakref`, `PyWeakrefReference` and `PyWeakrefProxy`. [#3835](https://github.com/PyO3/pyo3/pull/3835)
- Support `#[pyclass]` on enums that have tuple variants. [#4072](https://github.com/PyO3/pyo3/pull/4072)
- Add support for scientific notation in `Decimal` conversion. [#4079](https://github.com/PyO3/pyo3/pull/4079)
- Add `pyo3_disable_reference_pool` conditional compilation flag to avoid the overhead of the global reference pool at the cost of known limitations as explained in the performance section of the guide. [#4095](https://github.com/PyO3/pyo3/pull/4095)
- Add `#[pyo3(constructor = (...))]` to customize the generated constructors for complex enum variants. [#4158](https://github.com/PyO3/pyo3/pull/4158)
- Add `PyType::module`, which always matches Python `__module__`. [#4196](https://github.com/PyO3/pyo3/pull/4196)
- Add `PyType::fully_qualified_name` which matches the "fully qualified name" defined in [PEP 737](https://peps.python.org/pep-0737). [#4196](https://github.com/PyO3/pyo3/pull/4196)
- Add `PyTypeMethods::mro` and `PyTypeMethods::bases`. [#4197](https://github.com/PyO3/pyo3/pull/4197)
- Add `#[pyclass(ord)]` to implement ordering based on `PartialOrd`. [#4202](https://github.com/PyO3/pyo3/pull/4202)
- Implement `ToPyObject` and `IntoPy` for `PyBackedStr` and `PyBackedBytes`. [#4205](https://github.com/PyO3/pyo3/pull/4205)
- Add `#[pyclass(hash)]` option to implement `__hash__` in terms of the `Hash` implementation [#4206](https://github.com/PyO3/pyo3/pull/4206)
- Add `#[pyclass(eq)]` option to generate `__eq__` based on `PartialEq`, and `#[pyclass(eq_int)]` for simple enums to implement equality based on their discriminants. [#4210](https://github.com/PyO3/pyo3/pull/4210)
- Implement `From>` for `PyClassInitializer`. [#4214](https://github.com/PyO3/pyo3/pull/4214)
- Add `as_super` methods to `PyRef` and `PyRefMut` for accessing the base class by reference. [#4219](https://github.com/PyO3/pyo3/pull/4219)
- Implement `PartialEq` for `Bound<'py, PyString>`. [#4245](https://github.com/PyO3/pyo3/pull/4245)
- Implement `PyModuleMethods::filename` on PyPy. [#4249](https://github.com/PyO3/pyo3/pull/4249)
- Implement `PartialEq<[u8]>` for `Bound<'py, PyBytes>`. [#4250](https://github.com/PyO3/pyo3/pull/4250)
- Add `pyo3_ffi::c_str` macro to create `&'static CStr` on Rust versions which don't have 1.77's `c""` literals. [#4255](https://github.com/PyO3/pyo3/pull/4255)
- Support `bool` conversion with `numpy` 2.0's `numpy.bool` type [#4258](https://github.com/PyO3/pyo3/pull/4258)
- Add `PyAnyMethods::{bitnot, matmul, floor_div, rem, divmod}`. [#4264](https://github.com/PyO3/pyo3/pull/4264)
### Changed
- Change the type of `PySliceIndices::slicelength` and the `length` parameter of `PySlice::indices()`. [#3761](https://github.com/PyO3/pyo3/pull/3761)
- Deprecate implicit default for trailing optional arguments [#4078](https://github.com/PyO3/pyo3/pull/4078)
- `Clone`ing pointers into the Python heap has been moved behind the `py-clone` feature, as it must panic without the GIL being held as a soundness fix. [#4095](https://github.com/PyO3/pyo3/pull/4095)
- Add `#[track_caller]` to all `Py`, `Bound<'py, T>` and `Borrowed<'a, 'py, T>` methods which can panic. [#4098](https://github.com/PyO3/pyo3/pull/4098)
- Change `PyAnyMethods::dir` to be fallible and return `PyResult>` (and similar for `PyAny::dir`). [#4100](https://github.com/PyO3/pyo3/pull/4100)
- The global reference pool (to track pending reference count decrements) is now initialized lazily to avoid the overhead of taking a mutex upon function entry when the functionality is not actually used. [#4178](https://github.com/PyO3/pyo3/pull/4178)
- Emit error messages when using `weakref` or `dict` when compiling for `abi3` for Python older than 3.9. [#4194](https://github.com/PyO3/pyo3/pull/4194)
- Change `PyType::name` to always match Python `__name__`. [#4196](https://github.com/PyO3/pyo3/pull/4196)
- Remove CPython internal ffi call for complex number including: add, sub, mul, div, neg, abs, pow. Added PyAnyMethods::{abs, pos, neg} [#4201](https://github.com/PyO3/pyo3/pull/4201)
- Deprecate implicit integer comparison for simple enums in favor of `#[pyclass(eq_int)]`. [#4210](https://github.com/PyO3/pyo3/pull/4210)
- Set the `module=` attribute of declarative modules' child `#[pymodule]`s and `#[pyclass]`es. [#4213](https://github.com/PyO3/pyo3/pull/4213)
- Set the `module` option for complex enum variants from the value set on the complex enum `module`. [#4228](https://github.com/PyO3/pyo3/pull/4228)
- Respect the Python "limited API" when building for the `abi3` feature on PyPy or GraalPy. [#4237](https://github.com/PyO3/pyo3/pull/4237)
- Optimize code generated by `#[pyo3(get)]` on `#[pyclass]` fields. [#4254](https://github.com/PyO3/pyo3/pull/4254)
- `PyCFunction::new`, `PyCFunction::new_with_keywords` and `PyCFunction::new_closure` now take `&'static CStr` name and doc arguments (previously was `&'static str`). [#4255](https://github.com/PyO3/pyo3/pull/4255)
- The `experimental-declarative-modules` feature is now stabilized and available by default. [#4257](https://github.com/PyO3/pyo3/pull/4257)
### Fixed
- Fix panic when `PYO3_CROSS_LIB_DIR` is set to a missing path. [#4043](https://github.com/PyO3/pyo3/pull/4043)
- Fix a compile error when exporting an exception created with `create_exception!` living in a different Rust module using the `declarative-module` feature. [#4086](https://github.com/PyO3/pyo3/pull/4086)
- Fix FFI definitions of `PY_VECTORCALL_ARGUMENTS_OFFSET` and `PyVectorcall_NARGS` to fix a false-positive assertion. [#4104](https://github.com/PyO3/pyo3/pull/4104)
- Disable `PyUnicode_DATA` on PyPy: not exposed by PyPy. [#4116](https://github.com/PyO3/pyo3/pull/4116)
- Correctly handle `#[pyo3(from_py_with = ...)]` attribute on dunder (`__magic__`) method arguments instead of silently ignoring it. [#4117](https://github.com/PyO3/pyo3/pull/4117)
- Fix a compile error when declaring a standalone function or class method with a Python name that is a Rust keyword. [#4226](https://github.com/PyO3/pyo3/pull/4226)
- Fix declarative modules discarding doc comments on the `mod` node. [#4236](https://github.com/PyO3/pyo3/pull/4236)
- Fix `__dict__` attribute missing for `#[pyclass(dict)]` instances when building for `abi3` on Python 3.9. [#4251](https://github.com/PyO3/pyo3/pull/4251)
## [0.21.2] - 2024-04-16
### Changed
- Deprecate the `PySet::empty()` gil-ref constructor. [#4082](https://github.com/PyO3/pyo3/pull/4082)
### Fixed
- Fix compile error for `async fn` in `#[pymethods]` with a `&self` receiver and more than one additional argument. [#4035](https://github.com/PyO3/pyo3/pull/4035)
- Improve error message for wrong receiver type in `__traverse__`. [#4045](https://github.com/PyO3/pyo3/pull/4045)
- Fix compile error when exporting a `#[pyclass]` living in a different Rust module using the `experimental-declarative-modules` feature. [#4054](https://github.com/PyO3/pyo3/pull/4054)
- Fix `missing_docs` lint triggering on documented `#[pymodule]` functions. [#4067](https://github.com/PyO3/pyo3/pull/4067)
- Fix undefined symbol errors for extension modules on AIX (by linking `libpython`). [#4073](https://github.com/PyO3/pyo3/pull/4073)
## [0.21.1] - 2024-04-01
### Added
- Implement `Send` and `Sync` for `PyBackedStr` and `PyBackedBytes`. [#4007](https://github.com/PyO3/pyo3/pull/4007)
- Implement `Clone`, `Debug`, `PartialEq`, `Eq`, `PartialOrd`, `Ord` and `Hash` implementation for `PyBackedBytes` and `PyBackedStr`, and `Display` for `PyBackedStr`. [#4020](https://github.com/PyO3/pyo3/pull/4020)
- Add `import_exception_bound!` macro to import exception types without generating GIL Ref functionality for them. [#4027](https://github.com/PyO3/pyo3/pull/4027)
### Changed
- Emit deprecation warning for uses of GIL Refs as `#[setter]` function arguments. [#3998](https://github.com/PyO3/pyo3/pull/3998)
- Add `#[inline]` hints on many `Bound` and `Borrowed` methods. [#4024](https://github.com/PyO3/pyo3/pull/4024)
### Fixed
- Handle `#[pyo3(from_py_with = "")]` in `#[setter]` methods [#3995](https://github.com/PyO3/pyo3/pull/3995)
- Allow extraction of `&Bound` in `#[setter]` methods. [#3998](https://github.com/PyO3/pyo3/pull/3998)
- Fix some uncovered code blocks emitted by `#[pymodule]`, `#[pyfunction]` and `#[pyclass]` macros. [#4009](https://github.com/PyO3/pyo3/pull/4009)
- Fix typo in the panic message when a class referenced in `pyo3::import_exception!` does not exist. [#4012](https://github.com/PyO3/pyo3/pull/4012)
- Fix compile error when using an async `#[pymethod]` with a receiver and additional arguments. [#4015](https://github.com/PyO3/pyo3/pull/4015)
## [0.21.0] - 2024-03-25
### Added
- Add support for GraalPy (24.0 and up). [#3247](https://github.com/PyO3/pyo3/pull/3247)
- Add `PyMemoryView` type. [#3514](https://github.com/PyO3/pyo3/pull/3514)
- Allow `async fn` in for `#[pyfunction]` and `#[pymethods]`, with the `experimental-async` feature. [#3540](https://github.com/PyO3/pyo3/pull/3540) [#3588](https://github.com/PyO3/pyo3/pull/3588) [#3599](https://github.com/PyO3/pyo3/pull/3599) [#3931](https://github.com/PyO3/pyo3/pull/3931)
- Implement `PyTypeInfo` for `PyEllipsis`, `PyNone` and `PyNotImplemented`. [#3577](https://github.com/PyO3/pyo3/pull/3577)
- Support `#[pyclass]` on enums that have non-unit variants. [#3582](https://github.com/PyO3/pyo3/pull/3582)
- Support `chrono` feature with `abi3` feature. [#3664](https://github.com/PyO3/pyo3/pull/3664)
- `FromPyObject`, `IntoPy` and `ToPyObject` are implemented on `std::duration::Duration` [#3670](https://github.com/PyO3/pyo3/pull/3670)
- Add `PyString::to_cow`. Add `Py::to_str`, `Py::to_cow`, and `Py::to_string_lossy`, as ways to access Python string data safely beyond the GIL lifetime. [#3677](https://github.com/PyO3/pyo3/pull/3677)
- Add `Bound` and `Borrowed` smart pointers as a new API for accessing Python objects. [#3686](https://github.com/PyO3/pyo3/pull/3686)
- Add `PyNativeType::as_borrowed` to convert "GIL refs" to the new `Bound` smart pointer. [#3692](https://github.com/PyO3/pyo3/pull/3692)
- Add `FromPyObject::extract_bound` method, to migrate `FromPyObject` implementations to the Bound API. [#3706](https://github.com/PyO3/pyo3/pull/3706)
- Add `gil-refs` feature to allow continued use of the deprecated GIL Refs APIs. [#3707](https://github.com/PyO3/pyo3/pull/3707)
- Add methods to `PyAnyMethods` for binary operators (`add`, `sub`, etc.) [#3712](https://github.com/PyO3/pyo3/pull/3712)
- Add `chrono-tz` feature allowing conversion between `chrono_tz::Tz` and `zoneinfo.ZoneInfo` [#3730](https://github.com/PyO3/pyo3/pull/3730)
- Add FFI definition `PyType_GetModuleByDef`. [#3734](https://github.com/PyO3/pyo3/pull/3734)
- Conversion between `std::time::SystemTime` and `datetime.datetime` [#3736](https://github.com/PyO3/pyo3/pull/3736)
- Add `Py::as_any` and `Py::into_any`. [#3785](https://github.com/PyO3/pyo3/pull/3785)
- Add `PyStringMethods::encode_utf8`. [#3801](https://github.com/PyO3/pyo3/pull/3801)
- Add `PyBackedStr` and `PyBackedBytes`, as alternatives to `&str` and `&bytes` where a Python object owns the data. [#3802](https://github.com/PyO3/pyo3/pull/3802) [#3991](https://github.com/PyO3/pyo3/pull/3991)
- Allow `#[pymodule]` macro on Rust `mod` blocks, with the `experimental-declarative-modules` feature. [#3815](https://github.com/PyO3/pyo3/pull/3815)
- Implement `ExactSizeIterator` for `set` and `frozenset` iterators on `abi3` feature. [#3849](https://github.com/PyO3/pyo3/pull/3849)
- Add `Py::drop_ref` to explicitly drop a `Py`` and immediately decrease the Python reference count if the GIL is already held. [#3871](https://github.com/PyO3/pyo3/pull/3871)
- Allow `#[pymodule]` macro on single argument functions that take `&Bound<'_, PyModule>`. [#3905](https://github.com/PyO3/pyo3/pull/3905)
- Implement `FromPyObject` for `Cow`. [#3928](https://github.com/PyO3/pyo3/pull/3928)
- Implement `Default` for `GILOnceCell`. [#3971](https://github.com/PyO3/pyo3/pull/3971)
- Add `PyDictMethods::into_mapping`, `PyListMethods::into_sequence` and `PyTupleMethods::into_sequence`. [#3982](https://github.com/PyO3/pyo3/pull/3982)
### Changed
- `PyDict::from_sequence` now takes a single argument of type `&PyAny` (previously took two arguments `Python` and `PyObject`). [#3532](https://github.com/PyO3/pyo3/pull/3532)
- Deprecate `Py::is_ellipsis` and `PyAny::is_ellipsis` in favour of `any.is(py.Ellipsis())`. [#3577](https://github.com/PyO3/pyo3/pull/3577)
- Split some `PyTypeInfo` functionality into new traits `HasPyGilRef` and `PyTypeCheck`. [#3600](https://github.com/PyO3/pyo3/pull/3600)
- Deprecate `PyTryFrom` and `PyTryInto` traits in favor of `any.downcast()` via the `PyTypeCheck` and `PyTypeInfo` traits. [#3601](https://github.com/PyO3/pyo3/pull/3601)
- Allow async methods to accept `&self`/`&mut self` [#3609](https://github.com/PyO3/pyo3/pull/3609)
- `FromPyObject` for set types now also accept `frozenset` objects as input. [#3632](https://github.com/PyO3/pyo3/pull/3632)
- `FromPyObject` for `bool` now also accepts NumPy's `bool_` as input. [#3638](https://github.com/PyO3/pyo3/pull/3638)
- Add `AsRefSource` associated type to `PyNativeType`. [#3653](https://github.com/PyO3/pyo3/pull/3653)
- Rename `.is_true` to `.is_truthy` on `PyAny` and `Py` to clarify that the test is not based on identity with or equality to the True singleton. [#3657](https://github.com/PyO3/pyo3/pull/3657)
- `PyType::name` is now `PyType::qualname` whereas `PyType::name` efficiently accesses the full name which includes the module name. [#3660](https://github.com/PyO3/pyo3/pull/3660)
- The `Iter(A)NextOutput` types are now deprecated and `__(a)next__` can directly return anything which can be converted into Python objects, i.e. awaitables do not need to be wrapped into `IterANextOutput` or `Option` any more. `Option` can still be used as well and returning `None` will trigger the fast path for `__next__`, stopping iteration without having to raise a `StopIteration` exception. [#3661](https://github.com/PyO3/pyo3/pull/3661)
- Implement `FromPyObject` on `chrono::DateTime` for all `Tz`, not just `FixedOffset` and `Utc`. [#3663](https://github.com/PyO3/pyo3/pull/3663)
- Add lifetime parameter to `PyTzInfoAccess` trait. For the deprecated gil-ref API, the trait is now implemented for `&'py PyTime` and `&'py PyDateTime` instead of `PyTime` and `PyDate`. [#3679](https://github.com/PyO3/pyo3/pull/3679)
- Calls to `__traverse__` become no-ops for unsendable pyclasses if on the wrong thread, thereby avoiding hard aborts at the cost of potential leakage. [#3689](https://github.com/PyO3/pyo3/pull/3689)
- Include `PyNativeType` in `pyo3::prelude`. [#3692](https://github.com/PyO3/pyo3/pull/3692)
- Improve performance of `extract::` (and other integer types) by avoiding call to `__index__()` converting the value to an integer for 3.10+. Gives performance improvement of around 30% for successful extraction. [#3742](https://github.com/PyO3/pyo3/pull/3742)
- Relax bound of `FromPyObject` for `Py` to just `T: PyTypeCheck`. [#3776](https://github.com/PyO3/pyo3/pull/3776)
- `PySet` and `PyFrozenSet` iterators now always iterate the equivalent of `iter(set)`. (A "fast path" with no noticeable performance benefit was removed.) [#3849](https://github.com/PyO3/pyo3/pull/3849)
- Move implementations of `FromPyObject` for `&str`, `Cow`, `&[u8]` and `Cow<[u8]>` onto a temporary trait `FromPyObjectBound` when `gil-refs` feature is deactivated. [#3928](https://github.com/PyO3/pyo3/pull/3928)
- Deprecate `GILPool`, `Python::with_pool`, and `Python::new_pool`. [#3947](https://github.com/PyO3/pyo3/pull/3947)
### Removed
- Remove all functionality deprecated in PyO3 0.19. [#3603](https://github.com/PyO3/pyo3/pull/3603)
### Fixed
- Match PyPy 7.3.14 in removing PyPy-only symbol `Py_MAX_NDIMS` in favour of `PyBUF_MAX_NDIM`. [#3757](https://github.com/PyO3/pyo3/pull/3757)
- Fix segmentation fault using `datetime` types when an invalid `datetime` module is on sys.path. [#3818](https://github.com/PyO3/pyo3/pull/3818)
- Fix `non_local_definitions` lint warning triggered by many PyO3 macros. [#3901](https://github.com/PyO3/pyo3/pull/3901)
- Disable `PyCode` and `PyCode_Type` on PyPy: `PyCode_Type` is not exposed by PyPy. [#3934](https://github.com/PyO3/pyo3/pull/3934)
## [0.21.0-beta.0] - 2024-03-10
Prerelease of PyO3 0.21. See [the GitHub diff](https://github.com/pyo3/pyo3/compare/v0.21.0-beta.0...v0.21.0) for what changed between 0.21.0-beta.0 and the final release.
## [0.20.3] - 2024-02-23
### Packaging
- Add `portable-atomic` dependency. [#3619](https://github.com/PyO3/pyo3/pull/3619)
- Check maximum version of Python at build time and for versions not yet supported require opt-in to the `abi3` stable ABI by the environment variable `PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1`. [#3821](https://github.com/PyO3/pyo3/pull/3821)
### Fixed
- Use `portable-atomic` to support platforms without 64-bit atomics. [#3619](https://github.com/PyO3/pyo3/pull/3619)
- Fix compilation failure with `either` feature enabled without `experimental-inspect` enabled. [#3834](https://github.com/PyO3/pyo3/pull/3834)
## [0.20.2] - 2024-01-04
### Packaging
- Pin `pyo3` and `pyo3-ffi` dependencies on `pyo3-build-config` to require the same patch version, i.e. `pyo3` 0.20.2 requires _exactly_ `pyo3-build-config` 0.20.2. [#3721](https://github.com/PyO3/pyo3/pull/3721)
### Fixed
- Fix compile failure when building `pyo3` 0.20.0 with latest `pyo3-build-config` 0.20.X. [#3724](https://github.com/PyO3/pyo3/pull/3724)
- Fix docs.rs build. [#3722](https://github.com/PyO3/pyo3/pull/3722)
## [0.20.1] - 2023-12-30
### Added
- Add optional `either` feature to add conversions for `either::Either` sum type. [#3456](https://github.com/PyO3/pyo3/pull/3456)
- Add optional `smallvec` feature to add conversions for `smallvec::SmallVec`. [#3507](https://github.com/PyO3/pyo3/pull/3507)
- Add `take` and `into_inner` methods to `GILOnceCell` [#3556](https://github.com/PyO3/pyo3/pull/3556)
- `#[classmethod]` methods can now also receive `Py` as their first argument. [#3587](https://github.com/PyO3/pyo3/pull/3587)
- `#[pyfunction(pass_module)]` can now also receive `Py` as their first argument. [#3587](https://github.com/PyO3/pyo3/pull/3587)
- Add `traverse` method to `GILProtected`. [#3616](https://github.com/PyO3/pyo3/pull/3616)
- Added `abi3-py312` feature [#3687](https://github.com/PyO3/pyo3/pull/3687)
### Fixed
- Fix minimum version specification for optional `chrono` dependency. [#3512](https://github.com/PyO3/pyo3/pull/3512)
- Silenced new `clippy::unnecessary_fallible_conversions` warning when using a `Py` `self` receiver. [#3564](https://github.com/PyO3/pyo3/pull/3564)
## [0.20.0] - 2023-10-11
### Packaging
- Dual-license PyO3 under either the Apache 2.0 OR the MIT license. This makes the project GPLv2 compatible. [#3108](https://github.com/PyO3/pyo3/pull/3108)
- Update MSRV to Rust 1.56. [#3208](https://github.com/PyO3/pyo3/pull/3208)
- Bump `indoc` dependency to 2.0 and `unindent` dependency to 0.2. [#3237](https://github.com/PyO3/pyo3/pull/3237)
- Bump `syn` dependency to 2.0. [#3239](https://github.com/PyO3/pyo3/pull/3239)
- Drop support for debug builds of Python 3.7. [#3387](https://github.com/PyO3/pyo3/pull/3387)
- Bump `chrono` optional dependency to require 0.4.25 or newer. [#3427](https://github.com/PyO3/pyo3/pull/3427)
- Support Python 3.12. [#3488](https://github.com/PyO3/pyo3/pull/3488)
### Added
- Support `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__` and `__ge__` in `#[pymethods]`. [#3203](https://github.com/PyO3/pyo3/pull/3203)
- Add FFI definition `Py_GETENV`. [#3336](https://github.com/PyO3/pyo3/pull/3336)
- Add `as_ptr` and `into_ptr` inherent methods for `Py`, `PyAny`, `PyRef`, and `PyRefMut`. [#3359](https://github.com/PyO3/pyo3/pull/3359)
- Implement `DoubleEndedIterator` for `PyTupleIterator` and `PyListIterator`. [#3366](https://github.com/PyO3/pyo3/pull/3366)
- Add `#[pyclass(rename_all = "...")]` option: this allows renaming all getters and setters of a struct, or all variants of an enum. Available renaming rules are: `"camelCase"`, `"kebab-case"`, `"lowercase"`, `"PascalCase"`, `"SCREAMING-KEBAB-CASE"`, `"SCREAMING_SNAKE_CASE"`, `"snake_case"`, `"UPPERCASE"`. [#3384](https://github.com/PyO3/pyo3/pull/3384)
- Add FFI definitions `PyObject_GC_IsTracked` and `PyObject_GC_IsFinalized` on Python 3.9 and up (PyPy 3.10 and up). [#3403](https://github.com/PyO3/pyo3/pull/3403)
- Add types for `None`, `Ellipsis`, and `NotImplemented`. [#3408](https://github.com/PyO3/pyo3/pull/3408)
- Add FFI definitions for the `Py_mod_multiple_interpreters` constant and its possible values. [#3494](https://github.com/PyO3/pyo3/pull/3494)
- Add FFI definitions for `PyInterpreterConfig` struct, its constants and `Py_NewInterpreterFromConfig`. [#3502](https://github.com/PyO3/pyo3/pull/3502)
### Changed
- Change `PySet::discard` to return `PyResult` (previously returned nothing). [#3281](https://github.com/PyO3/pyo3/pull/3281)
- Optimize implementation of `IntoPy` for Rust tuples to Python tuples. [#3321](https://github.com/PyO3/pyo3/pull/3321)
- Change `PyDict::get_item` to no longer suppress arbitrary exceptions (the return type is now `PyResult>` instead of `Option<&PyAny>`), and deprecate `PyDict::get_item_with_error`. [#3330](https://github.com/PyO3/pyo3/pull/3330)
- Deprecate FFI definitions which are deprecated in Python 3.12. [#3336](https://github.com/PyO3/pyo3/pull/3336)
- `AsPyPointer` is now an `unsafe trait`. [#3358](https://github.com/PyO3/pyo3/pull/3358)
- Accept all `os.PathLike` values in implementation of `FromPyObject` for `PathBuf`. [#3374](https://github.com/PyO3/pyo3/pull/3374)
- Add `__builtins__` to globals in `py.run()` and `py.eval()` if they're missing. [#3378](https://github.com/PyO3/pyo3/pull/3378)
- Optimize implementation of `FromPyObject` for `BigInt` and `BigUint`. [#3379](https://github.com/PyO3/pyo3/pull/3379)
- `PyIterator::from_object` and `PyByteArray::from` now take a single argument of type `&PyAny` (previously took two arguments `Python` and `AsPyPointer`). [#3389](https://github.com/PyO3/pyo3/pull/3389)
- Replace `AsPyPointer` with `AsRef` as a bound in the blanket implementation of `From<&T> for PyObject`. [#3391](https://github.com/PyO3/pyo3/pull/3391)
- Replace blanket `impl IntoPy for &T where T: AsPyPointer` with implementations of `impl IntoPy` for `&PyAny`, `&T where T: AsRef`, and `&Py`. [#3393](https://github.com/PyO3/pyo3/pull/3393)
- Preserve `std::io::Error` kind in implementation of `From` for `PyErr` [#3396](https://github.com/PyO3/pyo3/pull/3396)
- Try to select a relevant `ErrorKind` in implementation of `From` for `OSError` subclass. [#3397](https://github.com/PyO3/pyo3/pull/3397)
- Retrieve the original `PyErr` in implementation of `From` for `PyErr` if the `std::io::Error` has been built using a Python exception (previously would create a new exception wrapping the `std::io::Error`). [#3402](https://github.com/PyO3/pyo3/pull/3402)
- `#[pymodule]` will now return the same module object on repeated import by the same Python interpreter, on Python 3.9 and up. [#3446](https://github.com/PyO3/pyo3/pull/3446)
- Truncate leap-seconds and warn when converting `chrono` types to Python `datetime` types (`datetime` cannot represent leap-seconds). [#3458](https://github.com/PyO3/pyo3/pull/3458)
- `Err` returned from `#[pyfunction]` will now have a non-None `__context__` if called from inside a `catch` block. [#3455](https://github.com/PyO3/pyo3/pull/3455)
- Deprecate undocumented `#[__new__]` form of `#[new]` attribute. [#3505](https://github.com/PyO3/pyo3/pull/3505)
### Removed
- Remove all functionality deprecated in PyO3 0.18, including `#[args]` attribute for `#[pymethods]`. [#3232](https://github.com/PyO3/pyo3/pull/3232)
- Remove `IntoPyPointer` trait in favour of `into_ptr` inherent methods. [#3385](https://github.com/PyO3/pyo3/pull/3385)
### Fixed
- Handle exceptions properly in `PySet::discard`. [#3281](https://github.com/PyO3/pyo3/pull/3281)
- The `PyTupleIterator` type returned by `PyTuple::iter` is now public and hence can be named by downstream crates. [#3366](https://github.com/PyO3/pyo3/pull/3366)
- Linking of `PyOS_FSPath` on PyPy. [#3374](https://github.com/PyO3/pyo3/pull/3374)
- Fix memory leak in `PyTypeBuilder::build`. [#3401](https://github.com/PyO3/pyo3/pull/3401)
- Disable removed FFI definitions `_Py_GetAllocatedBlocks`, `_PyObject_GC_Malloc`, and `_PyObject_GC_Calloc` on Python 3.11 and up. [#3403](https://github.com/PyO3/pyo3/pull/3403)
- Fix `ResourceWarning` and crashes related to GC when running with debug builds of CPython. [#3404](https://github.com/PyO3/pyo3/pull/3404)
- Some-wrapping of `Option` default arguments will no longer re-wrap `Some(T)` or expressions evaluating to `None`. [#3461](https://github.com/PyO3/pyo3/pull/3461)
- Fix `IterNextOutput::Return` not returning a value on PyPy. [#3471](https://github.com/PyO3/pyo3/pull/3471)
- Emit compile errors instead of ignoring macro invocations inside `#[pymethods]` blocks. [#3491](https://github.com/PyO3/pyo3/pull/3491)
- Emit error on invalid arguments to `#[new]`, `#[classmethod]`, `#[staticmethod]`, and `#[classattr]`. [#3484](https://github.com/PyO3/pyo3/pull/3484)
- Disable `PyMarshal_WriteObjectToString` from `PyMarshal_ReadObjectFromString` with the `abi3` feature. [#3490](https://github.com/PyO3/pyo3/pull/3490)
- Fix FFI definitions for `_PyFrameEvalFunction` on Python 3.11 and up (it now receives a `_PyInterpreterFrame` opaque struct). [#3500](https://github.com/PyO3/pyo3/pull/3500)
## [0.19.2] - 2023-08-01
### Added
- Add FFI definitions `PyState_AddModule`, `PyState_RemoveModule` and `PyState_FindModule` for PyPy 3.9 and up. [#3295](https://github.com/PyO3/pyo3/pull/3295)
- Add FFI definitions `_PyObject_CallFunction_SizeT` and `_PyObject_CallMethod_SizeT`. [#3297](https://github.com/PyO3/pyo3/pull/3297)
- Add a "performance" section to the guide collecting performance-related tricks and problems. [#3304](https://github.com/PyO3/pyo3/pull/3304)
- Add `PyErr::Display` for all Python versions, and FFI symbol `PyErr_DisplayException` for Python 3.12. [#3334](https://github.com/PyO3/pyo3/pull/3334)
- Add FFI definition `PyType_GetDict()` for Python 3.12. [#3339](https://github.com/PyO3/pyo3/pull/3339)
- Add `PyAny::downcast_exact`. [#3346](https://github.com/PyO3/pyo3/pull/3346)
- Add `PySlice::full()` to construct a full slice (`::`). [#3353](https://github.com/PyO3/pyo3/pull/3353)
### Changed
- Update `PyErr` for 3.12 betas to avoid deprecated ffi methods. [#3306](https://github.com/PyO3/pyo3/pull/3306)
- Update FFI definitions of `object.h` for Python 3.12.0b4. [#3335](https://github.com/PyO3/pyo3/pull/3335)
- Update `pyo3::ffi` struct definitions to be compatible with 3.12.0b4. [#3342](https://github.com/PyO3/pyo3/pull/3342)
- Optimize conversion of `float` to `f64` (and `PyFloat::value`) on non-abi3 builds. [#3345](https://github.com/PyO3/pyo3/pull/3345)
### Fixed
- Fix timezone conversion bug for FixedOffset datetimes that were being incorrectly converted to and from UTC. [#3269](https://github.com/PyO3/pyo3/pull/3269)
- Fix `SystemError` raised in `PyUnicodeDecodeError_Create` on PyPy 3.10. [#3297](https://github.com/PyO3/pyo3/pull/3297)
- Correct FFI definition `Py_EnterRecursiveCall` to return `c_int` (was incorrectly returning `()`). [#3300](https://github.com/PyO3/pyo3/pull/3300)
- Fix case where `PyErr::matches` and `PyErr::is_instance` returned results inconsistent with `PyErr::get_type`. [#3313](https://github.com/PyO3/pyo3/pull/3313)
- Fix loss of panic message in `PanicException` when unwinding after the exception was "normalized". [#3326](https://github.com/PyO3/pyo3/pull/3326)
- Fix `PyErr::from_value` and `PyErr::into_value` losing traceback on conversion. [#3328](https://github.com/PyO3/pyo3/pull/3328)
- Fix reference counting of immortal objects on Python 3.12.0b4. [#3335](https://github.com/PyO3/pyo3/pull/3335)
## [0.19.1] - 2023-07-03
### Packaging
- Extend range of supported versions of `hashbrown` optional dependency to include version 0.14 [#3258](https://github.com/PyO3/pyo3/pull/3258)
- Extend range of supported versions of `indexmap` optional dependency to include version 2. [#3277](https://github.com/PyO3/pyo3/pull/3277)
- Support PyPy 3.10. [#3289](https://github.com/PyO3/pyo3/pull/3289)
### Added
- Add `pyo3::types::PyFrozenSetBuilder` to allow building a `PyFrozenSet` item by item. [#3156](https://github.com/PyO3/pyo3/pull/3156)
- Add support for converting to and from Python's `ipaddress.IPv4Address`/`ipaddress.IPv6Address` and `std::net::IpAddr`. [#3197](https://github.com/PyO3/pyo3/pull/3197)
- Add support for `num-bigint` feature in combination with `abi3`. [#3198](https://github.com/PyO3/pyo3/pull/3198)
- Add `PyErr_GetRaisedException()`, `PyErr_SetRaisedException()` to FFI definitions for Python 3.12 and later. [#3248](https://github.com/PyO3/pyo3/pull/3248)
- Add `Python::with_pool` which is a safer but more limited alternative to `Python::new_pool`. [#3263](https://github.com/PyO3/pyo3/pull/3263)
- Add `PyDict::get_item_with_error` on PyPy. [#3270](https://github.com/PyO3/pyo3/pull/3270)
- Allow `#[new]` methods may to return `Py` in order to return existing instances. [#3287](https://github.com/PyO3/pyo3/pull/3287)
### Fixed
- Fix conversion of classes implementing `__complex__` to `Complex` when using `abi3` or PyPy. [#3185](https://github.com/PyO3/pyo3/pull/3185)
- Stop suppressing unrelated exceptions in `PyAny::hasattr`. [#3271](https://github.com/PyO3/pyo3/pull/3271)
- Fix memory leak when creating `PySet` or `PyFrozenSet` or returning types converted into these internally, e.g. `HashSet` or `BTreeSet`. [#3286](https://github.com/PyO3/pyo3/pull/3286)
## [0.19.0] - 2023-05-31
### Packaging
- Correct dependency on syn to version 1.0.85 instead of the incorrect version 1.0.56. [#3152](https://github.com/PyO3/pyo3/pull/3152)
### Added
- Accept `text_signature` option (and automatically generate signature) for `#[new]` in `#[pymethods]`. [#2980](https://github.com/PyO3/pyo3/pull/2980)
- Add support for converting to and from Python's `decimal.Decimal` and `rust_decimal::Decimal`. [#3016](https://github.com/PyO3/pyo3/pull/3016)
- Add `#[pyo3(from_item_all)]` when deriving `FromPyObject` to specify `get_item` as getter for all fields. [#3120](https://github.com/PyO3/pyo3/pull/3120)
- Add `pyo3::exceptions::PyBaseExceptionGroup` for Python 3.11, and corresponding FFI definition `PyExc_BaseExceptionGroup`. [#3141](https://github.com/PyO3/pyo3/pull/3141)
- Accept `#[new]` with `#[classmethod]` to create a constructor which receives a (subtype's) class/`PyType` as its first argument. [#3157](https://github.com/PyO3/pyo3/pull/3157)
- Add `PyClass::get` and `Py::get` for GIL-independent access to classes with `#[pyclass(frozen)]`. [#3158](https://github.com/PyO3/pyo3/pull/3158)
- Add `PyAny::is_exact_instance` and `PyAny::is_exact_instance_of`. [#3161](https://github.com/PyO3/pyo3/pull/3161)
### Changed
- `PyAny::is_instance_of::(obj)` is now equivalent to `T::is_type_of(obj)`, and now returns `bool` instead of `PyResult`. [#2881](https://github.com/PyO3/pyo3/pull/2881)
- Deprecate `text_signature` option on `#[pyclass]` structs. [#2980](https://github.com/PyO3/pyo3/pull/2980)
- No longer wrap `anyhow::Error`/`eyre::Report` containing a basic `PyErr` without a chain in a `PyRuntimeError`. [#3004](https://github.com/PyO3/pyo3/pull/3004)
- - Change `#[getter]` and `#[setter]` to use a common call "trampoline" to slightly reduce generated code size and compile times. [#3029](https://github.com/PyO3/pyo3/pull/3029)
- Improve default values for str, numbers and bool in automatically-generated `text_signature`. [#3050](https://github.com/PyO3/pyo3/pull/3050)
- Improve default value for `None` in automatically-generated `text_signature`. [#3066](https://github.com/PyO3/pyo3/pull/3066)
- Rename `PySequence::list` and `PySequence::tuple` to `PySequence::to_list` and `PySequence::to_tuple`. (The old names continue to exist as deprecated forms.) [#3111](https://github.com/PyO3/pyo3/pull/3111)
- Extend the lifetime of the GIL token returned by `PyRef::py` and `PyRefMut::py` to match the underlying borrow. [#3131](https://github.com/PyO3/pyo3/pull/3131)
- Safe access to the GIL, for example via `Python::with_gil`, is now locked inside of implementations of the `__traverse__` slot. [#3168](https://github.com/PyO3/pyo3/pull/3168)
### Removed
- Remove all functionality deprecated in PyO3 0.17, most prominently `Python::acquire_gil` is replaced by `Python::with_gil`. [#2981](https://github.com/PyO3/pyo3/pull/2981)
### Fixed
- Correct FFI definitions `PyGetSetDef`, `PyMemberDef`, `PyStructSequence_Field` and `PyStructSequence_Desc` to have `*const c_char` members for `name` and `doc` (not `*mut c_char`). [#3036](https://github.com/PyO3/pyo3/pull/3036)
- Fix panic on `fmt::Display`, instead return `""` string and report error via `sys.unraisablehook()` [#3062](https://github.com/PyO3/pyo3/pull/3062)
- Fix a compile error of "temporary value dropped while borrowed" when `#[pyfunction]`s take references into `#[pyclass]`es [#3142](https://github.com/PyO3/pyo3/pull/3142)
- Fix crashes caused by PyO3 applying deferred reference count updates when entering a `__traverse__` implementation. [#3168](https://github.com/PyO3/pyo3/pull/3168)
- Forbid running the `Drop` implementations of unsendable classes on other threads. [#3176](https://github.com/PyO3/pyo3/pull/3176)
- Fix a compile error when `#[pymethods]` items come from somewhere else (for example, as a macro argument) and a custom receiver like `Py` is used. [#3178](https://github.com/PyO3/pyo3/pull/3178)
## [0.18.3] - 2023-04-13
### Added
- Add `GILProtected` to mediate concurrent access to a value using Python's global interpreter lock (GIL). [#2975](https://github.com/PyO3/pyo3/pull/2975)
- Support `PyASCIIObject` / `PyUnicode` and associated methods on big-endian architectures. [#3015](https://github.com/PyO3/pyo3/pull/3015)
- Add FFI definition `_PyDict_Contains_KnownHash()` for CPython 3.10 and up. [#3088](https://github.com/PyO3/pyo3/pull/3088)
### Fixed
- Fix compile error for `#[pymethods]` and `#[pyfunction]` called "output". [#3022](https://github.com/PyO3/pyo3/pull/3022)
- Fix compile error in generated code for magic methods implemented as a `#[staticmethod]`. [#3055](https://github.com/PyO3/pyo3/pull/3055)
- Fix `is_instance` for `PyDateTime` (would incorrectly check for a `PyDate`). [#3071](https://github.com/PyO3/pyo3/pull/3071)
- Fix upstream deprecation of `PyUnicode_InternImmortal` since Python 3.10. [#3071](https://github.com/PyO3/pyo3/pull/3087)
## [0.18.2] - 2023-03-24
### Packaging
- Disable default features of `chrono` to avoid depending on `time` v0.1.x. [#2939](https://github.com/PyO3/pyo3/pull/2939)
### Added
- Implement `IntoPy`, `ToPyObject` and `FromPyObject` for `Cow<[u8]>` to efficiently handle both `bytes` and `bytearray` objects. [#2899](https://github.com/PyO3/pyo3/pull/2899)
- Implement `IntoPy`, `ToPyObject` and `FromPyObject` for `Cell`. [#3014](https://github.com/PyO3/pyo3/pull/3014)
- Add `PyList::to_tuple()`, as a convenient and efficient conversion from lists to tuples. [#3042](https://github.com/PyO3/pyo3/pull/3042)
- Add `PyTuple::to_list()`, as a convenient and efficient conversion from tuples to lists. [#3044](https://github.com/PyO3/pyo3/pull/3044)
### Changed
- Optimize `PySequence` conversion for `list` and `tuple` inputs. [#2944](https://github.com/PyO3/pyo3/pull/2944)
- Improve exception raised when creating `#[pyclass]` type object fails during module import. [#2947](https://github.com/PyO3/pyo3/pull/2947)
- Optimize `PyMapping` conversion for `dict` inputs. [#2954](https://github.com/PyO3/pyo3/pull/2954)
- Allow `create_exception!` to take a `dotted.module` to place the exception in a submodule. [#2979](https://github.com/PyO3/pyo3/pull/2979)
### Fixed
- Fix a reference counting race condition affecting `PyObject`s cloned in `allow_threads` blocks. [#2952](https://github.com/PyO3/pyo3/pull/2952)
- Fix `clippy::redundant_closure` lint on default arguments in `#[pyo3(signature = (...))]` annotations. [#2990](https://github.com/PyO3/pyo3/pull/2990)
- Fix `non_snake_case` lint on generated code in `#[pyfunction]` macro. [#2993](https://github.com/PyO3/pyo3/pull/2993)
- Fix some FFI definitions for the upcoming PyPy 3.10 release. [#3031](https://github.com/PyO3/pyo3/pull/3031)
## [0.18.1] - 2023-02-07
### Added
- Add `PyErr::write_unraisable()`. [#2889](https://github.com/PyO3/pyo3/pull/2889)
- Add `Python::Ellipsis()` and `PyAny::is_ellipsis()` methods. [#2911](https://github.com/PyO3/pyo3/pull/2911)
- Add `PyDict::update()` and `PyDict::update_if_missing()` methods. [#2912](https://github.com/PyO3/pyo3/pull/2912)
### Changed
- FFI definition `PyIter_Check` on CPython 3.7 is now implemented as `hasattr(type(obj), "__next__")`, which works correctly on all platforms and adds support for `abi3`. [#2914](https://github.com/PyO3/pyo3/pull/2914)
- Warn about unknown config keys in `PYO3_CONFIG_FILE` instead of denying. [#2926](https://github.com/PyO3/pyo3/pull/2926)
### Fixed
- Send errors returned by `__releasebuffer__` to `sys.unraisablehook` rather than causing `SystemError`. [#2886](https://github.com/PyO3/pyo3/pull/2886)
- Fix downcast to `PyIterator` succeeding for Python classes which did not implement `__next__`. [#2914](https://github.com/PyO3/pyo3/pull/2914)
- Fix segfault in `__traverse__` when visiting `None` fields of `Option`. [#2921](https://github.com/PyO3/pyo3/pull/2921)
- Fix `#[pymethods(crate = "...")]` option being ignored. [#2923](https://github.com/PyO3/pyo3/pull/2923)
- Link against `pythonXY_d.dll` for debug Python builds on Windows. [#2937](https://github.com/PyO3/pyo3/pull/2937)
## [0.18.0] - 2023-01-17
### Packaging
- Relax `indexmap` optional depecency to allow `>= 1.6, < 2`. [#2849](https://github.com/PyO3/pyo3/pull/2849)
- Relax `hashbrown` optional dependency to allow `>= 0.9, < 0.14`. [#2875](https://github.com/PyO3/pyo3/pull/2875)
- Update `memoffset` dependency to 0.8. [#2875](https://github.com/PyO3/pyo3/pull/2875)
### Added
- Add `GILOnceCell::get_or_try_init` for fallible `GILOnceCell` initialization. [#2398](https://github.com/PyO3/pyo3/pull/2398)
- Add experimental feature `experimental-inspect` with `type_input()` and `type_output()` helpers to get the Python type of any Python-compatible object. [#2490](https://github.com/PyO3/pyo3/pull/2490) [#2882](https://github.com/PyO3/pyo3/pull/2882)
- The `#[pyclass]` macro can now take `get_all` and `set_all` to create getters and setters for every field. [#2692](https://github.com/PyO3/pyo3/pull/2692)
- Add `#[pyo3(signature = (...))]` option for `#[pyfunction]` and `#[pymethods]`. [#2702](https://github.com/PyO3/pyo3/pull/2702)
- `pyo3-build-config`: rebuild when `PYO3_ENVIRONMENT_SIGNATURE` environment variable value changes. [#2727](https://github.com/PyO3/pyo3/pull/2727)
- Add conversions between non-zero int types in `std::num` and Python `int`. [#2730](https://github.com/PyO3/pyo3/pull/2730)
- Add `Py::downcast()` as a companion to `PyAny::downcast()`, as well as `downcast_unchecked()` for both types. [#2734](https://github.com/PyO3/pyo3/pull/2734)
- Add types for all built-in `Warning` classes as well as `PyErr::warn_explicit`. [#2742](https://github.com/PyO3/pyo3/pull/2742)
- Add `abi3-py311` feature. [#2776](https://github.com/PyO3/pyo3/pull/2776)
- Add FFI definition `_PyErr_ChainExceptions()` for CPython. [#2788](https://github.com/PyO3/pyo3/pull/2788)
- Add FFI definitions `PyVectorcall_NARGS` and `PY_VECTORCALL_ARGUMENTS_OFFSET` for PyPy 3.8 and up. [#2811](https://github.com/PyO3/pyo3/pull/2811)
- Add `PyList::get_item_unchecked` for PyPy. [#2827](https://github.com/PyO3/pyo3/pull/2827)
### Changed
- PyO3's macros now emit a much nicer error message if function return values don't implement the required trait(s). [#2664](https://github.com/PyO3/pyo3/pull/2664)
- Use a TypeError, rather than a ValueError, when refusing to treat a str as a Vec. [#2685](https://github.com/PyO3/pyo3/pull/2685)
- Change `PyCFunction::new_closure` to take `name` and `doc` arguments. [#2686](https://github.com/PyO3/pyo3/pull/2686)
- `PyType::is_subclass`, `PyErr::is_instance` and `PyAny::is_instance` now take `&PyAny` instead of `&PyType` arguments, so that they work with objects that pretend to be types using `__subclasscheck__` and `__instancecheck__`. [#2695](https://github.com/PyO3/pyo3/pull/2695)
- Deprecate `#[args]` attribute and passing "args" specification directly to `#[pyfunction]` in favor of the new `#[pyo3(signature = (...))]` option. [#2702](https://github.com/PyO3/pyo3/pull/2702)
- Deprecate required arguments after `Option` arguments to `#[pyfunction]` and `#[pymethods]` without also using `#[pyo3(signature)]` to specify whether the arguments should be required or have defaults. [#2703](https://github.com/PyO3/pyo3/pull/2703)
- Change `#[pyfunction]` and `#[pymethods]` to use a common call "trampoline" to slightly reduce generated code size and compile times. [#2705](https://github.com/PyO3/pyo3/pull/2705)
- `PyAny::cast_as()` and `Py::cast_as()` are now deprecated in favor of `PyAny::downcast()` and the new `Py::downcast()`. [#2734](https://github.com/PyO3/pyo3/pull/2734)
- Relax lifetime bounds on `PyAny::downcast()`. [#2734](https://github.com/PyO3/pyo3/pull/2734)
- Automatically generate `__text_signature__` for all Python functions created using `#[pyfunction]` and `#[pymethods]`. [#2784](https://github.com/PyO3/pyo3/pull/2784)
- Accept any iterator in `PySet::new` and `PyFrozenSet::new`. [#2795](https://github.com/PyO3/pyo3/pull/2795)
- Mixing `#[cfg(...)]` and `#[pyo3(...)]` attributes on `#[pyclass]` struct fields will now work. [#2796](https://github.com/PyO3/pyo3/pull/2796)
- Re-enable `PyFunction` on when building for abi3 or PyPy. [#2838](https://github.com/PyO3/pyo3/pull/2838)
- Improve `derive(FromPyObject)` to use `intern!` when applicable for `#[pyo3(item)]`. [#2879](https://github.com/PyO3/pyo3/pull/2879)
### Removed
- Remove the deprecated `pyproto` feature, `#[pyproto]` macro, and all accompanying APIs. [#2587](https://github.com/PyO3/pyo3/pull/2587)
- Remove all functionality deprecated in PyO3 0.16. [#2843](https://github.com/PyO3/pyo3/pull/2843)
### Fixed
- Disable `PyModule::filename` on PyPy. [#2715](https://github.com/PyO3/pyo3/pull/2715)
- `PyCodeObject` is now once again defined with fields on Python 3.7. [#2726](https://github.com/PyO3/pyo3/pull/2726)
- Raise a `TypeError` if `#[new]` pymethods with no arguments receive arguments when called from Python. [#2749](https://github.com/PyO3/pyo3/pull/2749)
- Use the `NOARGS` argument calling convention for methods that have a single `py: Python` argument (as a performance optimization). [#2760](https://github.com/PyO3/pyo3/pull/2760)
- Fix truncation of `isize` values to `c_long` in `PySlice::new`. [#2769](https://github.com/PyO3/pyo3/pull/2769)
- Fix soundness issue with FFI definition `PyUnicodeDecodeError_Create` on PyPy leading to indeterminate behavior (typically a `TypeError`). [#2772](https://github.com/PyO3/pyo3/pull/2772)
- Allow functions taking `**kwargs` to accept keyword arguments which share a name with a positional-only argument (as permitted by PEP 570). [#2800](https://github.com/PyO3/pyo3/pull/2800)
- Fix unresolved symbol for `PyObject_Vectorcall` on PyPy 3.9 and up. [#2811](https://github.com/PyO3/pyo3/pull/2811)
- Fix memory leak in `PyCFunction::new_closure`. [#2842](https://github.com/PyO3/pyo3/pull/2842)
## [0.17.3] - 2022-11-01
### Packaging
- Support Python 3.11. (Previous versions of PyO3 0.17 have been tested against Python 3.11 release candidates and are expected to be compatible, this is the first version tested against Python 3.11.0.) [#2708](https://github.com/PyO3/pyo3/pull/2708)
### Added
- Implemented `ExactSizeIterator` for `PyListIterator`, `PyDictIterator`, `PySetIterator` and `PyFrozenSetIterator`. [#2676](https://github.com/PyO3/pyo3/pull/2676)
### Fixed
- Fix regression of `impl FromPyObject for [T; N]` no longer accepting types passing `PySequence_Check`, e.g. NumPy arrays, since version 0.17.0. This the same fix that was applied `impl FromPyObject for Vec` in version 0.17.1 extended to fixed-size arrays. [#2675](https://github.com/PyO3/pyo3/pull/2675)
- Fix UB in `FunctionDescription::extract_arguments_fastcall` due to creating slices from a null pointer. [#2687](https://github.com/PyO3/pyo3/pull/2687)
## [0.17.2] - 2022-10-04
### Packaging
- Added optional `chrono` feature to convert `chrono` types into types in the `datetime` module. [#2612](https://github.com/PyO3/pyo3/pull/2612)
### Added
- Add support for `num-bigint` feature on `PyPy`. [#2626](https://github.com/PyO3/pyo3/pull/2626)
### Fixed
- Correctly implement `__richcmp__` for enums, fixing `__ne__` returning always returning `True`. [#2622](https://github.com/PyO3/pyo3/pull/2622)
- Fix compile error since 0.17.0 with `Option<&SomePyClass>` argument with a default. [#2630](https://github.com/PyO3/pyo3/pull/2630)
- Fix regression of `impl FromPyObject for Vec` no longer accepting types passing `PySequence_Check`, e.g. NumPy arrays, since 0.17.0. [#2631](https://github.com/PyO3/pyo3/pull/2631)
## [0.17.1] - 2022-08-28
### Fixed
- Fix visibility of `PyDictItems`, `PyDictKeys`, and `PyDictValues` types added in PyO3 0.17.0.
- Fix compile failure when using `#[pyo3(from_py_with = "...")]` attribute on an argument of type `Option`. [#2592](https://github.com/PyO3/pyo3/pull/2592)
- Fix clippy `redundant-closure` lint on `**kwargs` arguments for `#[pyfunction]` and `#[pymethods]`. [#2595](https://github.com/PyO3/pyo3/pull/2595)
## [0.17.0] - 2022-08-23
### Packaging
- Update inventory dependency to `0.3` (the `multiple-pymethods` feature now requires Rust 1.62 for correctness). [#2492](https://github.com/PyO3/pyo3/pull/2492)
### Added
- Add `timezone_utc`. [#1588](https://github.com/PyO3/pyo3/pull/1588)
- Implement `ToPyObject` for `[T; N]`. [#2313](https://github.com/PyO3/pyo3/pull/2313)
- Add `PyDictKeys`, `PyDictValues` and `PyDictItems` Rust types. [#2358](https://github.com/PyO3/pyo3/pull/2358)
- Add `append_to_inittab`. [#2377](https://github.com/PyO3/pyo3/pull/2377)
- Add FFI definition `PyFrame_GetCode`. [#2406](https://github.com/PyO3/pyo3/pull/2406)
- Add `PyCode` and `PyFrame` high level objects. [#2408](https://github.com/PyO3/pyo3/pull/2408)
- Add FFI definitions `Py_fstring_input`, `sendfunc`, and `_PyErr_StackItem`. [#2423](https://github.com/PyO3/pyo3/pull/2423)
- Add `PyDateTime::new_with_fold`, `PyTime::new_with_fold`, `PyTime::get_fold`, and `PyDateTime::get_fold` for PyPy. [#2428](https://github.com/PyO3/pyo3/pull/2428)
- Add `#[pyclass(frozen)]`. [#2448](https://github.com/PyO3/pyo3/pull/2448)
- Accept `#[pyo3(name)]` on enum variants. [#2457](https://github.com/PyO3/pyo3/pull/2457)
- Add `CompareOp::matches` to implement `__richcmp__` as the result of a Rust `std::cmp::Ordering` comparison. [#2460](https://github.com/PyO3/pyo3/pull/2460)
- Add `PySuper` type. [#2486](https://github.com/PyO3/pyo3/pull/2486)
- Support PyPy on Windows with the `generate-import-lib` feature. [#2506](https://github.com/PyO3/pyo3/pull/2506)
- Add FFI definitions `Py_EnterRecursiveCall` and `Py_LeaveRecursiveCall`. [#2511](https://github.com/PyO3/pyo3/pull/2511)
- Add `PyDict::get_item_with_error`. [#2536](https://github.com/PyO3/pyo3/pull/2536)
- Add `#[pyclass(sequence)]` option. [#2567](https://github.com/PyO3/pyo3/pull/2567)
### Changed
- Change datetime constructors taking a `tzinfo` to take `Option<&PyTzInfo>` instead of `Option<&PyObject>`: `PyDateTime::new`, `PyDateTime::new_with_fold`, `PyTime::new`, and `PyTime::new_with_fold`. [#1588](https://github.com/PyO3/pyo3/pull/1588)
- Move `PyTypeObject::type_object` method to the `PyTypeInfo` trait, and deprecate the `PyTypeObject` trait. [#2287](https://github.com/PyO3/pyo3/pull/2287)
- Methods of `Py` and `PyAny` now accept `impl IntoPy>` rather than just `&str` to allow use of the `intern!` macro. [#2312](https://github.com/PyO3/pyo3/pull/2312)
- Change the deprecated `pyproto` feature to be opt-in instead of opt-out. [#2322](https://github.com/PyO3/pyo3/pull/2322)
- Emit better error messages when `#[pyfunction]` return types do not implement `IntoPy`. [#2326](https://github.com/PyO3/pyo3/pull/2326)
- Require `T: IntoPy` for `impl IntoPy for [T; N]` instead of `T: ToPyObject`. [#2326](https://github.com/PyO3/pyo3/pull/2326)
- Deprecate the `ToBorrowedObject` trait. [#2333](https://github.com/PyO3/pyo3/pull/2333)
- Iterators over `PySet` and `PyDict` will now panic if the underlying collection is mutated during the iteration. [#2380](https://github.com/PyO3/pyo3/pull/2380)
- Iterators over `PySet` and `PyDict` will now panic if the underlying collection is mutated during the iteration. [#2380](https://github.com/PyO3/pyo3/pull/2380)
- Allow `#[classattr]` methods to be fallible. [#2385](https://github.com/PyO3/pyo3/pull/2385)
- Prevent multiple `#[pymethods]` with the same name for a single `#[pyclass]`. [#2399](https://github.com/PyO3/pyo3/pull/2399)
- Fixup `lib_name` when using `PYO3_CONFIG_FILE`. [#2404](https://github.com/PyO3/pyo3/pull/2404)
- Add a message to the `ValueError` raised by the `#[derive(FromPyObject)]` implementation for a tuple struct. [#2414](https://github.com/PyO3/pyo3/pull/2414)
- Allow `#[classattr]` methods to take `Python` argument. [#2456](https://github.com/PyO3/pyo3/pull/2456)
- Rework `PyCapsule` type to resolve soundness issues: [#2485](https://github.com/PyO3/pyo3/pull/2485)
- `PyCapsule::new` and `PyCapsule::new_with_destructor` now take `name: Option` instead of `&CStr`.
- The destructor `F` in `PyCapsule::new_with_destructor` must now be `Send`.
- `PyCapsule::get_context` deprecated in favor of `PyCapsule::context` which doesn't take a `py: Python<'_>` argument.
- `PyCapsule::set_context` no longer takes a `py: Python<'_>` argument.
- `PyCapsule::name` now returns `PyResult>` instead of `&CStr`.
- `FromPyObject::extract` for `Vec` no longer accepts Python `str` inputs. [#2500](https://github.com/PyO3/pyo3/pull/2500)
- Ensure each `#[pymodule]` is only initialized once. [#2523](https://github.com/PyO3/pyo3/pull/2523)
- `pyo3_build_config::add_extension_module_link_args` now also emits linker arguments for `wasm32-unknown-emscripten`. [#2538](https://github.com/PyO3/pyo3/pull/2538)
- Type checks for `PySequence` and `PyMapping` now require inputs to inherit from (or register with) `collections.abc.Sequence` and `collections.abc.Mapping` respectively. [#2477](https://github.com/PyO3/pyo3/pull/2477)
- Disable `PyFunction` on when building for abi3 or PyPy. [#2542](https://github.com/PyO3/pyo3/pull/2542)
- Deprecate `Python::acquire_gil`. [#2549](https://github.com/PyO3/pyo3/pull/2549)
### Removed
- Remove all functionality deprecated in PyO3 0.15. [#2283](https://github.com/PyO3/pyo3/pull/2283)
- Make the `Dict`, `WeakRef` and `BaseNativeType` members of the `PyClass` private implementation details. [#2572](https://github.com/PyO3/pyo3/pull/2572)
### Fixed
- Enable incorrectly disabled FFI definition `PyThreadState_DeleteCurrent`. [#2357](https://github.com/PyO3/pyo3/pull/2357)
- Fix `wrap_pymodule` interactions with name resolution rules: it no longer "sees through" glob imports of `use submodule::*` when `submodule::submodule` is a `#[pymodule]`. [#2363](https://github.com/PyO3/pyo3/pull/2363)
- Correct FFI definition `PyEval_EvalCodeEx` to take `*const *mut PyObject` array arguments instead of `*mut *mut PyObject`. [#2368](https://github.com/PyO3/pyo3/pull/2368)
- Fix "raw-ident" structs (e.g. `#[pyclass] struct r#RawName`) incorrectly having `r#` at the start of the class name created in Python. [#2395](https://github.com/PyO3/pyo3/pull/2395)
- Correct FFI definition `Py_tracefunc` to be `unsafe extern "C" fn` (was previously safe). [#2407](https://github.com/PyO3/pyo3/pull/2407)
- Fix compile failure with `#[pyo3(from_py_with = "...")]` annotations on a field in a `#[derive(FromPyObject)]` struct. [#2414](https://github.com/PyO3/pyo3/pull/2414)
- Fix FFI definitions `_PyDateTime_BaseTime` and `_PyDateTime_BaseDateTime` lacking leading underscores in their names. [#2421](https://github.com/PyO3/pyo3/pull/2421)
- Remove FFI definition `PyArena` on Python 3.10 and up. [#2421](https://github.com/PyO3/pyo3/pull/2421)
- Fix FFI definition `PyCompilerFlags` missing member `cf_feature_version` on Python 3.8 and up. [#2423](https://github.com/PyO3/pyo3/pull/2423)
- Fix FFI definition `PyAsyncMethods` missing member `am_send` on Python 3.10 and up. [#2423](https://github.com/PyO3/pyo3/pull/2423)
- Fix FFI definition `PyGenObject` having multiple incorrect members on various Python versions. [#2423](https://github.com/PyO3/pyo3/pull/2423)
- Fix FFI definition `PySyntaxErrorObject` missing members `end_lineno` and `end_offset` on Python 3.10 and up. [#2423](https://github.com/PyO3/pyo3/pull/2423)
- Fix FFI definition `PyHeapTypeObject` missing member `ht_module` on Python 3.9 and up. [#2423](https://github.com/PyO3/pyo3/pull/2423)
- Fix FFI definition `PyFrameObject` having multiple incorrect members on various Python versions. [#2424](https://github.com/PyO3/pyo3/pull/2424) [#2434](https://github.com/PyO3/pyo3/pull/2434)
- Fix FFI definition `PyTypeObject` missing deprecated field `tp_print` on Python 3.8. [#2428](https://github.com/PyO3/pyo3/pull/2428)
- Fix FFI definitions `PyDateTime_CAPI`. `PyDateTime_Date`, `PyASCIIObject`, `PyBaseExceptionObject`, `PyListObject`, and `PyTypeObject` on PyPy. [#2428](https://github.com/PyO3/pyo3/pull/2428)
- Fix FFI definition `_inittab` field `initfunc` typo'd as `initfun`. [#2431](https://github.com/PyO3/pyo3/pull/2431)
- Fix FFI definitions `_PyDateTime_BaseTime` and `_PyDateTime_BaseDateTime` incorrectly having `fold` member. [#2432](https://github.com/PyO3/pyo3/pull/2432)
- Fix FFI definitions `PyTypeObject`. `PyHeapTypeObject`, and `PyCFunctionObject` having incorrect members on PyPy 3.9. [#2433](https://github.com/PyO3/pyo3/pull/2433)
- Fix FFI definition `PyGetSetDef` to have `*const c_char` for `doc` member (not `*mut c_char`). [#2439](https://github.com/PyO3/pyo3/pull/2439)
- Fix `#[pyo3(from_py_with = "...")]` being ignored for 1-element tuple structs and transparent structs. [#2440](https://github.com/PyO3/pyo3/pull/2440)
- Use `memoffset` to avoid UB when computing `PyCell` layout. [#2450](https://github.com/PyO3/pyo3/pull/2450)
- Fix incorrect enum names being returned by the generated `repr` for enums renamed by `#[pyclass(name = "...")]` [#2457](https://github.com/PyO3/pyo3/pull/2457)
- Fix `PyObject_CallNoArgs` incorrectly being available when building for abi3 on Python 3.9. [#2476](https://github.com/PyO3/pyo3/pull/2476)
- Fix several clippy warnings generated by `#[pyfunction]` arguments. [#2503](https://github.com/PyO3/pyo3/pull/2503)
## [0.16.6] - 2022-08-23
### Changed
- Fix soundness issues with `PyCapsule` type with select workarounds. Users are encourage to upgrade to PyO3 0.17 at their earliest convenience which contains API breakages which fix the issues in a long-term fashion. [#2522](https://github.com/PyO3/pyo3/pull/2522)
- `PyCapsule::new` and `PyCapsule::new_with_destructor` now take ownership of a copy of the `name` to resolve a possible use-after-free.
- `PyCapsule::name` now returns an empty `CStr` instead of dereferencing a null pointer if the capsule has no name.
- The destructor `F` in `PyCapsule::new_with_destructor` will never be called if the capsule is deleted from a thread other than the one which the capsule was created in (a warning will be emitted).
- Panics during drop of panic payload caught by PyO3 will now abort. [#2544](https://github.com/PyO3/pyo3/pull/2544)
## [0.16.5] - 2022-05-15
### Added
- Add an experimental `generate-import-lib` feature to support auto-generating non-abi3 python import libraries for Windows targets. [#2364](https://github.com/PyO3/pyo3/pull/2364)
- Add FFI definition `Py_ExitStatusException`. [#2374](https://github.com/PyO3/pyo3/pull/2374)
### Changed
- Deprecate experimental `generate-abi3-import-lib` feature in favor of the new `generate-import-lib` feature. [#2364](https://github.com/PyO3/pyo3/pull/2364)
### Fixed
- Added missing `warn_default_encoding` field to `PyConfig` on 3.10+. The previously missing field could result in incorrect behavior or crashes. [#2370](https://github.com/PyO3/pyo3/pull/2370)
- Fixed order of `pathconfig_warnings` and `program_name` fields of `PyConfig` on 3.10+. Previously, the order of the fields was swapped and this could lead to incorrect behavior or crashes. [#2370](https://github.com/PyO3/pyo3/pull/2370)
## [0.16.4] - 2022-04-14
### Added
- Add `PyTzInfoAccess` trait for safe access to time zone information. [#2263](https://github.com/PyO3/pyo3/pull/2263)
- Add an experimental `generate-abi3-import-lib` feature to auto-generate `python3.dll` import libraries for Windows. [#2282](https://github.com/PyO3/pyo3/pull/2282)
- Add FFI definitions for `PyDateTime_BaseTime` and `PyDateTime_BaseDateTime`. [#2294](https://github.com/PyO3/pyo3/pull/2294)
### Changed
- Improved performance of failing calls to `FromPyObject::extract` which is common when functions accept multiple distinct types. [#2279](https://github.com/PyO3/pyo3/pull/2279)
- Default to "m" ABI tag when choosing `libpython` link name for CPython 3.7 on Unix. [#2288](https://github.com/PyO3/pyo3/pull/2288)
- Allow to compile "abi3" extensions without a working build host Python interpreter. [#2293](https://github.com/PyO3/pyo3/pull/2293)
### Fixed
- Crates depending on PyO3 can collect code coverage via LLVM instrumentation using stable Rust. [#2286](https://github.com/PyO3/pyo3/pull/2286)
- Fix segfault when calling FFI methods `PyDateTime_DATE_GET_TZINFO` or `PyDateTime_TIME_GET_TZINFO` on `datetime` or `time` without a tzinfo. [#2289](https://github.com/PyO3/pyo3/pull/2289)
- Fix directory names starting with the letter `n` breaking serialization of the interpreter configuration on Windows since PyO3 0.16.3. [#2299](https://github.com/PyO3/pyo3/pull/2299)
## [0.16.3] - 2022-04-05
### Packaging
- Extend `parking_lot` dependency supported versions to include 0.12. [#2239](https://github.com/PyO3/pyo3/pull/2239)
### Added
- Add methods to `pyo3_build_config::InterpreterConfig` to run Python scripts using the configured executable. [#2092](https://github.com/PyO3/pyo3/pull/2092)
- Add `as_bytes` method to `Py`. [#2235](https://github.com/PyO3/pyo3/pull/2235)
- Add FFI definitions for `PyType_FromModuleAndSpec`, `PyType_GetModule`, `PyType_GetModuleState` and `PyModule_AddType`. [#2250](https://github.com/PyO3/pyo3/pull/2250)
- Add `pyo3_build_config::cross_compiling_from_to` as a helper to detect when PyO3 is cross-compiling. [#2253](https://github.com/PyO3/pyo3/pull/2253)
- Add `#[pyclass(mapping)]` option to leave sequence slots empty in container implementations. [#2265](https://github.com/PyO3/pyo3/pull/2265)
- Add `PyString::intern` to enable usage of the Python's built-in string interning. [#2268](https://github.com/PyO3/pyo3/pull/2268)
- Add `intern!` macro which can be used to amortize the cost of creating Python strings by storing them inside a `GILOnceCell`. [#2269](https://github.com/PyO3/pyo3/pull/2269)
- Add `PYO3_CROSS_PYTHON_IMPLEMENTATION` environment variable for selecting the default cross Python implementation. [#2272](https://github.com/PyO3/pyo3/pull/2272)
### Changed
- Allow `#[pyo3(crate = "...", text_signature = "...")]` options to be used directly in `#[pyclass(crate = "...", text_signature = "...")]`. [#2234](https://github.com/PyO3/pyo3/pull/2234)
- Make `PYO3_CROSS_LIB_DIR` environment variable optional when cross compiling. [#2241](https://github.com/PyO3/pyo3/pull/2241)
- Mark `METH_FASTCALL` calling convention as limited API on Python 3.10. [#2250](https://github.com/PyO3/pyo3/pull/2250)
- Deprecate `pyo3_build_config::cross_compiling` in favor of `pyo3_build_config::cross_compiling_from_to`. [#2253](https://github.com/PyO3/pyo3/pull/2253)
### Fixed
- Fix `abi3-py310` feature: use Python 3.10 ABI when available instead of silently falling back to the 3.9 ABI. [#2242](https://github.com/PyO3/pyo3/pull/2242)
- Use shared linking mode when cross compiling against a [Framework bundle](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html) for macOS. [#2233](https://github.com/PyO3/pyo3/pull/2233)
- Fix panic during compilation when `PYO3_CROSS_LIB_DIR` is set for some host/target combinations. [#2232](https://github.com/PyO3/pyo3/pull/2232)
- Correct dependency version for `syn` to require minimal patch version 1.0.56. [#2240](https://github.com/PyO3/pyo3/pull/2240)
## [0.16.2] - 2022-03-15
### Packaging
- Warn when modules are imported on PyPy 3.7 versions older than PyPy 7.3.8, as they are known to have binary compatibility issues. [#2217](https://github.com/PyO3/pyo3/pull/2217)
- Ensure build script of `pyo3-ffi` runs before that of `pyo3` to fix cross compilation. [#2224](https://github.com/PyO3/pyo3/pull/2224)
## [0.16.1] - 2022-03-05
### Packaging
- Extend `hashbrown` optional dependency supported versions to include 0.12. [#2197](https://github.com/PyO3/pyo3/pull/2197)
### Fixed
- Fix incorrect platform detection for Windows in `pyo3-build-config`. [#2198](https://github.com/PyO3/pyo3/pull/2198)
- Fix regression from 0.16 preventing cross compiling to aarch64 macOS. [#2201](https://github.com/PyO3/pyo3/pull/2201)
## [0.16.0] - 2022-02-27
### Packaging
- Update MSRV to Rust 1.48. [#2004](https://github.com/PyO3/pyo3/pull/2004)
- Update `indoc` optional dependency to 1.0. [#2004](https://github.com/PyO3/pyo3/pull/2004)
- Drop support for Python 3.6, remove `abi3-py36` feature. [#2006](https://github.com/PyO3/pyo3/pull/2006)
- `pyo3-build-config` no longer enables the `resolve-config` feature by default. [#2008](https://github.com/PyO3/pyo3/pull/2008)
- Update `inventory` optional dependency to 0.2. [#2019](https://github.com/PyO3/pyo3/pull/2019)
- Drop `paste` dependency. [#2081](https://github.com/PyO3/pyo3/pull/2081)
- The bindings found in `pyo3::ffi` are now a re-export of a separate `pyo3-ffi` crate. [#2126](https://github.com/PyO3/pyo3/pull/2126)
- Support PyPy 3.9. [#2143](https://github.com/PyO3/pyo3/pull/2143)
### Added
- Add `PyCapsule` type exposing the [Capsule API](https://docs.python.org/3/c-api/capsule.html#capsules). [#1980](https://github.com/PyO3/pyo3/pull/1980)
- Add `pyo3_build_config::Sysconfigdata` and supporting APIs. [#1996](https://github.com/PyO3/pyo3/pull/1996)
- Add `Py::setattr` method. [#2009](https://github.com/PyO3/pyo3/pull/2009)
- Add `#[pyo3(crate = "some::path")]` option to all attribute macros (except the deprecated `#[pyproto]`). [#2022](https://github.com/PyO3/pyo3/pull/2022)
- Enable `create_exception!` macro to take an optional docstring. [#2027](https://github.com/PyO3/pyo3/pull/2027)
- Enable `#[pyclass]` for fieldless (aka C-like) enums. [#2034](https://github.com/PyO3/pyo3/pull/2034)
- Add buffer magic methods `__getbuffer__` and `__releasebuffer__` to `#[pymethods]`. [#2067](https://github.com/PyO3/pyo3/pull/2067)
- Add support for paths in `wrap_pyfunction` and `wrap_pymodule`. [#2081](https://github.com/PyO3/pyo3/pull/2081)
- Enable `wrap_pyfunction!` to wrap a `#[pyfunction]` implemented in a different Rust module or crate. [#2091](https://github.com/PyO3/pyo3/pull/2091)
- Add `PyAny::contains` method (`in` operator for `PyAny`). [#2115](https://github.com/PyO3/pyo3/pull/2115)
- Add `PyMapping::contains` method (`in` operator for `PyMapping`). [#2133](https://github.com/PyO3/pyo3/pull/2133)
- Add garbage collection magic magic methods `__traverse__` and `__clear__` to `#[pymethods]`. [#2159](https://github.com/PyO3/pyo3/pull/2159)
- Add support for `from_py_with` on struct tuples and enums to override the default from-Python conversion. [#2181](https://github.com/PyO3/pyo3/pull/2181)
- Add `eq`, `ne`, `lt`, `le`, `gt`, `ge` methods to `PyAny` that wrap `rich_compare`. [#2175](https://github.com/PyO3/pyo3/pull/2175)
- Add `Py::is` and `PyAny::is` methods to check for object identity. [#2183](https://github.com/PyO3/pyo3/pull/2183)
- Add support for the `__getattribute__` magic method. [#2187](https://github.com/PyO3/pyo3/pull/2187)
### Changed
- `PyType::is_subclass`, `PyErr::is_instance` and `PyAny::is_instance` now operate run-time type object instead of a type known at compile-time. The old behavior is still available as `PyType::is_subclass_of`, `PyErr::is_instance_of` and `PyAny::is_instance_of`. [#1985](https://github.com/PyO3/pyo3/pull/1985)
- Rename some methods on `PyErr` (the old names are just marked deprecated for now): [#2026](https://github.com/PyO3/pyo3/pull/2026)
- `pytype` -> `get_type`
- `pvalue` -> `value` (and deprecate equivalent `instance`)
- `ptraceback` -> `traceback`
- `from_instance` -> `from_value`
- `into_instance` -> `into_value`
- `PyErr::new_type` now takes an optional docstring and now returns `PyResult>` rather than a `ffi::PyTypeObject` pointer. [#2027](https://github.com/PyO3/pyo3/pull/2027)
- Deprecate `PyType::is_instance`; it is inconsistent with other `is_instance` methods in PyO3. Instead of `typ.is_instance(obj)`, use `obj.is_instance(typ)`. [#2031](https://github.com/PyO3/pyo3/pull/2031)
- `__getitem__`, `__setitem__` and `__delitem__` in `#[pymethods]` now implement both a Python mapping and sequence by default. [#2065](https://github.com/PyO3/pyo3/pull/2065)
- Improve performance and error messages for `#[derive(FromPyObject)]` for enums. [#2068](https://github.com/PyO3/pyo3/pull/2068)
- Reduce generated LLVM code size (to improve compile times) for:
- internal `handle_panic` helper [#2074](https://github.com/PyO3/pyo3/pull/2074) [#2158](https://github.com/PyO3/pyo3/pull/2158)
- `#[pyfunction]` and `#[pymethods]` argument extraction [#2075](https://github.com/PyO3/pyo3/pull/2075) [#2085](https://github.com/PyO3/pyo3/pull/2085)
- `#[pyclass]` type object creation [#2076](https://github.com/PyO3/pyo3/pull/2076) [#2081](https://github.com/PyO3/pyo3/pull/2081) [#2157](https://github.com/PyO3/pyo3/pull/2157)
- Respect Rust privacy rules for items wrapped with `wrap_pyfunction` and `wrap_pymodule`. [#2081](https://github.com/PyO3/pyo3/pull/2081)
- Add modulo argument to `__ipow__` magic method. [#2083](https://github.com/PyO3/pyo3/pull/2083)
- Fix FFI definition for `_PyCFunctionFast`. [#2126](https://github.com/PyO3/pyo3/pull/2126)
- `PyDateTimeAPI` and `PyDateTime_TimeZone_UTC` are now unsafe functions instead of statics. [#2126](https://github.com/PyO3/pyo3/pull/2126)
- `PyDateTimeAPI` does not implicitly call `PyDateTime_IMPORT` anymore to reflect the original Python API more closely. Before the first call to `PyDateTime_IMPORT` a null pointer is returned. Therefore before calling any of the following FFI functions `PyDateTime_IMPORT` must be called to avoid undefined behavior: [#2126](https://github.com/PyO3/pyo3/pull/2126)
- `PyDateTime_TimeZone_UTC`
- `PyDate_Check`
- `PyDate_CheckExact`
- `PyDateTime_Check`
- `PyDateTime_CheckExact`
- `PyTime_Check`
- `PyTime_CheckExact`
- `PyDelta_Check`
- `PyDelta_CheckExact`
- `PyTZInfo_Check`
- `PyTZInfo_CheckExact`
- `PyDateTime_FromTimestamp`
- `PyDate_FromTimestamp`
- Deprecate the `gc` option for `pyclass` (e.g. `#[pyclass(gc)]`). Just implement a `__traverse__` `#[pymethod]`. [#2159](https://github.com/PyO3/pyo3/pull/2159)
- The `ml_meth` field of `PyMethodDef` is now represented by the `PyMethodDefPointer` union. [2166](https://github.com/PyO3/pyo3/pull/2166)
- Deprecate the `#[pyproto]` traits. [#2173](https://github.com/PyO3/pyo3/pull/2173)
### Removed
- Remove all functionality deprecated in PyO3 0.14. [#2007](https://github.com/PyO3/pyo3/pull/2007)
- Remove `Default` impl for `PyMethodDef`. [#2166](https://github.com/PyO3/pyo3/pull/2166)
- Remove `PartialEq` impl for `Py` and `PyAny` (use the new `is` instead). [#2183](https://github.com/PyO3/pyo3/pull/2183)
### Fixed
- Fix undefined symbol for `PyObject_HasAttr` on PyPy. [#2025](https://github.com/PyO3/pyo3/pull/2025)
- Fix memory leak in `PyErr::into_value`. [#2026](https://github.com/PyO3/pyo3/pull/2026)
- Fix clippy warning `needless-option-as-deref` in code generated by `#[pyfunction]` and `#[pymethods]`. [#2040](https://github.com/PyO3/pyo3/pull/2040)
- Fix undefined behavior in `PySlice::indices`. [#2061](https://github.com/PyO3/pyo3/pull/2061)
- Fix the `wrap_pymodule!` macro using the wrong name for a `#[pymodule]` with a `#[pyo3(name = "..")]` attribute. [#2081](https://github.com/PyO3/pyo3/pull/2081)
- Fix magic methods in `#[pymethods]` accepting implementations with the wrong number of arguments. [#2083](https://github.com/PyO3/pyo3/pull/2083)
- Fix panic in `#[pyfunction]` generated code when a required argument following an `Option` was not provided. [#2093](https://github.com/PyO3/pyo3/pull/2093)
- Fixed undefined behavior caused by incorrect `ExactSizeIterator` implementations. [#2124](https://github.com/PyO3/pyo3/pull/2124)
- Fix missing FFI definition `PyCMethod_New` on Python 3.9 and up. [#2143](https://github.com/PyO3/pyo3/pull/2143)
- Add missing FFI definitions `_PyLong_NumBits` and `_PyLong_AsByteArray` on PyPy. [#2146](https://github.com/PyO3/pyo3/pull/2146)
- Fix memory leak in implementation of `AsPyPointer` for `Option`. [#2160](https://github.com/PyO3/pyo3/pull/2160)
- Fix FFI definition of `_PyLong_NumBits` to return `size_t` instead of `c_int`. [#2161](https://github.com/PyO3/pyo3/pull/2161)
- Fix `TypeError` thrown when argument parsing failed missing the originating causes. [2177](https://github.com/PyO3/pyo3/pull/2178)
## [0.15.2] - 2022-04-14
### Packaging
- Backport of PyPy 3.9 support from PyO3 0.16. [#2262](https://github.com/PyO3/pyo3/pull/2262)
## [0.15.1] - 2021-11-19
### Added
- Add implementations for `Py::as_ref` and `Py::into_ref` for `Py`, `Py` and `Py`. [#1682](https://github.com/PyO3/pyo3/pull/1682)
- Add `PyTraceback` type to represent and format Python tracebacks. [#1977](https://github.com/PyO3/pyo3/pull/1977)
### Changed
- `#[classattr]` constants with a known magic method name (which is lowercase) no longer trigger lint warnings expecting constants to be uppercase. [#1969](https://github.com/PyO3/pyo3/pull/1969)
### Fixed
- Fix creating `#[classattr]` by functions with the name of a known magic method. [#1969](https://github.com/PyO3/pyo3/pull/1969)
- Fix use of `catch_unwind` in `allow_threads` which can cause fatal crashes. [#1989](https://github.com/PyO3/pyo3/pull/1989)
- Fix build failure on PyPy when abi3 features are activated. [#1991](https://github.com/PyO3/pyo3/pull/1991)
- Fix mingw platform detection. [#1993](https://github.com/PyO3/pyo3/pull/1993)
- Fix panic in `__get__` implementation when accessing descriptor on type object. [#1997](https://github.com/PyO3/pyo3/pull/1997)
## [0.15.0] - 2021-11-03
### Packaging
- `pyo3`'s `Cargo.toml` now advertises `links = "python"` to inform Cargo that it links against *libpython*. [#1819](https://github.com/PyO3/pyo3/pull/1819)
- Added optional `anyhow` feature to convert `anyhow::Error` into `PyErr`. [#1822](https://github.com/PyO3/pyo3/pull/1822)
- Support Python 3.10. [#1889](https://github.com/PyO3/pyo3/pull/1889)
- Added optional `eyre` feature to convert `eyre::Report` into `PyErr`. [#1893](https://github.com/PyO3/pyo3/pull/1893)
- Support PyPy 3.8. [#1948](https://github.com/PyO3/pyo3/pull/1948)
### Added
- Add `PyList::get_item_unchecked` and `PyTuple::get_item_unchecked` to get items without bounds checks. [#1733](https://github.com/PyO3/pyo3/pull/1733)
- Support `#[doc = include_str!(...)]` attributes on Rust 1.54 and up. [#1746](https://github.com/PyO3/pyo3/issues/1746)
- Add `PyAny::py` as a convenience for `PyNativeType::py`. [#1751](https://github.com/PyO3/pyo3/pull/1751)
- Add implementation of `std::ops::Index` for `PyList`, `PyTuple` and `PySequence`. [#1825](https://github.com/PyO3/pyo3/pull/1825)
- Add range indexing implementations of `std::ops::Index` for `PyList`, `PyTuple` and `PySequence`. [#1829](https://github.com/PyO3/pyo3/pull/1829)
- Add `PyMapping` type to represent the Python mapping protocol. [#1844](https://github.com/PyO3/pyo3/pull/1844)
- Add commonly-used sequence methods to `PyList` and `PyTuple`. [#1849](https://github.com/PyO3/pyo3/pull/1849)
- Add `as_sequence` methods to `PyList` and `PyTuple`. [#1860](https://github.com/PyO3/pyo3/pull/1860)
- Add support for magic methods in `#[pymethods]`, intended as a replacement for `#[pyproto]`. [#1864](https://github.com/PyO3/pyo3/pull/1864)
- Add `abi3-py310` feature. [#1889](https://github.com/PyO3/pyo3/pull/1889)
- Add `PyCFunction::new_closure` to create a Python function from a Rust closure. [#1901](https://github.com/PyO3/pyo3/pull/1901)
- Add support for positional-only arguments in `#[pyfunction]`. [#1925](https://github.com/PyO3/pyo3/pull/1925)
- Add `PyErr::take` to attempt to fetch a Python exception if present. [#1957](https://github.com/PyO3/pyo3/pull/1957)
### Changed
- `PyList`, `PyTuple` and `PySequence`'s APIs now accepts only `usize` indices instead of `isize`.
[#1733](https://github.com/PyO3/pyo3/pull/1733), [#1802](https://github.com/PyO3/pyo3/pull/1802),
[#1803](https://github.com/PyO3/pyo3/pull/1803)
- `PyList::get_item` and `PyTuple::get_item` now return `PyResult<&PyAny>` instead of panicking. [#1733](https://github.com/PyO3/pyo3/pull/1733)
- `PySequence::in_place_repeat` and `PySequence::in_place_concat` now return `PyResult<&PySequence>` instead of `PyResult<()>`, which is needed in case of immutable sequences such as tuples. [#1803](https://github.com/PyO3/pyo3/pull/1803)
- `PySequence::get_slice` now returns `PyResult<&PySequence>` instead of `PyResult<&PyAny>`. [#1829](https://github.com/PyO3/pyo3/pull/1829)
- Deprecate `PyTuple::split_from`. [#1804](https://github.com/PyO3/pyo3/pull/1804)
- Deprecate `PyTuple::slice`, new method `PyTuple::get_slice` added with `usize` indices. [#1828](https://github.com/PyO3/pyo3/pull/1828)
- Deprecate FFI definitions `PyParser_SimpleParseStringFlags`, `PyParser_SimpleParseStringFlagsFilename`, `PyParser_SimpleParseFileFlags` when building for Python 3.9. [#1830](https://github.com/PyO3/pyo3/pull/1830)
- Mark FFI definitions removed in Python 3.10 `PyParser_ASTFromString`, `PyParser_ASTFromStringObject`, `PyParser_ASTFromFile`, `PyParser_ASTFromFileObject`, `PyParser_SimpleParseStringFlags`, `PyParser_SimpleParseStringFlagsFilename`, `PyParser_SimpleParseFileFlags`, `PyParser_SimpleParseString`, `PyParser_SimpleParseFile`, `Py_SymtableString`, and `Py_SymtableStringObject`. [#1830](https://github.com/PyO3/pyo3/pull/1830)
- `#[pymethods]` now handles magic methods similarly to `#[pyproto]`. In the future, `#[pyproto]` may be deprecated. [#1864](https://github.com/PyO3/pyo3/pull/1864)
- Deprecate FFI definitions `PySys_AddWarnOption`, `PySys_AddWarnOptionUnicode` and `PySys_HasWarnOptions`. [#1887](https://github.com/PyO3/pyo3/pull/1887)
- Deprecate `#[call]` attribute in favor of using `fn __call__`. [#1929](https://github.com/PyO3/pyo3/pull/1929)
- Fix missing FFI definition `_PyImport_FindExtensionObject` on Python 3.10. [#1942](https://github.com/PyO3/pyo3/pull/1942)
- Change `PyErr::fetch` to panic in debug mode if no exception is present. [#1957](https://github.com/PyO3/pyo3/pull/1957)
### Fixed
- Fix building with a conda environment on Windows. [#1873](https://github.com/PyO3/pyo3/pull/1873)
- Fix panic on Python 3.6 when calling `Python::with_gil` with Python initialized but threading not initialized. [#1874](https://github.com/PyO3/pyo3/pull/1874)
- Fix incorrect linking to version-specific DLL instead of `python3.dll` when cross-compiling to Windows with `abi3`. [#1880](https://github.com/PyO3/pyo3/pull/1880)
- Fix FFI definition for `PyTuple_ClearFreeList` incorrectly being present for Python 3.9 and up. [#1887](https://github.com/PyO3/pyo3/pull/1887)
- Fix panic in generated `#[derive(FromPyObject)]` for enums. [#1888](https://github.com/PyO3/pyo3/pull/1888)
- Fix cross-compiling to Python 3.7 builds with the "m" abi flag. [#1908](https://github.com/PyO3/pyo3/pull/1908)
- Fix `__mod__` magic method fallback to `__rmod__`. [#1934](https://github.com/PyO3/pyo3/pull/1934).
- Fix missing FFI definition `_PyImport_FindExtensionObject` on Python 3.10. [#1942](https://github.com/PyO3/pyo3/pull/1942)
## [0.14.5] - 2021-09-05
### Added
- Make `pyo3_build_config::InterpreterConfig` and subfields public. [#1848](https://github.com/PyO3/pyo3/pull/1848)
- Add `resolve-config` feature to the `pyo3-build-config` to control whether its build script does anything. [#1856](https://github.com/PyO3/pyo3/pull/1856)
### Fixed
- Fix 0.14.4 compile regression on `s390x-unknown-linux-gnu` target. [#1850](https://github.com/PyO3/pyo3/pull/1850)
## [0.14.4] - 2021-08-29
### Changed
- Mark `PyString::data` as `unsafe` and disable it and some supporting PyUnicode FFI APIs (which depend on a C bitfield) on big-endian targets. [#1834](https://github.com/PyO3/pyo3/pull/1834)
## [0.14.3] - 2021-08-22
### Added
- Add `PyString::data` to access the raw bytes stored in a Python string. [#1794](https://github.com/PyO3/pyo3/pull/1794)
### Fixed
- Raise `AttributeError` to avoid panic when calling `del` on a `#[setter]` defined class property. [#1779](https://github.com/PyO3/pyo3/pull/1779)
- Restrict FFI definitions `PyGILState_Check` and `Py_tracefunc` to the unlimited API. [#1787](https://github.com/PyO3/pyo3/pull/1787)
- Add missing `_type` field to `PyStatus` struct definition. [#1791](https://github.com/PyO3/pyo3/pull/1791)
- Reduce lower bound `num-complex` optional dependency to support interop with `rust-numpy` and `ndarray` when building with the MSRV of 1.41 [#1799](https://github.com/PyO3/pyo3/pull/1799)
- Fix memory leak in `Python::run_code`. [#1806](https://github.com/PyO3/pyo3/pull/1806)
- Fix memory leak in `PyModule::from_code`. [#1810](https://github.com/PyO3/pyo3/pull/1810)
- Remove use of `pyo3::` in `pyo3::types::datetime` which broke builds using `-Z avoid-dev-deps` [#1811](https://github.com/PyO3/pyo3/pull/1811)
## [0.14.2] - 2021-08-09
### Added
- Add `indexmap` feature to add `ToPyObject`, `IntoPy` and `FromPyObject` implementations for `indexmap::IndexMap`. [#1728](https://github.com/PyO3/pyo3/pull/1728)
- Add `pyo3_build_config::add_extension_module_link_args` to use in build scripts to set linker arguments (for macOS). [#1755](https://github.com/PyO3/pyo3/pull/1755)
- Add `Python::with_gil_unchecked` unsafe variation of `Python::with_gil` to allow obtaining a `Python` in scenarios where `Python::with_gil` would fail. [#1769](https://github.com/PyO3/pyo3/pull/1769)
### Changed
- `PyErr::new` no longer acquires the Python GIL internally. [#1724](https://github.com/PyO3/pyo3/pull/1724)
- Reverted PyO3 0.14.0's use of `cargo:rustc-cdylib-link-arg` in its build script, as Cargo unintentionally allowed crates to pass linker args to downstream crates in this way. Projects supporting macOS may need to restore `.cargo/config.toml` files. [#1755](https://github.com/PyO3/pyo3/pull/1755)
### Fixed
- Fix regression in 0.14.0 rejecting usage of `#[doc(hidden)]` on structs and functions annotated with PyO3 macros. [#1722](https://github.com/PyO3/pyo3/pull/1722)
- Fix regression in 0.14.0 leading to incorrect code coverage being computed for `#[pyfunction]`s. [#1726](https://github.com/PyO3/pyo3/pull/1726)
- Fix incorrect FFI definition of `Py_Buffer` on PyPy. [#1737](https://github.com/PyO3/pyo3/pull/1737)
- Fix incorrect calculation of `dictoffset` on 32-bit Windows. [#1475](https://github.com/PyO3/pyo3/pull/1475)
- Fix regression in 0.13.2 leading to linking to incorrect Python library on Windows "gnu" targets. [#1759](https://github.com/PyO3/pyo3/pull/1759)
- Fix compiler warning: deny trailing semicolons in expression macro. [#1762](https://github.com/PyO3/pyo3/pull/1762)
- Fix incorrect FFI definition of `Py_DecodeLocale`. The 2nd argument is now `*mut Py_ssize_t` instead of `Py_ssize_t`. [#1766](https://github.com/PyO3/pyo3/pull/1766)
## [0.14.1] - 2021-07-04
### Added
- Implement `IntoPy` for `&PathBuf` and `&OsString`. [#1712](https://github.com/PyO3/pyo3/pull/1712)
### Fixed
- Fix crashes on PyPy due to incorrect definitions of `PyList_SET_ITEM`. [#1713](https://github.com/PyO3/pyo3/pull/1713)
## [0.14.0] - 2021-07-03
### Packaging
- Update `num-bigint` optional dependency to 0.4. [#1481](https://github.com/PyO3/pyo3/pull/1481)
- Update `num-complex` optional dependency to 0.4. [#1482](https://github.com/PyO3/pyo3/pull/1482)
- Extend `hashbrown` optional dependency supported versions to include 0.11. [#1496](https://github.com/PyO3/pyo3/pull/1496)
- Support PyPy 3.7. [#1538](https://github.com/PyO3/pyo3/pull/1538)
### Added
- Extend conversions for `[T; N]` to all `N` using const generics (on Rust 1.51 and up). [#1128](https://github.com/PyO3/pyo3/pull/1128)
- Add conversions between `OsStr`/ `OsString` and Python strings. [#1379](https://github.com/PyO3/pyo3/pull/1379)
- Add conversions between `Path`/ `PathBuf` and Python strings (and `pathlib.Path` objects). [#1379](https://github.com/PyO3/pyo3/pull/1379) [#1654](https://github.com/PyO3/pyo3/pull/1654)
- Add a new set of `#[pyo3(...)]` attributes to control various PyO3 macro functionality:
- `#[pyo3(from_py_with = "...")]` function arguments and struct fields to override the default from-Python conversion. [#1411](https://github.com/PyO3/pyo3/pull/1411)
- `#[pyo3(name = "...")]` for setting Python names. [#1567](https://github.com/PyO3/pyo3/pull/1567)
- `#[pyo3(text_signature = "...")]` for setting text signature. [#1658](https://github.com/PyO3/pyo3/pull/1658)
- Add FFI definition `PyCFunction_CheckExact` for Python 3.9 and later. [#1425](https://github.com/PyO3/pyo3/pull/1425)
- Add FFI definition `Py_IS_TYPE`. [#1429](https://github.com/PyO3/pyo3/pull/1429)
- Add FFI definition `_Py_InitializeMain`. [#1473](https://github.com/PyO3/pyo3/pull/1473)
- Add FFI definitions from `cpython/import.h`.[#1475](https://github.com/PyO3/pyo3/pull/1475)
- Add tuple and unit struct support for `#[pyclass]` macro. [#1504](https://github.com/PyO3/pyo3/pull/1504)
- Add FFI definition `PyDateTime_TimeZone_UTC`. [#1572](https://github.com/PyO3/pyo3/pull/1572)
- Add support for `#[pyclass(extends=Exception)]`. [#1591](https://github.com/PyO3/pyo3/pull/1591)
- Add `PyErr::cause` and `PyErr::set_cause`. [#1679](https://github.com/PyO3/pyo3/pull/1679)
- Add FFI definitions from `cpython/pystate.h`. [#1687](https://github.com/PyO3/pyo3/pull/1687/)
- Add `wrap_pyfunction!` macro to `pyo3::prelude`. [#1695](https://github.com/PyO3/pyo3/pull/1695)
### Changed
- Allow only one `#[pymethods]` block per `#[pyclass]` by default, to remove the dependency on `inventory`. Add a `multiple-pymethods` feature to opt-in the original behavior and dependency on `inventory`. [#1457](https://github.com/PyO3/pyo3/pull/1457)
- Change `PyTimeAccess::get_fold` to return a `bool` instead of a `u8`. [#1397](https://github.com/PyO3/pyo3/pull/1397)
- Deprecate FFI definition `PyCFunction_Call` for Python 3.9 and up. [#1425](https://github.com/PyO3/pyo3/pull/1425)
- Deprecate FFI definition `PyModule_GetFilename`. [#1425](https://github.com/PyO3/pyo3/pull/1425)
- The `auto-initialize` feature is no longer enabled by default. [#1443](https://github.com/PyO3/pyo3/pull/1443)
- Change `PyCFunction::new` and `PyCFunction::new_with_keywords` to take `&'static str` arguments rather than implicitly copying (and leaking) them. [#1450](https://github.com/PyO3/pyo3/pull/1450)
- Deprecate `PyModule::call`, `PyModule::call0`, `PyModule::call1` and `PyModule::get`. [#1492](https://github.com/PyO3/pyo3/pull/1492)
- Add length information to `PyBufferError`s raised from `PyBuffer::copy_to_slice` and `PyBuffer::copy_from_slice`. [#1534](https://github.com/PyO3/pyo3/pull/1534)
- Automatically set `-undefined` and `dynamic_lookup` linker arguments on macOS with the `extension-module` feature. [#1539](https://github.com/PyO3/pyo3/pull/1539)
- Deprecate `#[pyproto]` methods which are easier to implement as `#[pymethods]`: [#1560](https://github.com/PyO3/pyo3/pull/1560)
- `PyBasicProtocol::__bytes__` and `PyBasicProtocol::__format__`
- `PyContextProtocol::__enter__` and `PyContextProtocol::__exit__`
- `PyDescrProtocol::__delete__` and `PyDescrProtocol::__set_name__`
- `PyMappingProtocol::__reversed__`
- `PyNumberProtocol::__complex__` and `PyNumberProtocol::__round__`
- `PyAsyncProtocol::__aenter__` and `PyAsyncProtocol::__aexit__`
- Deprecate several attributes in favor of the new `#[pyo3(...)]` options:
- `#[name = "..."]`, replaced by `#[pyo3(name = "...")]` [#1567](https://github.com/PyO3/pyo3/pull/1567)
- `#[pyfn(m, "name")]`, replaced by `#[pyfn(m)] #[pyo3(name = "...")]`. [#1610](https://github.com/PyO3/pyo3/pull/1610)
- `#[pymodule(name)]`, replaced by `#[pymodule] #[pyo3(name = "...")]` [#1650](https://github.com/PyO3/pyo3/pull/1650)
- `#[text_signature = "..."]`, replaced by `#[pyo3(text_signature = "...")]`. [#1658](https://github.com/PyO3/pyo3/pull/1658)
- Reduce LLVM line counts to improve compilation times. [#1604](https://github.com/PyO3/pyo3/pull/1604)
- No longer call `PyEval_InitThreads` in `#[pymodule]` init code. [#1630](https://github.com/PyO3/pyo3/pull/1630)
- Use `METH_FASTCALL` argument passing convention, when possible, to improve `#[pyfunction]` and method performance.
[#1619](https://github.com/PyO3/pyo3/pull/1619), [#1660](https://github.com/PyO3/pyo3/pull/1660)
- Filter sysconfigdata candidates by architecture when cross-compiling. [#1626](https://github.com/PyO3/pyo3/pull/1626)
### Removed
- Remove deprecated exception names `BaseException` etc. [#1426](https://github.com/PyO3/pyo3/pull/1426)
- Remove deprecated methods `Python::is_instance`, `Python::is_subclass`, `Python::release`, `Python::xdecref`, and `Py::from_owned_ptr_or_panic`. [#1426](https://github.com/PyO3/pyo3/pull/1426)
- Remove many FFI definitions which never existed in the Python C-API:
- (previously deprecated) `PyGetSetDef_INIT`, `PyGetSetDef_DICT`, `PyCoro_Check`, `PyCoroWrapper_Check`, and `PyAsyncGen_Check` [#1426](https://github.com/PyO3/pyo3/pull/1426)
- `PyMethodDef_INIT` [#1426](https://github.com/PyO3/pyo3/pull/1426)
- `PyTypeObject_INIT` [#1429](https://github.com/PyO3/pyo3/pull/1429)
- `PyObject_Check`, `PySuper_Check`, and `FreeFunc` [#1438](https://github.com/PyO3/pyo3/pull/1438)
- `PyModuleDef_INIT` [#1630](https://github.com/PyO3/pyo3/pull/1630)
- Remove pyclass implementation details from `PyTypeInfo`:
- `Type`, `DESCRIPTION`, and `FLAGS` [#1456](https://github.com/PyO3/pyo3/pull/1456)
- `BaseType`, `BaseLayout`, `Layout`, `Initializer` [#1596](https://github.com/PyO3/pyo3/pull/1596)
- Remove `PYO3_CROSS_INCLUDE_DIR` environment variable and the associated C header parsing functionality. [#1521](https://github.com/PyO3/pyo3/pull/1521)
- Remove `raw_pycfunction!` macro. [#1619](https://github.com/PyO3/pyo3/pull/1619)
- Remove `PyClassAlloc` trait. [#1657](https://github.com/PyO3/pyo3/pull/1657)
- Remove `PyList::get_parked_item`. [#1664](https://github.com/PyO3/pyo3/pull/1664)
### Fixed
- Remove FFI definition `PyCFunction_ClearFreeList` for Python 3.9 and later. [#1425](https://github.com/PyO3/pyo3/pull/1425)
- `PYO3_CROSS_LIB_DIR` environment variable no long required when compiling for x86-64 Python from macOS arm64 and reverse. [#1428](https://github.com/PyO3/pyo3/pull/1428)
- Fix FFI definition `_PyEval_RequestCodeExtraIndex`, which took an argument of the wrong type. [#1429](https://github.com/PyO3/pyo3/pull/1429)
- Fix FFI definition `PyIndex_Check` missing with the `abi3` feature. [#1436](https://github.com/PyO3/pyo3/pull/1436)
- Fix incorrect `TypeError` raised when keyword-only argument passed along with a positional argument in `*args`. [#1440](https://github.com/PyO3/pyo3/pull/1440)
- Fix inability to use a named lifetime for `&PyTuple` of `*args` in `#[pyfunction]`. [#1440](https://github.com/PyO3/pyo3/pull/1440)
- Fix use of Python argument for `#[pymethods]` inside macro expansions. [#1505](https://github.com/PyO3/pyo3/pull/1505)
- No longer include `__doc__` in `__all__` generated for `#[pymodule]`. [#1509](https://github.com/PyO3/pyo3/pull/1509)
- Always use cross-compiling configuration if any of the `PYO3_CROSS` family of environment variables are set. [#1514](https://github.com/PyO3/pyo3/pull/1514)
- Support `EnvironmentError`, `IOError`, and `WindowsError` on PyPy. [#1533](https://github.com/PyO3/pyo3/pull/1533)
- Fix unnecessary rebuilds when cycling between `cargo check` and `cargo clippy` in a Python virtualenv. [#1557](https://github.com/PyO3/pyo3/pull/1557)
- Fix segfault when dereferencing `ffi::PyDateTimeAPI` without the GIL. [#1563](https://github.com/PyO3/pyo3/pull/1563)
- Fix memory leak in `FromPyObject` implementations for `u128` and `i128`. [#1638](https://github.com/PyO3/pyo3/pull/1638)
- Fix `#[pyclass(extends=PyDict)]` leaking the dict contents on drop. [#1657](https://github.com/PyO3/pyo3/pull/1657)
- Fix segfault when calling `PyList::get_item` with negative indices. [#1668](https://github.com/PyO3/pyo3/pull/1668)
- Fix FFI definitions of `PyEval_SetProfile`/`PyEval_SetTrace` to take `Option` parameters. [#1692](https://github.com/PyO3/pyo3/pull/1692)
- Fix `ToPyObject` impl for `HashSet` to accept non-default hashers. [#1702](https://github.com/PyO3/pyo3/pull/1702)
## [0.13.2] - 2021-02-12
### Packaging
- Lower minimum supported Rust version to 1.41. [#1421](https://github.com/PyO3/pyo3/pull/1421)
### Added
- Add unsafe API `with_embedded_python_interpreter` to initialize a Python interpreter, execute a closure, and finalize the interpreter. [#1355](https://github.com/PyO3/pyo3/pull/1355)
- Add `serde` feature which provides implementations of `Serialize` and `Deserialize` for `Py`. [#1366](https://github.com/PyO3/pyo3/pull/1366)
- Add FFI definition `_PyCFunctionFastWithKeywords` on Python 3.7 and up. [#1384](https://github.com/PyO3/pyo3/pull/1384)
- Add `PyDateTime::new_with_fold` method. [#1398](https://github.com/PyO3/pyo3/pull/1398)
- Add `size_hint` impls for `{PyDict,PyList,PySet,PyTuple}Iterator`s. [#1699](https://github.com/PyO3/pyo3/pull/1699)
### Changed
- `prepare_freethreaded_python` will no longer register an `atexit` handler to call `Py_Finalize`. This resolves a number of issues with incompatible C extensions causing crashes at finalization. [#1355](https://github.com/PyO3/pyo3/pull/1355)
- Mark `PyLayout::py_init`, `PyClassDict::clear_dict`, and `opt_to_pyobj` safe, as they do not perform any unsafe operations. [#1404](https://github.com/PyO3/pyo3/pull/1404)
### Fixed
- Fix support for using `r#raw_idents` as argument names in pyfunctions. [#1383](https://github.com/PyO3/pyo3/pull/1383)
- Fix typo in FFI definition for `PyFunction_GetCode` (was incorrectly `PyFunction_Code`). [#1387](https://github.com/PyO3/pyo3/pull/1387)
- Fix FFI definitions `PyMarshal_WriteObjectToString` and `PyMarshal_ReadObjectFromString` as available in limited API. [#1387](https://github.com/PyO3/pyo3/pull/1387)
- Fix FFI definitions `PyListObject` and those from `funcobject.h` as requiring non-limited API. [#1387](https://github.com/PyO3/pyo3/pull/1387)
- Fix unqualified `Result` usage in `pyobject_native_type_base`. [#1402](https://github.com/PyO3/pyo3/pull/1402)
- Fix build on systems where the default Python encoding is not UTF-8. [#1405](https://github.com/PyO3/pyo3/pull/1405)
- Fix build on mingw / MSYS2. [#1423](https://github.com/PyO3/pyo3/pull/1423)
## [0.13.1] - 2021-01-10
### Added
- Add support for `#[pyclass(dict)]` and `#[pyclass(weakref)]` with the `abi3` feature on Python 3.9 and up. [#1342](https://github.com/PyO3/pyo3/pull/1342)
- Add FFI definitions `PyOS_BeforeFork`, `PyOS_AfterFork_Parent`, `PyOS_AfterFork_Child` for Python 3.7 and up. [#1348](https://github.com/PyO3/pyo3/pull/1348)
- Add an `auto-initialize` feature to control whether PyO3 should automatically initialize an embedded Python interpreter. For compatibility this feature is enabled by default in PyO3 0.13.1, but is planned to become opt-in from PyO3 0.14.0. [#1347](https://github.com/PyO3/pyo3/pull/1347)
- Add support for cross-compiling to Windows without needing `PYO3_CROSS_INCLUDE_DIR`. [#1350](https://github.com/PyO3/pyo3/pull/1350)
### Deprecated
- Deprecate FFI definitions `PyEval_CallObjectWithKeywords`, `PyEval_CallObject`, `PyEval_CallFunction`, `PyEval_CallMethod` when building for Python 3.9. [#1338](https://github.com/PyO3/pyo3/pull/1338)
- Deprecate FFI definitions `PyGetSetDef_DICT` and `PyGetSetDef_INIT` which have never been in the Python API. [#1341](https://github.com/PyO3/pyo3/pull/1341)
- Deprecate FFI definitions `PyGen_NeedsFinalizing`, `PyImport_Cleanup` (removed in 3.9), and `PyOS_InitInterrupts` (3.10). [#1348](https://github.com/PyO3/pyo3/pull/1348)
- Deprecate FFI definition `PyOS_AfterFork` for Python 3.7 and up. [#1348](https://github.com/PyO3/pyo3/pull/1348)
- Deprecate FFI definitions `PyCoro_Check`, `PyAsyncGen_Check`, and `PyCoroWrapper_Check`, which have never been in the Python API (for the first two, it is possible to use `PyCoro_CheckExact` and `PyAsyncGen_CheckExact` instead; these are the actual functions provided by the Python API). [#1348](https://github.com/PyO3/pyo3/pull/1348)
- Deprecate FFI definitions for `PyUnicode_FromUnicode`, `PyUnicode_AsUnicode` and `PyUnicode_AsUnicodeAndSize`, which will be removed from 3.12 and up due to [PEP 623](https://www.python.org/dev/peps/pep-0623/). [#1370](https://github.com/PyO3/pyo3/pull/1370)
### Removed
- Remove FFI definition `PyFrame_ClearFreeList` when building for Python 3.9. [#1341](https://github.com/PyO3/pyo3/pull/1341)
- Remove FFI definition `_PyDict_Contains` when building for Python 3.10. [#1341](https://github.com/PyO3/pyo3/pull/1341)
- Remove FFI definitions `PyGen_NeedsFinalizing` and `PyImport_Cleanup` (for 3.9 and up), and `PyOS_InitInterrupts` (3.10). [#1348](https://github.com/PyO3/pyo3/pull/1348)
### Fixed
- Stop including `Py_TRACE_REFS` config setting automatically if `Py_DEBUG` is set on Python 3.8 and up. [#1334](https://github.com/PyO3/pyo3/pull/1334)
- Remove `#[deny(warnings)]` attribute (and instead refuse warnings only in CI). [#1340](https://github.com/PyO3/pyo3/pull/1340)
- Fix deprecation warning for missing `__module__` with `#[pyclass]`. [#1343](https://github.com/PyO3/pyo3/pull/1343)
- Correct return type of `PyFrozenSet::empty` to `&PyFrozenSet` (was incorrectly `&PySet`). [#1351](https://github.com/PyO3/pyo3/pull/1351)
- Fix missing `Py_INCREF` on heap type objects on Python versions before 3.8. [#1365](https://github.com/PyO3/pyo3/pull/1365)
## [0.13.0] - 2020-12-22
### Packaging
- Drop support for Python 3.5 (as it is now end-of-life). [#1250](https://github.com/PyO3/pyo3/pull/1250)
- Bump minimum supported Rust version to 1.45. [#1272](https://github.com/PyO3/pyo3/pull/1272)
- Bump indoc dependency to 1.0. [#1272](https://github.com/PyO3/pyo3/pull/1272)
- Bump paste dependency to 1.0. [#1272](https://github.com/PyO3/pyo3/pull/1272)
- Rename internal crates `pyo3cls` and `pyo3-derive-backend` to `pyo3-macros` and `pyo3-macros-backend` respectively. [#1317](https://github.com/PyO3/pyo3/pull/1317)
### Added
- Add support for building for CPython limited API. Opting-in to the limited API enables a single extension wheel built with PyO3 to be installable on multiple Python versions. This required a few minor changes to runtime behavior of of PyO3 `#[pyclass]` types. See the migration guide for full details. [#1152](https://github.com/PyO3/pyo3/pull/1152)
- Add feature flags `abi3-py36`, `abi3-py37`, `abi3-py38` etc. to set the minimum Python version when using the limited API. [#1263](https://github.com/PyO3/pyo3/pull/1263)
- Add argument names to `TypeError` messages generated by pymethod wrappers. [#1212](https://github.com/PyO3/pyo3/pull/1212)
- Add FFI definitions for PEP 587 "Python Initialization Configuration". [#1247](https://github.com/PyO3/pyo3/pull/1247)
- Add FFI definitions for `PyEval_SetProfile` and `PyEval_SetTrace`. [#1255](https://github.com/PyO3/pyo3/pull/1255)
- Add FFI definitions for context.h functions (`PyContext_New`, etc). [#1259](https://github.com/PyO3/pyo3/pull/1259)
- Add `PyAny::is_instance` method. [#1276](https://github.com/PyO3/pyo3/pull/1276)
- Add support for conversion between `char` and `PyString`. [#1282](https://github.com/PyO3/pyo3/pull/1282)
- Add FFI definitions for `PyBuffer_SizeFromFormat`, `PyObject_LengthHint`, `PyObject_CallNoArgs`, `PyObject_CallOneArg`, `PyObject_CallMethodNoArgs`, `PyObject_CallMethodOneArg`, `PyObject_VectorcallDict`, and `PyObject_VectorcallMethod`. [#1287](https://github.com/PyO3/pyo3/pull/1287)
- Add conversions between `u128`/`i128` and `PyLong` for PyPy. [#1310](https://github.com/PyO3/pyo3/pull/1310)
- Add `Python::version` and `Python::version_info` to get the running interpreter version. [#1322](https://github.com/PyO3/pyo3/pull/1322)
- Add conversions for tuples of length 10, 11, and 12. [#1454](https://github.com/PyO3/pyo3/pull/1454)
### Changed
- Change return type of `PyType::name` from `Cow` to `PyResult<&str>`. [#1152](https://github.com/PyO3/pyo3/pull/1152)
- `#[pyclass(subclass)]` is now required for subclassing from Rust (was previously just required for subclassing from Python). [#1152](https://github.com/PyO3/pyo3/pull/1152)
- Change `PyIterator` to be consistent with other native types: it is now used as `&PyIterator` instead of `PyIterator<'a>`. [#1176](https://github.com/PyO3/pyo3/pull/1176)
- Change formatting of `PyDowncastError` messages to be closer to Python's builtin error messages. [#1212](https://github.com/PyO3/pyo3/pull/1212)
- Change `Debug` and `Display` impls for `PyException` to be consistent with `PyAny`. [#1275](https://github.com/PyO3/pyo3/pull/1275)
- Change `Debug` impl of `PyErr` to output more helpful information (acquiring the GIL if necessary). [#1275](https://github.com/PyO3/pyo3/pull/1275)
- Rename `PyTypeInfo::is_instance` and `PyTypeInfo::is_exact_instance` to `PyTypeInfo::is_type_of` and `PyTypeInfo::is_exact_type_of`. [#1278](https://github.com/PyO3/pyo3/pull/1278)
- Optimize `PyAny::call0`, `Py::call0` and `PyAny::call_method0` and `Py::call_method0` on Python 3.9 and up. [#1287](https://github.com/PyO3/pyo3/pull/1285)
- Require double-quotes for pyclass name argument e.g `#[pyclass(name = "MyClass")]`. [#1303](https://github.com/PyO3/pyo3/pull/1303)
### Deprecated
- Deprecate `Python::is_instance`, `Python::is_subclass`, `Python::release`, and `Python::xdecref`. [#1292](https://github.com/PyO3/pyo3/pull/1292)
### Removed
- Remove deprecated ffi definitions `PyUnicode_AsUnicodeCopy`, `PyUnicode_GetMax`, `_Py_CheckRecursionLimit`, `PyObject_AsCharBuffer`, `PyObject_AsReadBuffer`, `PyObject_CheckReadBuffer` and `PyObject_AsWriteBuffer`, which will be removed in Python 3.10. [#1217](https://github.com/PyO3/pyo3/pull/1217)
- Remove unused `python3` feature. [#1235](https://github.com/PyO3/pyo3/pull/1235)
### Fixed
- Fix missing field in `PyCodeObject` struct (`co_posonlyargcount`) - caused invalid access to other fields in Python >3.7. [#1260](https://github.com/PyO3/pyo3/pull/1260)
- Fix building for `x86_64-unknown-linux-musl` target from `x86_64-unknown-linux-gnu` host. [#1267](https://github.com/PyO3/pyo3/pull/1267)
- Fix `#[text_signature]` interacting badly with rust `r#raw_identifiers`. [#1286](https://github.com/PyO3/pyo3/pull/1286)
- Fix FFI definitions for `PyObject_Vectorcall` and `PyVectorcall_Call`. [#1287](https://github.com/PyO3/pyo3/pull/1285)
- Fix building with Anaconda python inside a virtualenv. [#1290](https://github.com/PyO3/pyo3/pull/1290)
- Fix definition of opaque FFI types. [#1312](https://github.com/PyO3/pyo3/pull/1312)
- Fix using custom error type in pyclass `#[new]` methods. [#1319](https://github.com/PyO3/pyo3/pull/1319)
## [0.12.4] - 2020-11-28
### Fixed
- Fix reference count bug in implementation of `From>` for `PyObject`, a regression introduced in PyO3 0.12. [#1297](https://github.com/PyO3/pyo3/pull/1297)
## [0.12.3] - 2020-10-12
### Fixed
- Fix support for Rust versions 1.39 to 1.44, broken by an incorrect internal update to paste 1.0 which was done in PyO3 0.12.2. [#1234](https://github.com/PyO3/pyo3/pull/1234)
## [0.12.2] - 2020-10-12
### Added
- Add support for keyword-only arguments without default values in `#[pyfunction]`. [#1209](https://github.com/PyO3/pyo3/pull/1209)
- Add `Python::check_signals` as a safe a wrapper for `PyErr_CheckSignals`. [#1214](https://github.com/PyO3/pyo3/pull/1214)
### Fixed
- Fix invalid document for protocol methods. [#1169](https://github.com/PyO3/pyo3/pull/1169)
- Hide docs of PyO3 private implementation details in `pyo3::class::methods`. [#1169](https://github.com/PyO3/pyo3/pull/1169)
- Fix unnecessary rebuild on PATH changes when the python interpreter is provided by PYO3_PYTHON. [#1231](https://github.com/PyO3/pyo3/pull/1231)
## [0.12.1] - 2020-09-16
### Fixed
- Fix building for a 32-bit Python on 64-bit Windows with a 64-bit Rust toolchain. [#1179](https://github.com/PyO3/pyo3/pull/1179)
- Fix building on platforms where `c_char` is `u8`. [#1182](https://github.com/PyO3/pyo3/pull/1182)
## [0.12.0] - 2020-09-12
### Added
- Add FFI definitions `Py_FinalizeEx`, `PyOS_getsig`, and `PyOS_setsig`. [#1021](https://github.com/PyO3/pyo3/pull/1021)
- Add `PyString::to_str` for accessing `PyString` as `&str`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
- Add `Python::with_gil` for executing a closure with the Python GIL. [#1037](https://github.com/PyO3/pyo3/pull/1037)
- Add type information to failures in `PyAny::downcast`. [#1050](https://github.com/PyO3/pyo3/pull/1050)
- Implement `Debug` for `PyIterator`. [#1051](https://github.com/PyO3/pyo3/pull/1051)
- Add `PyBytes::new_with` and `PyByteArray::new_with` for initialising `bytes` and `bytearray` objects using a closure. [#1074](https://github.com/PyO3/pyo3/pull/1074)
- Add `#[derive(FromPyObject)]` macro for enums and structs. [#1065](https://github.com/PyO3/pyo3/pull/1065)
- Add `Py::as_ref` and `Py::into_ref` for converting `Py` to `&T`. [#1098](https://github.com/PyO3/pyo3/pull/1098)
- Add ability to return `Result` types other than `PyResult` from `#[pyfunction]`, `#[pymethod]` and `#[pyproto]` functions. [#1106](https://github.com/PyO3/pyo3/pull/1118).
- Implement `ToPyObject`, `IntoPy`, and `FromPyObject` for [hashbrown](https://crates.io/crates/hashbrown)'s `HashMap` and `HashSet` types (requires the `hashbrown` feature). [#1114](https://github.com/PyO3/pyo3/pull/1114)
- Add `#[pyfunction(pass_module)]` and `#[pyfn(pass_module)]` to pass the module object as the first function argument. [#1143](https://github.com/PyO3/pyo3/pull/1143)
- Add `PyModule::add_function` and `PyModule::add_submodule` as typed alternatives to `PyModule::add_wrapped`. [#1143](https://github.com/PyO3/pyo3/pull/1143)
- Add native `PyCFunction` and `PyFunction` types. [#1163](https://github.com/PyO3/pyo3/pull/1163)
### Changed
- Rework exception types: [#1024](https://github.com/PyO3/pyo3/pull/1024) [#1115](https://github.com/PyO3/pyo3/pull/1115)
- Rename exception types from e.g. `RuntimeError` to `PyRuntimeError`. The old names continue to exist but are deprecated.
- Exception objects are now accessible as `&T` or `Py`, just like other Python-native types.
- Rename `PyException::py_err` to `PyException::new_err`.
- Rename `PyUnicodeDecodeErr::new_err` to `PyUnicodeDecodeErr::new`.
- Remove `PyStopIteration::stop_iteration`.
- Require `T: Send` for the return value `T` of `Python::allow_threads`. [#1036](https://github.com/PyO3/pyo3/pull/1036)
- Rename `PYTHON_SYS_EXECUTABLE` to `PYO3_PYTHON`. The old name will continue to work (undocumented) but will be removed in a future release. [#1039](https://github.com/PyO3/pyo3/pull/1039)
- Remove `unsafe` from signature of `PyType::as_type_ptr`. [#1047](https://github.com/PyO3/pyo3/pull/1047)
- Change return type of `PyIterator::from_object` to `PyResult` (was `Result`). [#1051](https://github.com/PyO3/pyo3/pull/1051)
- `IntoPy` is no longer implied by `FromPy`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
- Change `PyObject` to be a type alias for `Py`. [#1063](https://github.com/PyO3/pyo3/pull/1063)
- Rework `PyErr` to be compatible with the `std::error::Error` trait: [#1067](https://github.com/PyO3/pyo3/pull/1067) [#1115](https://github.com/PyO3/pyo3/pull/1115)
- Implement `Display`, `Error`, `Send` and `Sync` for `PyErr` and `PyErrArguments`.
- Add `PyErr::instance` for accessing `PyErr` as `&PyBaseException`.
- `PyErr`'s fields are now an implementation detail. The equivalent values can be accessed with `PyErr::ptype`, `PyErr::pvalue` and `PyErr::ptraceback`.
- Change receiver of `PyErr::print` and `PyErr::print_and_set_sys_last_vars` to `&self` (was `self`).
- Remove `PyErrValue`, `PyErr::from_value`, `PyErr::into_normalized`, and `PyErr::normalize`.
- Remove `PyException::into`.
- Remove `Into>` for `PyErr` and `PyException`.
- Change methods generated by `#[pyproto]` to return `NotImplemented` if Python should try a reversed operation. #[1072](https://github.com/PyO3/pyo3/pull/1072)
- Change argument to `PyModule::add` to `impl IntoPy` (was `impl ToPyObject`). #[1124](https://github.com/PyO3/pyo3/pull/1124)
### Removed
- Remove many exception and `PyErr` APIs; see the "changed" section above. [#1024](https://github.com/PyO3/pyo3/pull/1024) [#1067](https://github.com/PyO3/pyo3/pull/1067) [#1115](https://github.com/PyO3/pyo3/pull/1115)
- Remove `PyString::to_string` (use new `PyString::to_str`). [#1023](https://github.com/PyO3/pyo3/pull/1023)
- Remove `PyString::as_bytes`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
- Remove `Python::register_any`. [#1023](https://github.com/PyO3/pyo3/pull/1023)
- Remove `GILGuard::acquire` from the public API. Use `Python::acquire_gil` or `Python::with_gil`. [#1036](https://github.com/PyO3/pyo3/pull/1036)
- Remove the `FromPy` trait. [#1063](https://github.com/PyO3/pyo3/pull/1063)
- Remove the `AsPyRef` trait. [#1098](https://github.com/PyO3/pyo3/pull/1098)
### Fixed
- Correct FFI definitions `Py_SetProgramName` and `Py_SetPythonHome` to take `*const` arguments (was `*mut`). [#1021](https://github.com/PyO3/pyo3/pull/1021)
- Fix `FromPyObject` for `num_bigint::BigInt` for Python objects with an `__index__` method. [#1027](https://github.com/PyO3/pyo3/pull/1027)
- Correct FFI definition `_PyLong_AsByteArray` to take `*mut c_uchar` argument (was `*const c_uchar`). [#1029](https://github.com/PyO3/pyo3/pull/1029)
- Fix segfault with `#[pyclass(dict, unsendable)]`. [#1058](https://github.com/PyO3/pyo3/pull/1058) [#1059](https://github.com/PyO3/pyo3/pull/1059)
- Fix using `&Self` as an argument type for functions in a `#[pymethods]` block. [#1071](https://github.com/PyO3/pyo3/pull/1071)
- Fix best-effort build against PyPy 3.6. [#1092](https://github.com/PyO3/pyo3/pull/1092)
- Fix many cases of lifetime elision in `#[pyproto]` implementations. [#1093](https://github.com/PyO3/pyo3/pull/1093)
- Fix detection of Python build configuration when cross-compiling. [#1095](https://github.com/PyO3/pyo3/pull/1095)
- Always link against libpython on android with the `extension-module` feature. [#1095](https://github.com/PyO3/pyo3/pull/1095)
- Fix the `+` operator not trying `__radd__` when both `__add__` and `__radd__` are defined in `PyNumberProtocol` (and similar for all other reversible operators). [#1107](https://github.com/PyO3/pyo3/pull/1107)
- Fix building with Anaconda python. [#1175](https://github.com/PyO3/pyo3/pull/1175)
## [0.11.1] - 2020-06-30
### Added
- `#[pyclass(unsendable)]`. [#1009](https://github.com/PyO3/pyo3/pull/1009)
### Changed
- Update `parking_lot` dependency to `0.11`. [#1010](https://github.com/PyO3/pyo3/pull/1010)
## [0.11.0] - 2020-06-28
### Added
- Support stable versions of Rust (>=1.39). [#969](https://github.com/PyO3/pyo3/pull/969)
- Add FFI definition `PyObject_AsFileDescriptor`. [#938](https://github.com/PyO3/pyo3/pull/938)
- Add `PyByteArray::data`, `PyByteArray::as_bytes`, and `PyByteArray::as_bytes_mut`. [#967](https://github.com/PyO3/pyo3/pull/967)
- Add `GILOnceCell` to use in situations where `lazy_static` or `once_cell` can deadlock. [#975](https://github.com/PyO3/pyo3/pull/975)
- Add `Py::borrow`, `Py::borrow_mut`, `Py::try_borrow`, and `Py::try_borrow_mut` for accessing `#[pyclass]` values. [#976](https://github.com/PyO3/pyo3/pull/976)
- Add `IterNextOutput` and `IterANextOutput` for returning from `__next__` / `__anext__`. [#997](https://github.com/PyO3/pyo3/pull/997)
### Changed
- Simplify internals of `#[pyo3(get)]` attribute. (Remove the hidden API `GetPropertyValue`.) [#934](https://github.com/PyO3/pyo3/pull/934)
- Call `Py_Finalize` at exit to flush buffers, etc. [#943](https://github.com/PyO3/pyo3/pull/943)
- Add type parameter to PyBuffer. #[951](https://github.com/PyO3/pyo3/pull/951)
- Require `Send` bound for `#[pyclass]`. [#966](https://github.com/PyO3/pyo3/pull/966)
- Add `Python` argument to most methods on `PyObject` and `Py` to ensure GIL safety. [#970](https://github.com/PyO3/pyo3/pull/970)
- Change signature of `PyTypeObject::type_object` - now takes `Python` argument and returns `&PyType`. [#970](https://github.com/PyO3/pyo3/pull/970)
- Change return type of `PyTuple::slice` and `PyTuple::split_from` from `Py` to `&PyTuple`. [#970](https://github.com/PyO3/pyo3/pull/970)
- Change return type of `PyTuple::as_slice` to `&[&PyAny]`. [#971](https://github.com/PyO3/pyo3/pull/971)
- Rename `PyTypeInfo::type_object` to `type_object_raw`, and add `Python` argument. [#975](https://github.com/PyO3/pyo3/pull/975)
- Update `num-complex` optional dependency from `0.2` to `0.3`. [#977](https://github.com/PyO3/pyo3/pull/977)
- Update `num-bigint` optional dependency from `0.2` to `0.3`. [#978](https://github.com/PyO3/pyo3/pull/978)
- `#[pyproto]` is re-implemented without specialization. [#961](https://github.com/PyO3/pyo3/pull/961)
- `PyClassAlloc::alloc` is renamed to `PyClassAlloc::new`. [#990](https://github.com/PyO3/pyo3/pull/990)
- `#[pyproto]` methods can now have return value `T` or `PyResult` (previously only `PyResult` was supported). [#996](https://github.com/PyO3/pyo3/pull/996)
- `#[pyproto]` methods can now skip annotating the return type if it is `()`. [#998](https://github.com/PyO3/pyo3/pull/998)
### Removed
- Remove `ManagedPyRef` (unused, and needs specialization) [#930](https://github.com/PyO3/pyo3/pull/930)
### Fixed
- Fix passing explicit `None` to `Option` argument `#[pyfunction]` with a default value. [#936](https://github.com/PyO3/pyo3/pull/936)
- Fix `PyClass.__new__`'s not respecting subclasses when inherited by a Python class. [#990](https://github.com/PyO3/pyo3/pull/990)
- Fix returning `Option` from `#[pyproto]` methods. [#996](https://github.com/PyO3/pyo3/pull/996)
- Fix accepting `PyRef` and `PyRefMut` to `#[getter]` and `#[setter]` methods. [#999](https://github.com/PyO3/pyo3/pull/999)
## [0.10.1] - 2020-05-14
### Fixed
- Fix deadlock in `Python::acquire_gil` after dropping a `PyObject` or `Py`. [#924](https://github.com/PyO3/pyo3/pull/924)
## [0.10.0] - 2020-05-13
### Added
- Add FFI definition `_PyDict_NewPresized`. [#849](https://github.com/PyO3/pyo3/pull/849)
- Implement `IntoPy` for `HashSet` and `BTreeSet`. [#864](https://github.com/PyO3/pyo3/pull/864)
- Add `PyAny::dir` method. [#886](https://github.com/PyO3/pyo3/pull/886)
- Gate macros behind a `macros` feature (enabled by default). [#897](https://github.com/PyO3/pyo3/pull/897)
- Add ability to define class attributes using `#[classattr]` on functions in `#[pymethods]`. [#905](https://github.com/PyO3/pyo3/pull/905)
- Implement `Clone` for `PyObject` and `Py`. [#908](https://github.com/PyO3/pyo3/pull/908)
- Implement `Deref` for all builtin types. (`PyList`, `PyTuple`, `PyDict` etc.) [#911](https://github.com/PyO3/pyo3/pull/911)
- Implement `Deref` for `PyCell`. [#911](https://github.com/PyO3/pyo3/pull/911)
- Add `#[classattr]` support for associated constants in `#[pymethods]`. [#914](https://github.com/PyO3/pyo3/pull/914)
### Changed
- Panics will now be raised as a Python `PanicException`. [#797](https://github.com/PyO3/pyo3/pull/797)
- Change `PyObject` and `Py` reference counts to decrement immediately upon drop when the GIL is held. [#851](https://github.com/PyO3/pyo3/pull/851)
- Allow `PyIterProtocol` methods to use either `PyRef` or `PyRefMut` as the receiver type. [#856](https://github.com/PyO3/pyo3/pull/856)
- Change the implementation of `FromPyObject` for `Py` to apply to a wider range of `T`, including all `T: PyClass`. [#880](https://github.com/PyO3/pyo3/pull/880)
- Move all methods from the `ObjectProtocol` trait to the `PyAny` struct. [#911](https://github.com/PyO3/pyo3/pull/911)
- Remove need for `#![feature(specialization)]` in crates depending on PyO3. [#917](https://github.com/PyO3/pyo3/pull/917)
### Removed
- Remove `PyMethodsProtocol` trait. [#889](https://github.com/PyO3/pyo3/pull/889)
- Remove `num-traits` dependency. [#895](https://github.com/PyO3/pyo3/pull/895)
- Remove `ObjectProtocol` trait. [#911](https://github.com/PyO3/pyo3/pull/911)
- Remove `PyAny::None`. Users should use `Python::None` instead. [#911](https://github.com/PyO3/pyo3/pull/911)
- Remove all `*ProtocolImpl` traits. [#917](https://github.com/PyO3/pyo3/pull/917)
### Fixed
- Fix support for `__radd__` and other `__r*__` methods as implementations for Python mathematical operators. [#839](https://github.com/PyO3/pyo3/pull/839)
- Fix panics during garbage collection when traversing objects that were already mutably borrowed. [#855](https://github.com/PyO3/pyo3/pull/855)
- Prevent `&'static` references to Python objects as arguments to `#[pyfunction]` and `#[pymethods]`. [#869](https://github.com/PyO3/pyo3/pull/869)
- Fix lifetime safety bug with `AsPyRef::as_ref`. [#876](https://github.com/PyO3/pyo3/pull/876)
- Fix `#[pyo3(get)]` attribute on `Py` fields. [#880](https://github.com/PyO3/pyo3/pull/880)
- Fix segmentation faults caused by functions such as `PyList::get_item` returning borrowed objects when it was not safe to do so. [#890](https://github.com/PyO3/pyo3/pull/890)
- Fix segmentation faults caused by nested `Python::acquire_gil` calls creating dangling references. [#893](https://github.com/PyO3/pyo3/pull/893)
- Fix segmentatation faults when a panic occurs during a call to `Python::allow_threads`. [#912](https://github.com/PyO3/pyo3/pull/912)
## [0.9.2] - 2020-04-09
### Added
- `FromPyObject` implementations for `HashSet` and `BTreeSet`. [#842](https://github.com/PyO3/pyo3/pull/842)
### Fixed
- Correctly detect 32bit architecture. [#830](https://github.com/PyO3/pyo3/pull/830)
## [0.9.1] - 2020-03-23
### Fixed
- Error messages for `#[pyclass]`. [#826](https://github.com/PyO3/pyo3/pull/826)
- `FromPyObject` implementation for `PySequence`. [#827](https://github.com/PyO3/pyo3/pull/827)
## [0.9.0] - 2020-03-19
### Added
- `PyCell`, which has RefCell-like features. [#770](https://github.com/PyO3/pyo3/pull/770)
- `PyClass`, `PyLayout`, `PyClassInitializer`. [#683](https://github.com/PyO3/pyo3/pull/683)
- Implemented `IntoIterator` for `PySet` and `PyFrozenSet`. [#716](https://github.com/PyO3/pyo3/pull/716)
- `FromPyObject` is now automatically implemented for `T: Clone` pyclasses. [#730](https://github.com/PyO3/pyo3/pull/730)
- `#[pyo3(get)]` and `#[pyo3(set)]` will now use the Rust doc-comment from the field for the Python property. [#755](https://github.com/PyO3/pyo3/pull/755)
- `#[setter]` functions may now take an argument of `Pyo3::Python`. [#760](https://github.com/PyO3/pyo3/pull/760)
- `PyTypeInfo::BaseLayout` and `PyClass::BaseNativeType`. [#770](https://github.com/PyO3/pyo3/pull/770)
- `PyDowncastImpl`. [#770](https://github.com/PyO3/pyo3/pull/770)
- Implement `FromPyObject` and `IntoPy` traits for arrays (up to 32). [#778](https://github.com/PyO3/pyo3/pull/778)
- `migration.md` and `types.md` in the guide. [#795](https://github.com/PyO3/pyo3/pull/795), #[802](https://github.com/PyO3/pyo3/pull/802)
- `ffi::{_PyBytes_Resize, _PyDict_Next, _PyDict_Contains, _PyDict_GetDictPtr}`. #[820](https://github.com/PyO3/pyo3/pull/820)
### Changed
- `#[new]` does not take `PyRawObject` and can return `Self`. [#683](https://github.com/PyO3/pyo3/pull/683)
- The blanket implementations for `FromPyObject` for `&T` and `&mut T` are no longer specializable. Implement `PyTryFrom` for your type to control the behavior of `FromPyObject::extract` for your types. [#713](https://github.com/PyO3/pyo3/pull/713)
- The implementation for `IntoPy for T` where `U: FromPy` is no longer specializable. Control the behavior of this via the implementation of `FromPy`. [#713](https://github.com/PyO3/pyo3/pull/713)
- Use `parking_lot::Mutex` instead of `spin::Mutex`. [#734](https://github.com/PyO3/pyo3/pull/734)
- Bumped minimum Rust version to `1.42.0-nightly 2020-01-21`. [#761](https://github.com/PyO3/pyo3/pull/761)
- `PyRef` and `PyRefMut` are renewed for `PyCell`. [#770](https://github.com/PyO3/pyo3/pull/770)
- Some new FFI functions for Python 3.8. [#784](https://github.com/PyO3/pyo3/pull/784)
- `PyAny` is now on the top level module and prelude. [#816](https://github.com/PyO3/pyo3/pull/816)
### Removed
- `PyRawObject`. [#683](https://github.com/PyO3/pyo3/pull/683)
- `PyNoArgsFunction`. [#741](https://github.com/PyO3/pyo3/pull/741)
- `initialize_type`. To set the module name for a `#[pyclass]`, use the `module` argument to the macro. #[751](https://github.com/PyO3/pyo3/pull/751)
- `AsPyRef::as_mut/with/with_mut/into_py/into_mut_py`. [#770](https://github.com/PyO3/pyo3/pull/770)
- `PyTryFrom::try_from_mut/try_from_mut_exact/try_from_mut_unchecked`. [#770](https://github.com/PyO3/pyo3/pull/770)
- `Python::mut_from_owned_ptr/mut_from_borrowed_ptr`. [#770](https://github.com/PyO3/pyo3/pull/770)
- `ObjectProtocol::get_base/get_mut_base`. [#770](https://github.com/PyO3/pyo3/pull/770)
### Fixed
- Fixed unsoundness of subclassing. [#683](https://github.com/PyO3/pyo3/pull/683).
- Clear error indicator when the exception is handled on the Rust side. [#719](https://github.com/PyO3/pyo3/pull/719)
- Usage of raw identifiers with `#[pyo3(set)]`. [#745](https://github.com/PyO3/pyo3/pull/745)
- Usage of `PyObject` with `#[pyo3(get)]`. [#760](https://github.com/PyO3/pyo3/pull/760)
- `#[pymethods]` used in conjunction with `#[cfg]`. #[769](https://github.com/PyO3/pyo3/pull/769)
- `"*"` in a `#[pyfunction()]` argument list incorrectly accepting any number of positional arguments (use `args = "*"` when this behavior is desired). #[792](https://github.com/PyO3/pyo3/pull/792)
- `PyModule::dict`. #[809](https://github.com/PyO3/pyo3/pull/809)
- Fix the case where `DESCRIPTION` is not null-terminated. #[822](https://github.com/PyO3/pyo3/pull/822)
## [0.8.5] - 2020-01-05
### Added
- Implemented `FromPyObject` for `HashMap` and `BTreeMap`
- Support for `#[name = "foo"]` attribute for `#[pyfunction]` and in `#[pymethods]`. [#692](https://github.com/PyO3/pyo3/pull/692)
## [0.8.4] - 2019-12-14
### Added
- Support for `#[text_signature]` attribute. [#675](https://github.com/PyO3/pyo3/pull/675)
## [0.8.3] - 2019-11-23
### Removed
- `#[init]` is removed. [#658](https://github.com/PyO3/pyo3/pull/658)
### Fixed
- Now all `&Py~` types have `!Send` bound. [#655](https://github.com/PyO3/pyo3/pull/655)
- Fix a compile error raised by the stabilization of `!` type. [#672](https://github.com/PyO3/pyo3/issues/672).
## [0.8.2] - 2019-10-27
### Added
- FFI compatibility for PEP 590 Vectorcall. [#641](https://github.com/PyO3/pyo3/pull/641)
### Fixed
- Fix PySequenceProtocol::set_item. [#624](https://github.com/PyO3/pyo3/pull/624)
- Fix a corner case of BigInt::FromPyObject. [#630](https://github.com/PyO3/pyo3/pull/630)
- Fix index errors in parameter conversion. [#631](https://github.com/PyO3/pyo3/pull/631)
- Fix handling of invalid utf-8 sequences in `PyString::as_bytes`. [#639](https://github.com/PyO3/pyo3/pull/639)
and `PyString::to_string_lossy` [#642](https://github.com/PyO3/pyo3/pull/642).
- Remove `__contains__` and `__iter__` from PyMappingProtocol. [#644](https://github.com/PyO3/pyo3/pull/644)
- Fix proc-macro definition of PySetAttrProtocol. [#645](https://github.com/PyO3/pyo3/pull/645)
## [0.8.1] - 2019-10-08
### Added
- Conversion between [num-bigint](https://github.com/rust-num/num-bigint) and Python int. [#608](https://github.com/PyO3/pyo3/pull/608)
### Fixed
- Make sure the right Python interpreter is used in OSX builds. [#604](https://github.com/PyO3/pyo3/pull/604)
- Patch specialization being broken by Rust 1.40. [#614](https://github.com/PyO3/pyo3/issues/614)
- Fix a segfault around PyErr. [#597](https://github.com/PyO3/pyo3/pull/597)
## [0.8.0] - 2019-09-16
### Added
- `module` argument to `pyclass` macro. [#499](https://github.com/PyO3/pyo3/pull/499)
- `py_run!` macro [#512](https://github.com/PyO3/pyo3/pull/512)
- Use existing fields and methods before calling custom **getattr**. [#505](https://github.com/PyO3/pyo3/pull/505)
- `PyBytes` can now be indexed just like `Vec`
- Implement `IntoPy` for `PyRef` and `PyRefMut`.
### Changed
- Implementing the Using the `gc` parameter for `pyclass` (e.g. `#[pyclass(gc)]`) without implementing the `class::PyGCProtocol` trait is now a compile-time error. Failing to implement this trait could lead to segfaults. [#532](https://github.com/PyO3/pyo3/pull/532)
- `PyByteArray::data` has been replaced with `PyDataArray::to_vec` because returning a `&[u8]` is unsound. (See [this comment](https://github.com/PyO3/pyo3/issues/373#issuecomment-512332696) for a great write-up for why that was unsound)
- Replace `mashup` with `paste`.
- `GILPool` gained a `Python` marker to prevent it from being misused to release Python objects without the GIL held.
### Removed
- `IntoPyObject` was replaced with `IntoPy`
- `#[pyclass(subclass)]` is hidden a `unsound-subclass` feature because it's causing segmentation faults.
### Fixed
- More readable error message for generics in pyclass [#503](https://github.com/PyO3/pyo3/pull/503)
## [0.7.0] - 2019-05-26
### Added
- PyPy support by omerbenamram in [#393](https://github.com/PyO3/pyo3/pull/393)
- Have `PyModule` generate an index of its members (`__all__` list).
- Allow `slf: PyRef` for pyclass(#419)
- Allow to use lifetime specifiers in `pymethods`
- Add `marshal` module. [#460](https://github.com/PyO3/pyo3/pull/460)
### Changed
- `Python::run` returns `PyResult<()>` instead of `PyResult<&PyAny>`.
- Methods decorated with `#[getter]` and `#[setter]` can now omit wrapping the
result type in `PyResult` if they don't raise exceptions.
### Fixed
- `type_object::PyTypeObject` has been marked unsafe because breaking the contract `type_object::PyTypeObject::init_type` can lead to UB.
- Fixed automatic derive of `PySequenceProtocol` implementation in [#423](https://github.com/PyO3/pyo3/pull/423).
- Capitalization & better wording to README.md.
- Docstrings of properties is now properly set using the doc of the `#[getter]` method.
- Fixed issues with `pymethods` crashing on doc comments containing double quotes.
- `PySet::new` and `PyFrozenSet::new` now return `PyResult<&Py[Frozen]Set>`; exceptions are raised if
the items are not hashable.
- Fixed building using `venv` on Windows.
- `PyTuple::new` now returns `&PyTuple` instead of `Py`.
- Fixed several issues with argument parsing; notable, the `*args` and `**kwargs`
tuple/dict now doesn't contain arguments that are otherwise assigned to parameters.
## [0.6.0] - 2019-03-28
### Regressions
- Currently, [#341](https://github.com/PyO3/pyo3/issues/341) causes `cargo test` to fail with weird linking errors when the `extension-module` feature is activated. For now you can work around this by making the `extension-module` feature optional and running the tests with `cargo test --no-default-features`:
```toml
[dependencies.pyo3]
version = "0.6.0"
[features]
extension-module = ["pyo3/extension-module"]
default = ["extension-module"]
```
### Added
- Added a `wrap_pymodule!` macro similar to the existing `wrap_pyfunction!` macro. Only available on python 3
- Added support for cross compiling (e.g. to arm v7) by mtp401 in [#327](https://github.com/PyO3/pyo3/pull/327). See the "Cross Compiling" section in the "Building and Distribution" chapter of the guide for more details.
- The `PyRef` and `PyRefMut` types, which allow to differentiate between an instance of a rust struct on the rust heap and an instance that is embedded inside a python object. By kngwyu in [#335](https://github.com/PyO3/pyo3/pull/335)
- Added `FromPy` and `IntoPy` which are equivalent to `From` and `Into` except that they require a gil token.
- Added `ManagedPyRef`, which should eventually replace `ToBorrowedObject`.
### Changed
- Renamed `PyObjectRef` to `PyAny` in #388
- Renamed `add_function` to `add_wrapped` as it now also supports modules.
- Renamed `#[pymodinit]` to `#[pymodule]`
- `py.init(|| value)` becomes `Py::new(value)`
- `py.init_ref(|| value)` becomes `PyRef::new(value)`
- `py.init_mut(|| value)` becomes `PyRefMut::new(value)`.
- `PyRawObject::init` is now infallible, e.g. it returns `()` instead of `PyResult<()>`.
- Renamed `py_exception!` to `create_exception!` and refactored the error macros.
- Renamed `wrap_function!` to `wrap_pyfunction!`
- Renamed `#[prop(get, set)]` to `#[pyo3(get, set)]`
- `#[pyfunction]` now supports the same arguments as `#[pyfn()]`
- Some macros now emit proper spanned errors instead of panics.
- Migrated to the 2018 edition
- `crate::types::exceptions` moved to `crate::exceptions`
- Replace `IntoPyTuple` with `IntoPy>`.
- `IntoPyPointer` and `ToPyPointer` moved into the crate root.
- `class::CompareOp` moved into `class::basic::CompareOp`
- PyTypeObject is now a direct subtrait PyTypeCreate, removing the old cyclical implementation in [#350](https://github.com/PyO3/pyo3/pull/350)
- Add `PyList::{sort, reverse}` by chr1sj0nes in [#357](https://github.com/PyO3/pyo3/pull/357) and [#358](https://github.com/PyO3/pyo3/pull/358)
- Renamed the `typeob` module to `type_object`
### Removed
- `PyToken` was removed due to unsoundness (See [#94](https://github.com/PyO3/pyo3/issues/94)).
- Removed the unnecessary type parameter from `PyObjectAlloc`
- `NoArgs`. Just use an empty tuple
- `PyObjectWithGIL`. `PyNativeType` is sufficient now that PyToken is removed.
### Fixed
- A soudness hole where every instances of a `#[pyclass]` struct was considered to be part of a python object, even though you can create instances that are not part of the python heap. This was fixed through `PyRef` and `PyRefMut`.
- Fix kwargs support in [#328](https://github.com/PyO3/pyo3/pull/328).
- Add full support for `__dict__` in [#403](https://github.com/PyO3/pyo3/pull/403).
## [0.5.3] - 2019-01-04
### Fixed
- Fix memory leak in ArrayList by kngwyu [#316](https://github.com/PyO3/pyo3/pull/316)
## [0.5.2] - 2018-11-25
### Fixed
- Fix indeterministic segfaults when creating many objects by kngwyu in [#281](https://github.com/PyO3/pyo3/pull/281)
## [0.5.1] - 2018-11-24
Yanked
## [0.5.0] - 2018-11-11
### Added
- `#[pyclass]` objects can now be returned from rust functions
- `PyComplex` by kngwyu in [#226](https://github.com/PyO3/pyo3/pull/226)
- `PyDict::from_sequence`, equivalent to `dict([(key, val), ...])`
- Bindings for the `datetime` standard library types: `PyDate`, `PyTime`, `PyDateTime`, `PyTzInfo`, `PyDelta` with associated `ffi` types, by pganssle [#200](https://github.com/PyO3/pyo3/pull/200).
- `PyString`, `PyUnicode`, and `PyBytes` now have an `as_bytes` method that returns `&[u8]`.
- `PyObjectProtocol::get_type_ptr` by ijl in [#242](https://github.com/PyO3/pyo3/pull/242)
### Changed
- Removes the types from the root module and the prelude. They now live in `pyo3::types` instead.
- All exceptions are constructed with `py_err` instead of `new`, as they return `PyErr` and not `Self`.
- `as_mut` and friends take and `&mut self` instead of `&self`
- `ObjectProtocol::call` now takes an `Option<&PyDict>` for the kwargs instead of an `IntoPyDictPointer`.
- `IntoPyDictPointer` was replace by `IntoPyDict` which doesn't convert `PyDict` itself anymore and returns a `PyDict` instead of `*mut PyObject`.
- `PyTuple::new` now takes an `IntoIterator` instead of a slice
- Updated to syn 0.15
- Split `PyTypeObject` into `PyTypeObject` without the create method and `PyTypeCreate` with requires `PyObjectAlloc + PyTypeInfo + Sized`.
- Ran `cargo edition --fix` which prefixed path with `crate::` for rust 2018
- Renamed `async` to `pyasync` as async will be a keyword in the 2018 edition.
- Starting to use `NonNull<*mut PyObject>` for Py and PyObject by ijl [#260](https://github.com/PyO3/pyo3/pull/260)
### Removed
- Removed most entries from the prelude. The new prelude is small and clear.
- Slowly removing specialization uses
- `PyString`, `PyUnicode`, and `PyBytes` no longer have a `data` method
(replaced by `as_bytes`) and `PyStringData` has been removed.
- The pyobject_extract macro
### Fixed
- Added an explanation that the GIL can temporarily be released even while holding a GILGuard.
- Lots of clippy errors
- Fix segfault on calling an unknown method on a PyObject
- Work around a [bug](https://github.com/rust-lang/rust/issues/55380) in the rust compiler by kngwyu [#252](https://github.com/PyO3/pyo3/pull/252)
- Fixed a segfault with subclassing pyo3 create classes and using `__class__` by kngwyu [#263](https://github.com/PyO3/pyo3/pull/263)
## [0.4.1] - 2018-08-20
### Changed
- PyTryFrom's error is always to `PyDowncastError`
### Fixed
- Fixed compilation on nightly since `use_extern_macros` was stabilized
### Removed
- The pyobject_downcast macro
## [0.4.0] - 2018-07-30
### Changed
- Merged both examples into one
- Rustfmt all the things :heavy_check_mark:
- Switched to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
### Removed
- Conversions from tuples to PyDict due to [rust-lang/rust#52050](https://github.com/rust-lang/rust/issues/52050)
## [0.3.2] - 2018-07-22
### Changed
- Replaced `concat_idents` with mashup
## [0.3.1] - 2018-07-18
### Fixed
- Fixed scoping bug in pyobject_native_type that would break rust-numpy
## [0.3.0] - 2018-07-18
### Added
- A few internal macros became part of the public api ([#155](https://github.com/PyO3/pyo3/pull/155), [#186](https://github.com/PyO3/pyo3/pull/186))
- Always clone in getters. This allows using the get-annotation on all Clone-Types
### Changed
- Upgraded to syn 0.14 which means much better error messages :tada:
- 128 bit integer support by [kngwyu](https://github.com/kngwyu) ([#137](https://github.com/PyO3/pyo3/pull/173))
- `proc_macro` has been stabilized on nightly ([rust-lang/rust#52081](https://github.com/rust-lang/rust/pull/52081)). This means that we can remove the `proc_macro` feature, but now we need the `use_extern_macros` from the 2018 edition instead.
- All proc macro are now prefixed with `py` and live in the prelude. This means you can use `#[pyclass]`, `#[pymethods]`, `#[pyproto]`, `#[pyfunction]` and `#[pymodinit]` directly, at least after a `use pyo3::prelude::*`. They were also moved into a module called `proc_macro`. You shouldn't use `#[pyo3::proc_macro::pyclass]` or other longer paths in attributes because `proc_macro_path_invoc` isn't going to be stabilized soon.
- Renamed the `base` option in the `pyclass` macro to `extends`.
- `#[pymodinit]` uses the function name as module name, unless the name is overridden with `#[pymodinit(name)]`
- The guide is now properly versioned.
## [0.2.7] - 2018-05-18
### Fixed
- Fix nightly breakage with proc_macro_path
## [0.2.6] - 2018-04-03
### Fixed
- Fix compatibility with TryFrom trait #137
## [0.2.5] - 2018-02-21
### Added
- CPython 3.7 support
### Fixed
- Embedded CPython 3.7b1 crashes on initialization #110
- Generated extension functions are weakly typed #108
- call_method\* crashes when the method does not exist #113
- Allow importing exceptions from nested modules #116
## [0.2.4] - 2018-01-19
### Added
- Allow to get mutable ref from PyObject #106
- Drop `RefFromPyObject` trait
- Add Python::register_any method
### Fixed
- Fix impl `FromPyObject` for `Py`
- Mark method that work with raw pointers as unsafe #95
## [0.2.3] - 11-27-2017
### Changed
- Rustup to 1.23.0-nightly 2017-11-07
### Fixed
- Proper `c_char` usage #93
### Removed
- Remove use of now unneeded 'AsciiExt' trait
## [0.2.2] - 09-26-2017
### Changed
- Rustup to 1.22.0-nightly 2017-09-30
## [0.2.1] - 09-26-2017
### Fixed
- Fix rustc const_fn nightly breakage
## [0.2.0] - 08-12-2017
### Added
- Added inheritance support #15
- Added weakref support #56
- Added subclass support #64
- Added `self.__dict__` support #68
- Added `pyo3::prelude` module #70
- Better `Iterator` support for PyTuple, PyList, PyDict #75
- Introduce IntoPyDictPointer similar to IntoPyTuple #69
### Changed
- Allow to add gc support without implementing PyGCProtocol #57
- Refactor `PyErr` implementation. Drop `py` parameter from constructor.
## [0.1.0] - 07-23-2017
### Added
- Initial release
[Unreleased]: https://github.com/pyo3/pyo3/compare/v0.28.2...HEAD
[0.28.2]: https://github.com/pyo3/pyo3/compare/v0.28.1...v0.28.2
[0.28.1]: https://github.com/pyo3/pyo3/compare/v0.28.0...v0.28.1
[0.28.0]: https://github.com/pyo3/pyo3/compare/v0.27.2...v0.28.0
[0.27.2]: https://github.com/pyo3/pyo3/compare/v0.27.1...v0.27.2
[0.27.1]: https://github.com/pyo3/pyo3/compare/v0.27.0...v0.27.1
[0.27.0]: https://github.com/pyo3/pyo3/compare/v0.26.0...v0.27.0
[0.26.0]: https://github.com/pyo3/pyo3/compare/v0.25.1...v0.26.0
[0.25.1]: https://github.com/pyo3/pyo3/compare/v0.25.0...v0.25.1
[0.25.0]: https://github.com/pyo3/pyo3/compare/v0.24.2...v0.25.0
[0.24.2]: https://github.com/pyo3/pyo3/compare/v0.24.1...v0.24.2
[0.24.1]: https://github.com/pyo3/pyo3/compare/v0.24.0...v0.24.1
[0.24.0]: https://github.com/pyo3/pyo3/compare/v0.23.5...v0.24.0
[0.23.5]: https://github.com/pyo3/pyo3/compare/v0.23.4...v0.23.5
[0.23.4]: https://github.com/pyo3/pyo3/compare/v0.23.3...v0.23.4
[0.23.3]: https://github.com/pyo3/pyo3/compare/v0.23.2...v0.23.3
[0.23.2]: https://github.com/pyo3/pyo3/compare/v0.23.1...v0.23.2
[0.23.1]: https://github.com/pyo3/pyo3/compare/v0.23.0...v0.23.1
[0.23.0]: https://github.com/pyo3/pyo3/compare/v0.22.5...v0.23.0
[0.22.5]: https://github.com/pyo3/pyo3/compare/v0.22.4...v0.22.5
[0.22.4]: https://github.com/pyo3/pyo3/compare/v0.22.3...v0.22.4
[0.22.3]: https://github.com/pyo3/pyo3/compare/v0.22.2...v0.22.3
[0.22.2]: https://github.com/pyo3/pyo3/compare/v0.22.1...v0.22.2
[0.22.1]: https://github.com/pyo3/pyo3/compare/v0.22.0...v0.22.1
[0.22.0]: https://github.com/pyo3/pyo3/compare/v0.21.2...v0.22.0
[0.21.2]: https://github.com/pyo3/pyo3/compare/v0.21.1...v0.21.2
[0.21.1]: https://github.com/pyo3/pyo3/compare/v0.21.0...v0.21.1
[0.21.0]: https://github.com/pyo3/pyo3/compare/v0.20.3...v0.21.0
[0.21.0-beta.0]: https://github.com/pyo3/pyo3/compare/v0.20.3...v0.21.0-beta.0
[0.20.3]: https://github.com/pyo3/pyo3/compare/v0.20.2...v0.20.3
[0.20.2]: https://github.com/pyo3/pyo3/compare/v0.20.1...v0.20.2
[0.20.1]: https://github.com/pyo3/pyo3/compare/v0.20.0...v0.20.1
[0.20.0]: https://github.com/pyo3/pyo3/compare/v0.19.2...v0.20.0
[0.19.2]: https://github.com/pyo3/pyo3/compare/v0.19.1...v0.19.2
[0.19.1]: https://github.com/pyo3/pyo3/compare/v0.19.0...v0.19.1
[0.19.0]: https://github.com/pyo3/pyo3/compare/v0.18.3...v0.19.0
[0.18.3]: https://github.com/pyo3/pyo3/compare/v0.18.2...v0.18.3
[0.18.2]: https://github.com/pyo3/pyo3/compare/v0.18.1...v0.18.2
[0.18.1]: https://github.com/pyo3/pyo3/compare/v0.18.0...v0.18.1
[0.18.0]: https://github.com/pyo3/pyo3/compare/v0.17.3...v0.18.0
[0.17.3]: https://github.com/pyo3/pyo3/compare/v0.17.2...v0.17.3
[0.17.2]: https://github.com/pyo3/pyo3/compare/v0.17.1...v0.17.2
[0.17.1]: https://github.com/pyo3/pyo3/compare/v0.17.0...v0.17.1
[0.17.0]: https://github.com/pyo3/pyo3/compare/v0.16.6...v0.17.0
[0.16.6]: https://github.com/pyo3/pyo3/compare/v0.16.5...v0.16.6
[0.16.5]: https://github.com/pyo3/pyo3/compare/v0.16.4...v0.16.5
[0.16.4]: https://github.com/pyo3/pyo3/compare/v0.16.3...v0.16.4
[0.16.3]: https://github.com/pyo3/pyo3/compare/v0.16.2...v0.16.3
[0.16.2]: https://github.com/pyo3/pyo3/compare/v0.16.1...v0.16.2
[0.16.1]: https://github.com/pyo3/pyo3/compare/v0.16.0...v0.16.1
[0.16.0]: https://github.com/pyo3/pyo3/compare/v0.15.1...v0.16.0
[0.15.2]: https://github.com/pyo3/pyo3/compare/v0.15.1...v0.15.2
[0.15.1]: https://github.com/pyo3/pyo3/compare/v0.15.0...v0.15.1
[0.15.0]: https://github.com/pyo3/pyo3/compare/v0.14.5...v0.15.0
[0.14.5]: https://github.com/pyo3/pyo3/compare/v0.14.4...v0.14.5
[0.14.4]: https://github.com/pyo3/pyo3/compare/v0.14.3...v0.14.4
[0.14.3]: https://github.com/pyo3/pyo3/compare/v0.14.2...v0.14.3
[0.14.2]: https://github.com/pyo3/pyo3/compare/v0.14.1...v0.14.2
[0.14.1]: https://github.com/pyo3/pyo3/compare/v0.14.0...v0.14.1
[0.14.0]: https://github.com/pyo3/pyo3/compare/v0.13.2...v0.14.0
[0.13.2]: https://github.com/pyo3/pyo3/compare/v0.13.1...v0.13.2
[0.13.1]: https://github.com/pyo3/pyo3/compare/v0.13.0...v0.13.1
[0.13.0]: https://github.com/pyo3/pyo3/compare/v0.12.4...v0.13.0
[0.12.4]: https://github.com/pyo3/pyo3/compare/v0.12.3...v0.12.4
[0.12.3]: https://github.com/pyo3/pyo3/compare/v0.12.2...v0.12.3
[0.12.2]: https://github.com/pyo3/pyo3/compare/v0.12.1...v0.12.2
[0.12.1]: https://github.com/pyo3/pyo3/compare/v0.12.0...v0.12.1
[0.12.0]: https://github.com/pyo3/pyo3/compare/v0.11.1...v0.12.0
[0.11.1]: https://github.com/pyo3/pyo3/compare/v0.11.0...v0.11.1
[0.11.0]: https://github.com/pyo3/pyo3/compare/v0.10.1...v0.11.0
[0.10.1]: https://github.com/pyo3/pyo3/compare/v0.10.0...v0.10.1
[0.10.0]: https://github.com/pyo3/pyo3/compare/v0.9.2...v0.10.0
[0.9.2]: https://github.com/pyo3/pyo3/compare/v0.9.1...v0.9.2
[0.9.1]: https://github.com/pyo3/pyo3/compare/v0.9.0...v0.9.1
[0.9.0]: https://github.com/pyo3/pyo3/compare/v0.8.5...v0.9.0
[0.8.5]: https://github.com/pyo3/pyo3/compare/v0.8.4...v0.8.5
[0.8.4]: https://github.com/pyo3/pyo3/compare/v0.8.3...v0.8.4
[0.8.3]: https://github.com/pyo3/pyo3/compare/v0.8.2...v0.8.3
[0.8.2]: https://github.com/pyo3/pyo3/compare/v0.8.1...v0.8.2
[0.8.1]: https://github.com/pyo3/pyo3/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/pyo3/pyo3/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/pyo3/pyo3/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/pyo3/pyo3/compare/v0.5.3...v0.6.0
[0.5.3]: https://github.com/pyo3/pyo3/compare/v0.5.2...v0.5.3
[0.5.2]: https://github.com/pyo3/pyo3/compare/v0.5.1...v0.5.2
[0.5.1]: https://github.com/pyo3/pyo3/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/pyo3/pyo3/compare/v0.4.1...v0.5.0
[0.4.1]: https://github.com/pyo3/pyo3/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/pyo3/pyo3/compare/v0.3.2...v0.4.0
[0.3.2]: https://github.com/pyo3/pyo3/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/pyo3/pyo3/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/pyo3/pyo3/compare/v0.2.7...v0.3.0
[0.2.7]: https://github.com/pyo3/pyo3/compare/v0.2.6...v0.2.7
[0.2.6]: https://github.com/pyo3/pyo3/compare/v0.2.5...v0.2.6
[0.2.5]: https://github.com/pyo3/pyo3/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/pyo3/pyo3/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/pyo3/pyo3/compare/v0.2.2...v0.2.3
[0.2.2]: https://github.com/pyo3/pyo3/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/pyo3/pyo3/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/pyo3/pyo3/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/PyO3/pyo3/tree/0.1.0
================================================
FILE: CITATION.cff
================================================
cff-version: 1.2.0
title: PyO3
message: >-
If you use this software as part of a publication and wish to cite
it, please use the metadata from this file.
type: software
authors:
- name: PyO3 Project and Contributors
website: https://github.com/PyO3
license:
- Apache-2.0
- MIT
================================================
FILE: Cargo.toml
================================================
[package]
name = "pyo3"
version = "0.28.2"
description = "Bindings to Python interpreter"
authors = ["PyO3 Project and Contributors "]
readme = "README.md"
keywords = ["pyo3", "python", "cpython", "ffi"]
homepage = "https://github.com/pyo3/pyo3"
repository = "https://github.com/pyo3/pyo3"
documentation = "https://docs.rs/crate/pyo3/"
categories = ["api-bindings", "development-tools::ffi"]
license = "MIT OR Apache-2.0"
exclude = [
"/.gitignore",
".cargo/config",
"/codecov.yml",
"/Makefile",
"/pyproject.toml",
"/noxfile.py",
"/.github",
"/tests/test_compile_error.rs",
"/tests/ui",
]
edition = "2021"
rust-version.workspace = true
[dependencies]
libc = "0.2.62"
once_cell = "1.21"
# ffi bindings to the python interpreter, split into a separate crate so they can be used independently
pyo3-ffi = { path = "pyo3-ffi", version = "=0.28.2" }
# support crate for macros feature
pyo3-macros = { path = "pyo3-macros", version = "=0.28.2", optional = true }
# support crate for multiple-pymethods feature
inventory = { version = "0.3.5", optional = true }
# crate integrations that can be added using the eponymous features
anyhow = { version = "1.0.1", optional = true }
bigdecimal = { version = "0.4.7", optional = true }
bytes = { version = "1.10", optional = true }
chrono = { version = "0.4.25", default-features = false, optional = true }
chrono-tz = { version = ">= 0.10, < 0.11", default-features = false, optional = true }
either = { version = "1.9", optional = true }
eyre = { version = ">= 0.6.8, < 0.7", optional = true }
hashbrown = { version = ">= 0.15.0, < 0.17", optional = true, default-features = false }
indexmap = { version = ">= 2.5.0, < 3", optional = true }
jiff-02 = { package = "jiff", version = "0.2", optional = true }
num-bigint = { version = "0.4.4", optional = true }
num-complex = { version = ">= 0.4.6, < 0.5", optional = true }
num-rational = { version = "0.4.1", optional = true }
num-traits = { version = "0.2.16", optional = true }
ordered-float = { version = "5.0.0", default-features = false, optional = true }
rust_decimal = { version = "1.15", default-features = false, optional = true }
time = { version = "0.3.38", default-features = false, optional = true }
serde = { version = "1.0", optional = true }
smallvec = { version = "1.0", optional = true }
uuid = { version = "1.12.0", optional = true }
lock_api = { version = "0.4", optional = true }
parking_lot = { version = "0.12", optional = true }
iana-time-zone = { version = "0.1", optional = true, features = ["fallback"]}
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
portable-atomic = "1.0"
[dev-dependencies]
assert_approx_eq = "1.1.0"
chrono = "0.4.25"
chrono-tz = ">= 0.10, < 0.11"
trybuild = ">=1.0.115"
proptest = { version = "1.0", default-features = false, features = ["std"] }
send_wrapper = "0.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.61"
rayon = "1.6.1"
futures = "0.3.28"
tempfile = "3.12.0"
static_assertions = "1.1.0"
uuid = { version = "1.10.0", features = ["v4"] }
parking_lot = { version = "0.12.3", features = ["arc_lock"] }
[build-dependencies]
pyo3-build-config = { path = "pyo3-build-config", version = "=0.28.2", features = ["resolve-config"] }
[features]
default = ["macros"]
# Enables support for `async fn` for `#[pyfunction]` and `#[pymethods]`.
experimental-async = ["macros", "pyo3-macros/experimental-async"]
# Enables pyo3::inspect module and additional type information on FromPyObject
# and IntoPy traits
experimental-inspect = ["pyo3-macros/experimental-inspect"]
# Enables macros: #[pyclass], #[pymodule], #[pyfunction] etc.
macros = ["pyo3-macros"]
# Enables multiple #[pymethods] per #[pyclass]
multiple-pymethods = ["inventory", "pyo3-macros/multiple-pymethods"]
# Deprecated: use the `PYO3_BUILD_EXTENSION_MODULE` environment variable when
# building a Python extension module (set automatically by `setuptools-rust` and
# `maturin`).
extension-module = ["pyo3-ffi/extension-module"]
# Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for more.
abi3 = ["pyo3-build-config/abi3", "pyo3-ffi/abi3"]
# With abi3, we can manually set the minimum Python version.
abi3-py37 = ["abi3-py38", "pyo3-build-config/abi3-py37", "pyo3-ffi/abi3-py37"]
abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38", "pyo3-ffi/abi3-py38"]
abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39", "pyo3-ffi/abi3-py39"]
abi3-py310 = ["abi3-py311", "pyo3-build-config/abi3-py310", "pyo3-ffi/abi3-py310"]
abi3-py311 = ["abi3-py312", "pyo3-build-config/abi3-py311", "pyo3-ffi/abi3-py311"]
abi3-py312 = ["abi3-py313", "pyo3-build-config/abi3-py312", "pyo3-ffi/abi3-py312"]
abi3-py313 = ["abi3-py314", "pyo3-build-config/abi3-py313", "pyo3-ffi/abi3-py313"]
abi3-py314 = ["abi3", "pyo3-build-config/abi3-py314", "pyo3-ffi/abi3-py314"]
# deprecated: no longer needed, raw-dylib is used instead
generate-import-lib = ["pyo3-ffi/generate-import-lib"]
# Changes `Python::attach` to automatically initialize the Python interpreter if needed.
auto-initialize = []
# Enables `Clone`ing references to Python objects `Py` which panics if the
# thread is not attached to the Python interpreter.
py-clone = []
# Adds `OnceExt` and `MutexExt` implementations to the `parking_lot` types
parking_lot = ["dep:parking_lot", "lock_api"]
arc_lock = ["lock_api", "lock_api/arc_lock", "parking_lot?/arc_lock"]
num-bigint = ["dep:num-bigint", "dep:num-traits"]
bigdecimal = ["dep:bigdecimal", "num-bigint"]
chrono-local = ["chrono/clock", "dep:iana-time-zone"]
# Optimizes PyObject to Vec conversion and so on.
nightly = []
# Activates all additional features
# This is mostly intended for testing purposes - activating *all* of these isn't particularly useful.
full = [
"macros",
# "multiple-pymethods", # Not supported by wasm
"anyhow",
"arc_lock",
"bigdecimal",
"bytes",
"chrono",
"chrono-local",
"chrono-tz",
"either",
"experimental-async",
"experimental-inspect",
"eyre",
"hashbrown",
"indexmap",
"jiff-02",
"lock_api",
"num-bigint",
"num-complex",
"num-rational",
"ordered-float",
"parking_lot",
"py-clone",
"rust_decimal",
"serde",
"smallvec",
"time",
"uuid",
]
[workspace]
members = [
"pyo3-ffi",
"pyo3-build-config",
"pyo3-macros",
"pyo3-macros-backend",
"pyo3-introspection",
"pytests",
"examples",
]
package.rust-version = "1.83"
[package.metadata.docs.rs]
no-default-features = true
features = ["full"]
rustdoc-args = ["--cfg", "docsrs"]
[workspace.lints.clippy]
checked_conversions = "warn"
dbg_macro = "warn"
explicit_into_iter_loop = "warn"
explicit_iter_loop = "warn"
filter_map_next = "warn"
flat_map_option = "warn"
let_unit_value = "warn"
manual_assert = "warn"
manual_ok_or = "warn"
todo = "warn"
# TODO: make this "warn"
# https://github.com/PyO3/pyo3/issues/5487
undocumented_unsafe_blocks = "allow"
unnecessary_wraps = "warn"
useless_transmute = "warn"
used_underscore_binding = "warn"
[workspace.lints.rust]
elided_lifetimes_in_paths = "warn"
invalid_doc_attributes = "warn"
rust_2018_idioms = { level = "warn", priority = -1 }
rust_2021_prelude_collisions = "warn"
unused_lifetimes = "warn"
unsafe_op_in_unsafe_fn = "warn"
[workspace.lints.rustdoc]
broken_intra_doc_links = "warn"
bare_urls = "warn"
[lints]
workspace = true
================================================
FILE: Code-of-Conduct.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: Contributing.md
================================================
# Contributing
Thank you for your interest in contributing to PyO3! All are welcome - please consider reading our [Code of Conduct](https://github.com/PyO3/pyo3/blob/main/Code-of-Conduct.md) to keep our community positive and inclusive.
If you are searching for ideas how to contribute, proceed to the ["Getting started contributing"](#getting-started-contributing) section. If you have found a specific issue to contribute to and need information about the development process, you may find the section ["Writing pull requests"](#writing-pull-requests) helpful.
If you want to become familiar with the codebase, see
[Architecture.md](https://github.com/PyO3/pyo3/blob/main/Architecture.md).
## Getting started contributing
Please join in with any part of PyO3 which interests you. We use GitHub issues to record all bugs and ideas. Feel free to request an issue to be assigned to you if you want to work on it.
You can browse the API of the non-public parts of PyO3 [here](https://pyo3.netlify.app/internal/doc/pyo3/index.html).
The following sections also contain specific ideas on where to start contributing to PyO3.
## Setting up a development environment
To work and develop PyO3, you need Python & Rust installed on your system.
* We encourage the use of [rustup](https://rustup.rs/) to be able to select and choose specific toolchains based on the project.
* [Pyenv](https://github.com/pyenv/pyenv) is also highly recommended for being able to choose a specific Python version.
* [virtualenv](https://virtualenv.pypa.io/en/latest/) can also be used with or without Pyenv to use specific installed Python versions.
* [`nox`][nox] is used to automate many of our CI tasks.
### Testing, linting, etc. with nox
[`Nox`][nox] is used to automate many of our CI tasks and can be used locally to handle verification tasks as you code. We recommend running these actions via nox to make use of our preferred configuration options. You can install nox into your global python with pip: `pip install nox` or (recommended) with [`pipx`][pipx] `pip install pipx`, `pipx install nox`
The main nox commands we have implemented are:
* `nox -s test` will run the full suite of recommended rust and python tests (>10 minutes)
* `nox -s test-rust -- skip-full` will run a short suite of rust tests (2-3 minutes)
* `nox -s ruff` will check python linting and apply standard formatting rules
* `nox -s rustfmt` will check basic rust linting and apply standard formatting rules
* `nox -s rumdl` will check the markdown in the guide
* `nox -s clippy` will run clippy to make recommendations on rust style
* `nox -s bench` will benchmark your rust code
* `nox -s codspeed` will run our suite of rust and python performance tests
* `nox -s coverage` will analyse test coverage and output `coverage.json` (alternatively: `nox -s coverage lcov` outputs `lcov.info`)
* `nox -s check-guide` will use [`lychee`][lychee] to check all the links in the guide and doc comments.
Use `nox -l` to list the full set of subcommands you can run.
#### UI Tests
PyO3 uses [`trybuild`][trybuild] to develop UI tests to capture error messages from the Rust compiler for some of the macro functionality.
The Rust compiler's error output differs depending on whether the `rust-src` component is installed. PyO3's CI has `rust-src` installed, so you need it locally for your UI test output to match:
```bash
rustup component add rust-src
```
Because there are several feature combinations for these UI tests, when updating them all (e.g. for a new Rust compiler version) it may be helpful to use the `update-ui-tests` nox session:
```bash
nox -s update-ui-tests
```
## Ways to help
### Help users identify bugs
The [PyO3 Discord server](https://discord.gg/33kcChzH7f) is very active with users who are new to PyO3, and often completely new to Rust. Helping them debug is a great way to get experience with the PyO3 codebase.
Helping others often reveals bugs, documentation weaknesses, and missing APIs. It's a good idea to open GitHub issues for these immediately so the resolution can be designed and implemented!
### Implement issues ready for development
Issues where the solution is clear and work is not in progress use the [needs-implementer](https://github.com/PyO3/pyo3/issues?q=is%3Aissue+is%3Aopen+label%3Aneeds-implementer) label.
Don't be afraid if the solution is not clear to you! The core PyO3 contributors will be happy to mentor you through any questions you have to help you write the solution.
### Help write great docs
PyO3 has a user guide (using mdbook) as well as the usual Rust API docs. The aim is for both of these to be detailed, easy to understand, and up-to-date. Pull requests are always welcome to fix typos, change wording, add examples, etc.
There are some specific areas of focus where help is currently needed for the documentation:
- Issues requesting documentation improvements are tracked with the [documentation](https://github.com/PyO3/pyo3/issues?q=is%3Aissue+is%3Aopen+label%3Adocumentation) label.
- Not all APIs had docs or examples when they were made. The goal is to have documentation on all PyO3 APIs ([#306](https://github.com/PyO3/pyo3/issues/306)). If you see an API lacking a doc, please write one and open a PR!
To build the docs (including all features), install [`nox`][nox] and then run
```shell
nox -s docs -- open
```
#### Doctests
We use lots of code blocks in our docs. Run `cargo test --doc` when making changes to check that
the doctests still work, or `cargo test` to run all the Rust tests including doctests. See
https://doc.rust-lang.org/rustdoc/documentation-tests.html for a guide on doctests.
#### Building the guide
You can preview the user guide by building it locally with `mdbook`.
First, install [`mdbook`][mdbook], the [`mdbook-tabs`][mdbook-tabs] plugin and [`nox`][nox]. Then, run
```shell
nox -s build-guide -- --open
```
To check all links in the guide are valid, also install [`lychee`][lychee] and use the `check-guide` session instead:
```shell
nox -s check-guide
```
### Help design the next PyO3
Issues which don't yet have a clear solution use the [needs-design](https://github.com/PyO3/pyo3/issues?q=is%3Aissue+is%3Aopen+label%3Aneeds-design) label.
If any of these issues interest you, please join in with the conversation on the issue! All opinions are valued, and if you're interested in going further with e.g. draft PRs to experiment with API designs, even better!
### Review pull requests
Everybody is welcome to submit comments on open PRs. Please help ensure new PyO3 APIs are safe, performant, tidy, and easy to use!
## Writing pull requests
Here are a few things to note when you are writing PRs.
### Testing and Continuous Integration
The PyO3 repo uses GitHub Actions.
PRs are blocked from merging if CI is not successful.
Formatting, linting and tests are checked for all Rust and Python code (the pipeline will abort early if formatting fails to save resources).
In addition, all warnings in Rust code are disallowed (using `RUSTFLAGS="-D warnings"`).
Tests run with all supported Python versions with the latest stable Rust compiler, as well as for Python 3.9 with the minimum supported Rust version.
If you are adding a new feature, you should add it to the `full` feature in our *Cargo.toml** so that it is tested in CI.
You can run the CI pipeline components yourself with `nox`, see [the testing section above](#testing-linting-etc-with-nox).
### Documenting changes
We use [towncrier](https://towncrier.readthedocs.io/en/stable/index.html) to generate a CHANGELOG for each release.
To include your changes in the release notes, you should create one (or more) news items in the `newsfragments` directory. Valid news items should be saved as `..md` where `` is the pull request number and `` is one of the following:
- `packaging` - for dependency changes and Python / Rust version compatibility changes
- `added` - for new features
- `changed` - for features which already existed but have been altered or deprecated
- `removed` - for features which have been removed
- `fixed` - for "changed" features which were classed as a bugfix
Docs-only PRs do not need news items; start your PR title with `docs:` to skip the check.
### Style guide
#### Generic code
PyO3 has a lot of generic APIs to increase usability. These can come at the cost of generic code bloat. Where reasonable, try to implement a concrete sub-portion of generic functions. There are two forms of this:
- If the concrete sub-portion doesn't benefit from re-use by other functions, name it `inner` and keep it as a local to the function.
- If the concrete sub-portion is re-used by other functions, preferably name it `_foo` and place it directly below `foo` in the source code (where `foo` is the original generic function).
#### FFI calls
PyO3 makes a lot of FFI calls to Python's C API using raw pointers. Where possible try to avoid using pointers-to-temporaries in expressions:
```rust
// dangerous
pyo3::ffi::Something(name.to_object(py).as_ptr());
// because the following refactoring is a use-after-free error:
let name = name.to_object(py).as_ptr();
pyo3::ffi::Something(name)
```
Instead, prefer to bind the safe owned `PyObject` wrapper before passing to ffi functions:
```rust
let name: PyObject = name.to_object(py);
pyo3::ffi::Something(name.as_ptr())
// name will automatically be freed when it falls out of scope
```
## Python and Rust version support policy
PyO3 aims to keep sufficient compatibility to make packaging Python extensions built with PyO3 feasible on most common package managers.
To keep package maintainers' lives simpler, PyO3 will commit, wherever possible, to only adjust minimum supported Rust and Python versions at the same time. This bump will only come in an `0.x` release, roughly once per year, after the oldest supported Python version reaches its end-of-life. (Check https://endoflife.date/python for a clear timetable on these.)
Below are guidelines on what compatibility all PRs are expected to deliver for each language.
### Python
PyO3 supports all officially supported Python versions, as well as the latest PyPy3 release. All of these versions are tested in CI.
#### Adding support for new CPython versions
If you plan to add support for a pre-release version of CPython, here's a (non-exhaustive) checklist:
- [ ] Wait until the last alpha release (usually alpha7), since ABI is not guaranteed until the first beta release
- [ ] Add prerelease_ver-dev (e.g. `3.14-dev`) to `.github/workflows/ci.yml`, and bump version in `noxfile.py`, `pyo3-ffi/Cargo.toml` under `max-version` within `[package.metadata.cpython]`, and `max` within `pyo3-ffi/build.rs`
- [ ] Add a new abi3-prerelease feature for the version (e.g. `abi3-py314`)
- In `pyo3-build-config/Cargo.toml`, set abi3-most_current_stable to ["abi3-prerelease"] and abi3-prerelease to ["abi3"]
- In `pyo3-ffi/Cargo.toml`, set abi3-most_current_stable to ["abi3-prerelease", "pyo3-build-config/abi3-most_current_stable"] and abi3-prerelease to ["abi3", "pyo3-build-config/abi3-prerelease"]
- In `Cargo.toml`, set abi3-most_current_stable to ["abi3-prerelease", "pyo3-ffi/abi3-most_current_stable"] and abi3-prerelease to ["abi3", "pyo3-ffi/abi3-prerelease"]
- [ ] Use `#[cfg(Py_prerelease])` (e.g. `#[cfg(Py_3_14)]`) and `#[cfg(not(Py_prerelease]))` to indicate changes between the stable branches of CPython and the pre-release
- [ ] Do not add a Rust binding to any function, struct, or global variable prefixed with `_` in CPython's headers
- [ ] Ping @ngoldbaum and @davidhewitt for assistance
### Rust
PyO3 aims to make use of up-to-date Rust language features to keep the implementation as efficient as possible.
The minimum Rust version supported will be decided when the release which bumps Python and Rust versions is made. At the time, the minimum Rust version will be set no higher than the lowest Rust version shipped in the current Debian, RHEL and Alpine Linux distributions.
CI tests both the most recent stable Rust version and the minimum supported Rust version. Because of Rust's stability guarantees this is sufficient to confirm support for all Rust versions in between.
## Benchmarking
PyO3 has two sets of benchmarks for evaluating some aspects of its performance. The benchmark suite is currently very small - please open PRs with new benchmarks if you're interested in helping to expand it!
First, there are Rust-based benchmarks located in the `pyo3-benches` subdirectory. You can run these benchmarks with:
nox -s bench
Second, there is a Python-based benchmark contained in the `pytests` subdirectory. You can read more about it [here](https://github.com/PyO3/pyo3/tree/main/pytests).
## Code coverage
You can view what code is and isn't covered by PyO3's tests. We aim to have 100% coverage - please check coverage and add tests if you notice a lack of coverage!
- First, ensure the llvm-cov cargo plugin is installed. You may need to run the plugin through cargo once before using it with `nox`.
```shell
cargo install cargo-llvm-cov
cargo llvm-cov
```
- Then, generate an `lcov.info` file with
```shell
nox -s coverage -- lcov
```
You can install an IDE plugin to view the coverage. For example, if you use VSCode:
- Add the [coverage-gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters) plugin.
- Add these settings to VSCode's `settings.json`:
```json
{
"coverage-gutters.coverageFileNames": [
"lcov.info",
"cov.xml",
"coverage.xml",
],
"coverage-gutters.showLineCoverage": true
}
```
- You should now be able to see green highlights for code that is tested, and red highlights for code that is not tested.
## Sponsor this project
At the moment there is no official organisation that accepts sponsorship on PyO3's behalf. If you're seeking to provide significant funding to the PyO3 ecosystem, please reach out to us on [GitHub](https://github.com/PyO3/pyo3/issues/new) or [Discord](https://discord.gg/33kcChzH7f) and we can discuss.
In the meanwhile, some of our maintainers have personal GitHub sponsorship pages and would be grateful for your support:
- [davidhewitt](https://github.com/sponsors/davidhewitt)
- [messense](https://github.com/sponsors/messense)
[mdbook]: https://rust-lang.github.io/mdBook/cli/index.html
[mdbook-tabs]: https://mdbook-plugins.rustforweb.org/tabs.html
[lychee]: https://github.com/lycheeverse/lychee
[nox]: https://github.com/theacodes/nox
[pipx]: https://pipx.pypa.io/stable/
[trybuild]: https://github.com/dtolnay/trybuild
================================================
FILE: LICENSE-APACHE
================================================
Copyright (c) 2017-present PyO3 Project and Contributors. https://github.com/PyO3
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
================================================
FILE: LICENSE-MIT
================================================
Copyright (c) 2023-present PyO3 Project and Contributors. https://github.com/PyO3
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# PyO3
[](https://github.com/PyO3/pyo3/actions)
[](https://codspeed.io/PyO3/pyo3)
[](https://codecov.io/gh/PyO3/pyo3)
[](https://crates.io/crates/pyo3)
[](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
[](https://discord.gg/33kcChzH7f)
[](https://github.com/PyO3/pyo3/blob/main/Contributing.md)
[Rust](https://www.rust-lang.org/) bindings for [Python](https://www.python.org/), including tools for creating native Python extension modules. Running and interacting with Python code from a Rust binary is also supported.
- User Guide: [stable](https://pyo3.rs) | [main](https://pyo3.rs/main)
- API Documentation: [stable](https://docs.rs/pyo3/) | [main](https://pyo3.rs/main/doc)
## Usage
Requires Rust 1.83 or greater.
PyO3 supports the following Python distributions:
- CPython 3.7 or greater
- PyPy 7.3 (Python 3.11+)
- GraalPy 25.0 or greater (Python 3.12+)
You can use PyO3 to write a native Python module in Rust, or to embed Python in a Rust binary. The following sections explain each of these in turn.
### Using Rust from Python
PyO3 can be used to generate a native Python module. The easiest way to try this out for the first time is to use [`maturin`](https://github.com/PyO3/maturin). `maturin` is a tool for building and publishing Rust-based Python packages with minimal configuration. The following steps install `maturin`, use it to generate and build a new Python package, and then launch Python to import and execute a function from the package.
First, follow the commands below to create a new directory containing a new Python `virtualenv`, and install `maturin` into the virtualenv using Python's package manager, `pip`:
```bash
# (replace string_sum with the desired package name)
$ mkdir string_sum
$ cd string_sum
$ python -m venv .env
$ source .env/bin/activate
$ pip install maturin
```
Still inside this `string_sum` directory, now run `maturin init`. This will generate the new package source. When given the choice of bindings to use, select pyo3 bindings:
```bash
$ maturin init
✔ 🤷 What kind of bindings to use? · pyo3
✨ Done! New project created string_sum
```
The most important files generated by this command are `Cargo.toml` and `lib.rs`, which will look roughly like the following:
**`Cargo.toml`**
```toml
[package]
name = "string_sum"
version = "0.1.0"
edition = "2021"
[lib]
# The name of the native library. This is the name which will be used in Python to import the
# library (i.e. `import string_sum`). If you change this, you must also change the name of the
# `#[pymodule]` in `src/lib.rs`.
name = "string_sum"
# "cdylib" is necessary to produce a shared library for Python to import from.
#
# Downstream Rust code (including code in `bin/`, `examples/`, and `tests/`) will not be able
# to `use string_sum;` unless the "rlib" or "lib" crate type is also included, e.g.:
# crate-type = ["cdylib", "rlib"]
crate-type = ["cdylib"]
[dependencies]
pyo3 = "0.28.2"
```
**`src/lib.rs`**
```rust
/// A Python module implemented in Rust. The name of this module must match
/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
/// import the module.
#[pyo3::pymodule]
mod string_sum {
use pyo3::prelude::*;
/// Formats the sum of two numbers as string.
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult {
Ok((a + b).to_string())
}
}
```
Finally, run `maturin develop`. This will build the package and install it into the Python virtualenv previously created and activated. The package is then ready to be used from `python`:
```bash
$ maturin develop
# lots of progress output as maturin runs the compilation...
$ python
>>> import string_sum
>>> string_sum.sum_as_string(5, 20)
'25'
```
To make changes to the package, just edit the Rust source code and then re-run `maturin develop` to recompile.
To run this all as a single copy-and-paste, use the bash script below (replace `string_sum` in the first command with the desired package name):
```bash
mkdir string_sum && cd "$_"
python -m venv .env
source .env/bin/activate
pip install maturin
maturin init --bindings pyo3
maturin develop
```
If you want to be able to run `cargo test` or use this project in a Cargo workspace and are running into linker issues, there are some workarounds in [the FAQ](https://pyo3.rs/latest/faq.html#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror).
As well as with `maturin`, it is possible to build using [`setuptools-rust`](https://github.com/PyO3/setuptools-rust) or [manually](https://pyo3.rs/latest/building-and-distribution.html#manual-builds). Both offer more flexibility than `maturin` but require more configuration to get started.
### Using Python from Rust
To embed Python into a Rust binary, you need to ensure that your Python installation contains a shared library. The following steps demonstrate how to ensure this (for Ubuntu), and then give some example code which runs an embedded Python interpreter.
To install the Python shared library on Ubuntu:
```bash
sudo apt install python3-dev
```
To install the Python shared library on RPM based distributions (e.g. Fedora, Red Hat, SuSE), install the `python3-devel` package.
Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like this:
```toml
[dependencies.pyo3]
version = "0.28.2"
# Enabling this cargo feature will cause PyO3 to start a Python interpreter on first call to `Python::attach`
features = ["auto-initialize"]
```
Example program displaying the value of `sys.version` and the current user name:
```rust
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
fn main() -> PyResult<()> {
Python::attach(|py| {
let sys = py.import("sys")?;
let version: String = sys.getattr("version")?.extract()?;
let locals = [("os", py.import("os")?)].into_py_dict(py)?;
let code = c"os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
let user: String = py.eval(code, None, Some(&locals))?.extract()?;
println!("Hello {}, I'm Python {}", user, version);
Ok(())
})
}
```
The guide has [a section](https://pyo3.rs/latest/python-from-rust.html) with lots of examples
about this topic.
## Tools and libraries
- [maturin](https://github.com/PyO3/maturin) _Build and publish crates with pyo3, rust-cpython or cffi bindings as well as rust binaries as python packages_
- [setuptools-rust](https://github.com/PyO3/setuptools-rust) _Setuptools plugin for Rust support_.
- [pyo3-built](https://github.com/PyO3/pyo3-built) _Simple macro to expose metadata obtained with the [`built`](https://crates.io/crates/built) crate as a [`PyDict`](https://docs.rs/pyo3/*/pyo3/types/struct.PyDict.html)_
- [rust-numpy](https://github.com/PyO3/rust-numpy) _Rust binding of NumPy C-API_
- [dict-derive](https://github.com/gperinazzo/dict-derive) _Derive FromPyObject to automatically transform Python dicts into Rust structs_
- [pyo3-log](https://github.com/vorner/pyo3-log) _Bridge from Rust to Python logging_
- [pythonize](https://github.com/davidhewitt/pythonize) _Serde serializer for converting Rust objects to JSON-compatible Python objects_
- [pyo3-async-runtimes](https://github.com/PyO3/pyo3-async-runtimes) _Utilities for interoperability with Python's Asyncio library and Rust's async runtimes._
- [rustimport](https://github.com/mityax/rustimport) _Directly import Rust files or crates from Python, without manual compilation step. Provides pyo3 integration by default and generates pyo3 binding code automatically._
- [pyo3-arrow](https://crates.io/crates/pyo3-arrow) _Lightweight [Apache Arrow](https://arrow.apache.org/) integration for pyo3._
- [pyo3-bytes](https://crates.io/crates/pyo3-bytes) _Integration between [`bytes`](https://crates.io/crates/bytes) and pyo3._
- [pyo3-object_store](https://github.com/developmentseed/obstore/tree/main/pyo3-object_store) _Integration between [`object_store`](https://docs.rs/object_store) and [`pyo3`](https://github.com/PyO3/pyo3)._
## Examples
- [anise](https://github.com/nyx-space/anise) _A modern, high-performance toolkit for spacecraft mission design, notably used to help softly land Firefly Blue Ghost on the Moon on 02 Feb 2025._
- [arro3](https://github.com/kylebarron/arro3) _A minimal Python library for Apache Arrow, connecting to the Rust arrow crate._
- [arro3-compute](https://github.com/kylebarron/arro3/tree/main/arro3-compute) _`arro3-compute`_
- [arro3-core](https://github.com/kylebarron/arro3/tree/main/arro3-core) _`arro3-core`_
- [arro3-io](https://github.com/kylebarron/arro3/tree/main/arro3-io) _`arro3-io`_
- [bed-reader](https://github.com/fastlmm/bed-reader) _Read and write the PLINK BED format, simply and efficiently._
- Shows Rayon/ndarray::parallel (including capturing errors, controlling thread num), Python types to Rust generics, Github Actions
- [blake3-py](https://github.com/oconnor663/blake3-py) _Python bindings for the [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) cryptographic hash function._
- Parallelized [builds](https://github.com/oconnor663/blake3-py/blob/master/.github/workflows/dists.yml) on GitHub Actions for MacOS, Linux, Windows, including free-threaded 3.13t wheels.
- [cellular_raza](https://cellular-raza.com) _A cellular agent-based simulation framework for building complex models from a clean slate._
- [connector-x](https://github.com/sfu-db/connector-x/tree/main/connectorx-python) _Fastest library to load data from DB to DataFrames in Rust and Python._
- [cryptography](https://github.com/pyca/cryptography/tree/main/src/rust) _Python cryptography library with some functionality in Rust._
- [css-inline](https://github.com/Stranger6667/css-inline/tree/master/bindings/python) _CSS inlining for Python implemented in Rust._
- [datafusion-python](https://github.com/apache/arrow-datafusion-python) _A Python library that binds to Apache Arrow in-memory query engine DataFusion._
- [deltalake-python](https://github.com/delta-io/delta-rs/tree/main/python) _Native Delta Lake Python binding based on delta-rs with Pandas integration._
- [fastbloom](https://github.com/yankun1992/fastbloom) _A fast [bloom filter](https://github.com/yankun1992/fastbloom#BloomFilter) | [counting bloom filter](https://github.com/yankun1992/fastbloom#countingbloomfilter) implemented by Rust for Rust and Python!_
- [fastuuid](https://github.com/thedrow/fastuuid/) _Python bindings to Rust's UUID library._
- [fast-paseto](https://github.com/CodingCogs-OSS/Fast-Paseto) _High-performance PASETO (Platform-Agnostic Security Tokens) implementation with Python bindings._
- [feos](https://github.com/feos-org/feos) _Lightning fast thermodynamic modeling in Rust with fully developed Python interface._
- [finalytics](https://github.com/Nnamdi-sys/finalytics) _Investment Analysis library in Rust | Python._
- [forust](https://github.com/jinlow/forust) _A lightweight gradient boosted decision tree library written in Rust._
- [geo-index](https://github.com/kylebarron/geo-index) _A Rust crate and [Python library](https://github.com/kylebarron/geo-index/tree/main/python) for packed, immutable, zero-copy spatial indexes._
- [granian](https://github.com/emmett-framework/granian) _A Rust HTTP server for Python applications._
- [haem](https://github.com/BooleanCat/haem) _A Python library for working on Bioinformatics problems._
- [hifitime](https://github.com/nyx-space/hifitime) _A high fidelity time management library for engineering and scientific applications where general relativity and time dilation matter._
- [html2text-rs](https://github.com/deedy5/html2text_rs) _Python library for converting HTML to markup or plain text._
- [html-py-ever](https://github.com/PyO3/setuptools-rust/tree/main/examples/html-py-ever) _Using [html5ever](https://github.com/servo/html5ever) through [kuchiki](https://github.com/kuchiki-rs/kuchiki) to speed up html parsing and css-selecting._
- [hudi-rs](https://github.com/apache/hudi-rs) _The native Rust implementation for Apache Hudi, with C++ & Python API bindings._
- [inline-python](https://github.com/m-ou-se/inline-python) _Inline Python code directly in your Rust code._
- [johnnycanencrypt](https://github.com/kushaldas/johnnycanencrypt) OpenPGP library with Yubikey support.
- [jsonschema](https://github.com/Stranger6667/jsonschema/tree/master/crates/jsonschema-py) _A high-performance JSON Schema validator for Python._
- [mocpy](https://github.com/cds-astro/mocpy) _Astronomical Python library offering data structures for describing any arbitrary coverage regions on the unit sphere._
- [obstore](https://github.com/developmentseed/obstore) _The simplest, highest-throughput Python interface to Amazon S3, Google Cloud Storage, Azure Storage, & other S3-compliant APIs, powered by Rust._
- [opendal](https://github.com/apache/opendal/tree/main/bindings/python) _A data access layer that allows users to easily and efficiently retrieve data from various storage services in a unified way._
- [orjson](https://github.com/ijl/orjson) _Fast Python JSON library._
- [ormsgpack](https://github.com/aviramha/ormsgpack) _Fast Python msgpack library._
- [polars](https://github.com/pola-rs/polars) _Fast multi-threaded DataFrame library in Rust | Python | Node.js._
- [pycrdt](https://github.com/jupyter-server/pycrdt) _Python bindings for the Rust CRDT implementation [Yrs](https://github.com/y-crdt/y-crdt)._
- [pydantic-core](https://github.com/pydantic/pydantic-core) _Core validation logic for pydantic written in Rust._
- [primp](https://github.com/deedy5/primp) _The fastest python HTTP client that can impersonate web browsers by mimicking their headers and TLS/JA3/JA4/HTTP2 fingerprints._
- [radiate](https://github.com/pkalivas/radiate): _A high-performance evolution engine for genetic programming and evolutionary algorithms._
- [rateslib](https://github.com/attack68/rateslib) _A fixed income library for Python using Rust extensions._
- [river](https://github.com/online-ml/river) _Online machine learning in python, the computationally heavy statistics algorithms are implemented in Rust._
- [robyn](https://github.com/sparckles/Robyn) A Super Fast Async Python Web Framework with a Rust runtime.
- [rust-python-coverage](https://github.com/cjermain/rust-python-coverage) _Example PyO3 project with automated test coverage for Rust and Python._
- [rnet](https://github.com/0x676e67/rnet) Asynchronous Python HTTP Client with Black Magic
- [sail](https://github.com/lakehq/sail) _Unifying stream, batch, and AI workloads with Apache Spark compatibility._
- [tiktoken](https://github.com/openai/tiktoken) _A fast BPE tokeniser for use with OpenAI's models._
- [tokenizers](https://github.com/huggingface/tokenizers/tree/main/bindings/python) _Python bindings to the Hugging Face tokenizers (NLP) written in Rust._
- [tzfpy](http://github.com/ringsaturn/tzfpy) _A fast package to convert longitude/latitude to timezone name._
- [toml-rs](https://github.com/lava-sh/toml-rs) _A High-Performance TOML v1.0.0 and v1.1.0 parser for Python written in Rust._
- [utiles](https://github.com/jessekrubin/utiles) _Fast Python web-map tile utilities_
## Articles and other media
- [(Video) Using Rust in Free-Threaded vs Regular Python 3.13](https://www.youtube.com/watch?v=J7phN_M4GLM) - Jun 4, 2025
- [(Video) Techniques learned from five years finding the way for Rust in Python](https://www.youtube.com/watch?v=KTQn_PTHNCw) - Feb 26, 2025
- [(Podcast) Bridging Python and Rust: An Interview with PyO3 Maintainer David Hewitt](https://www.youtube.com/watch?v=P47JUMSQagU) - Aug 30, 2024
- [(Video) PyO3: From Python to Rust and Back Again](https://www.youtube.com/watch?v=UmL_CA-v3O8) - Jul 3, 2024
- [Parsing Python ASTs 20x Faster with Rust](https://www.gauge.sh/blog/parsing-python-asts-20x-faster-with-rust) - Jun 17, 2024
- [(Video) How Python Harnesses Rust through PyO3](https://www.youtube.com/watch?v=UilujdubqVU) - May 18, 2024
- [(Video) Combining Rust and Python: The Best of Both Worlds?](https://www.youtube.com/watch?v=lyG6AKzu4ew) - Mar 1, 2024
- [(Video) Extending Python with Rust using PyO3](https://www.youtube.com/watch?v=T45ZEmSR1-s) - Dec 16, 2023
- [A Week of PyO3 + rust-numpy (How to Speed Up Your Data Pipeline X Times)](https://terencezl.github.io/blog/2023/06/06/a-week-of-pyo3-rust-numpy/) - Jun 6, 2023
- [(Podcast) PyO3 with David Hewitt](https://rustacean-station.org/episode/david-hewitt/) - May 19, 2023
- [Making Python 100x faster with less than 100 lines of Rust](https://ohadravid.github.io/posts/2023-03-rusty-python/) - Mar 28, 2023
- [How Pydantic V2 leverages Rust's Superpowers](https://fosdem.org/2023/schedule/event/rust_how_pydantic_v2_leverages_rusts_superpowers/) - Feb 4, 2023
- [How we extended the River stats module with Rust using PyO3](https://boring-guy.sh/posts/river-rust/) - Dec 23, 2022
- [Nine Rules for Writing Python Extensions in Rust](https://towardsdatascience.com/nine-rules-for-writing-python-extensions-in-rust-d35ea3a4ec29?sk=f8d808d5f414154fdb811e4137011437) - Dec 31, 2021
- [Calling Rust from Python using PyO3](https://saidvandeklundert.net/learn/2021-11-18-calling-rust-from-python-using-pyo3/) - Nov 18, 2021
- [davidhewitt's 2021 talk at Rust Manchester meetup](https://www.youtube.com/watch?v=-XyWG_klSAw&t=320s) - Aug 19, 2021
- [Incrementally porting a small Python project to Rust](https://blog.waleedkhan.name/port-python-to-rust/) - Apr 29, 2021
- [Vortexa - Integrating Rust into Python](https://www.vortexa.com/blog/integrating-rust-into-python) - Apr 12, 2021
- [Writing and publishing a Python module in Rust](https://blog.yossarian.net/2020/08/02/Writing-and-publishing-a-python-module-in-rust) - Aug 2, 2020
## Contributing
Everyone is welcomed to contribute to PyO3! There are many ways to support the project, such as:
- help PyO3 users with issues on GitHub and [Discord](https://discord.gg/33kcChzH7f)
- improve documentation
- write features and bugfixes
- publish blogs and examples of how to use PyO3
Our [contributing notes](https://github.com/PyO3/pyo3/blob/main/Contributing.md) and [architecture guide](https://github.com/PyO3/pyo3/blob/main/Architecture.md) have more resources if you wish to volunteer time for PyO3 and are searching where to start.
If you don't have time to contribute yourself but still wish to support the project's future success, some of our maintainers have GitHub sponsorship pages:
- [davidhewitt](https://github.com/sponsors/davidhewitt)
- [messense](https://github.com/sponsors/messense)
## License
PyO3 is licensed under the [Apache-2.0 license](LICENSE-APACHE) or the [MIT license](LICENSE-MIT), at your option.
Python is licensed under the [Python License](https://docs.python.org/3/license.html).
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in PyO3 by you, as defined in the Apache License, shall be dual-licensed as above, without any additional terms or conditions.
================================================
FILE: Releasing.md
================================================
# Releasing
This is notes for the current process of releasing a new PyO3 version. Replace `` in all instructions below with the new version.
## 1. Prepare the release commit
Follow the process below to update all required pieces to bump the version. All these changes are done in a single commit because it makes it clear to git readers what happened to bump the version. It also makes it easy to cherry-pick the version bump onto the `main` branch when tidying up branch history at the end of the release process.
1. Replace all instances of the PyO3 current version and the with the new version to be released. Places to check:
- `Cargo.toml` for all PyO3 crates in the repository.
- Examples in `README.md`
- PyO3 version embedded into documentation like the README.
- `pre-script.rhai` templates for the examples.
- `[towncrier]` section in `pyproject.toml`.
Some of the above locations may already have the new version with a `-dev` suffix, which needs to be removed.
**Make sure not to modify the CHANGELOG during this step!**
2. Run `towncrier build` to generate the CHANGELOG. The version used by `towncrier` should automatically be correct because of the update to `pyproject.toml` in step 1.
3. Manually edit the CHANGELOG for final notes. Steps to do:
- Adjust wording of any release lines to make them clearer for users / fix typos.
- Add a new link at the bottom for the new version, and update the `Unreleased` link.
4. Create the commit containing all the above changes, with a message of `release: `. Push to `release-` branch on the main PyO3 repository, where `` depends on whether this is a major or minor release:
- for O.X.0 minor releases, just use `0.X`, e.g. `release-0.17`. This will become the maintenance branch after release.
- for 0.X.Y patch releases, use the full `0.X.Y`, e.g. `release-0.17.1`. This will be deleted after merge.
## 2. Create the release PR and draft release notes
Open a PR for the branch, and confirm that it passes CI. For `0.X.0` minor releases, the PR should be merging into `main`, for `0.X.Y` patch releases, the PR should be merging the `release-0.X` maintenance branch.
On https://github.com/PyO3/pyo3/releases, click "Draft a new release". The tag will be a new tag of `v` (note preceding `v`) and target should be the `release-` branch you just pushed.
Write release notes which match the style of previous releases. You can get the list of contributors by running `nox -s contributors -- v release-` to get contributors from the previous version tag through to the branch tip you just pushed. (This uses the GitHub API, so you'll need to push the branch first.)
Save as a draft and wait for now.
## 3. Leave for a cooling off period
Wait a couple of days in case anyone wants to hold up the release to add bugfixes etc.
## 4. Put live
To put live:
- 1. merge the release PR
- 2. publish a release on GitHub targeting the release branch
CI will automatically push to `crates.io`.
## 5. Tidy the main branch
If the release PR targeted a branch other than main, you will need to cherry-pick the version bumps, CHANGELOG modifications and removal of towncrier `newsfragments` and open another PR to land these on main.
## 6. Delete the release branch (patch releases only)
For 0.X.Y patch releases, the release branch is no longer needed, so it should be deleted.
================================================
FILE: assets/script.py
================================================
# Used in PyModule examples.
class Blah:
pass
================================================
FILE: build.rs
================================================
use std::env;
use pyo3_build_config::pyo3_build_script_impl::{cargo_env_var, errors::Result};
use pyo3_build_config::{
add_python_framework_link_args, bail, print_feature_cfgs, InterpreterConfig,
};
fn ensure_auto_initialize_ok(interpreter_config: &InterpreterConfig) -> Result<()> {
if cargo_env_var("CARGO_FEATURE_AUTO_INITIALIZE").is_some() && !interpreter_config.shared {
bail!(
"The `auto-initialize` feature is enabled, but your python installation only supports \
embedding the Python interpreter statically. If you are attempting to run tests, or a \
binary which is okay to link dynamically, install a Python distribution which ships \
with the Python shared library.\n\
\n\
Embedding the Python interpreter statically does not yet have first-class support in \
PyO3. If you are sure you intend to do this, disable the `auto-initialize` feature.\n\
\n\
For more information, see \
https://pyo3.rs/v{pyo3_version}/\
building-and-distribution.html#embedding-python-in-rust",
pyo3_version = env::var("CARGO_PKG_VERSION").unwrap()
);
}
Ok(())
}
/// Prepares the PyO3 crate for compilation.
///
/// This loads the config from pyo3-build-config and then makes some additional checks to improve UX
/// for users.
///
/// Emits the cargo configuration based on this config as well as a few checks of the Rust compiler
/// version to enable features which aren't supported on MSRV.
fn configure_pyo3() -> Result<()> {
let interpreter_config = pyo3_build_config::get();
ensure_auto_initialize_ok(interpreter_config)?;
for cfg in interpreter_config.build_script_outputs() {
println!("{cfg}")
}
print_feature_cfgs();
// Make `cargo test` etc work on macOS with Xcode bundled Python
add_python_framework_link_args();
Ok(())
}
fn main() {
pyo3_build_config::print_expected_cfgs();
if let Err(e) = configure_pyo3() {
eprintln!("error: {}", e.report());
std::process::exit(1)
}
}
================================================
FILE: codecov.yml
================================================
comment: off
coverage:
status:
project:
default:
target: auto
# Allow a tiny drop of overall project coverage in PR due to
# not all configurations being tested in PR runs.
#
# (Note that patch coverage will still be required to be at least
# the project coverage.)
threshold: 1%
ignore:
- tests/
- pytests/
- src/test_hygiene/*.rs
================================================
FILE: emscripten/.gitignore
================================================
pybuilddir.txt
builddir
================================================
FILE: emscripten/Makefile
================================================
CURDIR=$(abspath .)
# These three are passed in from nox.
BUILDROOT ?= $(CURDIR)/builddir
PYTHON ?= python3
PYMAJORMINORMICRO ?= $(shell $(PYTHON) --version 2>&1 | awk '{print $$2}')
export EMSDKDIR = $(PYTHONBUILD)/emsdk-cache
PLATFORM=wasm32_emscripten
SYSCONFIGDATA_NAME=_sysconfigdata__$(PLATFORM)
# Set version variables.
version_tuple := $(subst ., ,$(PYMAJORMINORMICRO:v%=%))
PYMAJOR=$(word 1,$(version_tuple))
PYMINOR=$(word 2,$(version_tuple))
PYMICRO=$(word 3,$(version_tuple))
PYVERSION=$(PYMAJORMINORMICRO)
PYMAJORMINOR=$(PYMAJOR).$(PYMINOR)
PYTHONURL=https://www.python.org/ftp/python/$(PYMAJORMINORMICRO)/Python-$(PYVERSION).tgz
# TODO: resume download once 3.14.4 ships with emscripten cache
# PYTHONTARBALL=$(BUILDROOT)/downloads/Python-$(PYVERSION).tgz
# PYTHONBUILD=$(BUILDROOT)/build/Python-$(PYVERSION)
PYTHONBUILD=$(BUILDROOT)/build/cpython
PYTHONLIBDIR=$(BUILDROOT)/install/Python-$(PYVERSION)/lib
CROSS_PYTHON=$(PYTHONBUILD)/cross-build/wasm32-emscripten/build/python/python.sh
all: $(PYTHONLIBDIR)/libpython$(PYMAJORMINOR).a
$(BUILDROOT)/.exists:
mkdir -p $(BUILDROOT)
touch $@
# TODO: use tarball once 3.14.4 ships with emscripten cache
# $(PYTHONTARBALL):
# [ -d $(BUILDROOT)/downloads ] || mkdir -p $(BUILDROOT)/downloads
# wget -q -O $@ $(PYTHONURL)
# $(PYTHONBUILD)/.patched: $(PYTHONTARBALL)
# [ -d $(PYTHONBUILD) ] || ( \
# mkdir -p $(dir $(PYTHONBUILD));\
# tar -C $(dir $(PYTHONBUILD)) -xf $(PYTHONTARBALL) \
# )
# touch $@
ifneq ($(PYMAJORMINOR),3.14)
$(error PYMAJORMINOR must be 3.14, got '$(PYMAJORMINOR)')
endif
$(PYTHONBUILD)/.patched: $(BUILDROOT)/.exists
[ -d $(PYTHONBUILD) ] || ( \
mkdir -p $(dir $(PYTHONBUILD));\
git clone --depth 1 --branch 3.14 https://github.com/python/cpython $(PYTHONBUILD) \
)
touch $@
$(CROSS_PYTHON): $(PYTHONBUILD)/.patched
cd $(PYTHONBUILD) && \
$(PYTHON) Tools/wasm/emscripten install-emscripten --quiet --emsdk-cache=$(EMSDKDIR) && \
$(PYTHON) Tools/wasm/emscripten build --quiet --emsdk-cache=$(EMSDKDIR)
$(PYTHONLIBDIR)/libpython$(PYMAJORMINOR).a: $(CROSS_PYTHON)
# Generate sysconfigdata
_PYTHON_SYSCONFIGDATA_NAME=$(SYSCONFIGDATA_NAME) _PYTHON_PROJECT_BASE=$(PYTHONBUILD)/cross-build/wasm32-emscripten/build/python $(CROSS_PYTHON) -m sysconfig --generate-posix-vars
cp `cat pybuilddir.txt`/$(SYSCONFIGDATA_NAME).py $(PYTHONBUILD)/Lib
mkdir -p $(PYTHONLIBDIR)
# Make a static library for _hacl, for some reason these are missing from the build?
# source emsdk_env.sh to get emar in PATH, works best when done from the emsdk directory
EMSDK_ENV=$$(find $(PYTHONBUILD)/emsdk-cache -name 'emsdk_env.sh' | head -n 1) && \
cd $$(dirname $$EMSDK_ENV) && \
. $$EMSDK_ENV && \
cd $(PYTHONBUILD)/cross-build/wasm32-emscripten/build/python && \
emar rcs Modules/_hacl/libhacl.a Modules/_hacl/*.o
# Copy all .a libraries
find $(PYTHONBUILD)/cross-build/wasm32-emscripten/ -name '*.a' -exec cp {} $(PYTHONLIBDIR) \;
# Install Python stdlib
cp -r $(PYTHONBUILD)/Lib $(PYTHONLIBDIR)/python$(PYMAJORMINOR)
clean:
rm -rf $(BUILDROOT)
================================================
FILE: emscripten/runner.py
================================================
#!/usr/local/bin/python
import pathlib
import sys
import subprocess
p = pathlib.Path(sys.argv[1])
command = ["node", p.name, *sys.argv[2:]]
print("Running:", " ".join(command))
sys.exit(subprocess.call(command, cwd=p.parent))
================================================
FILE: examples/Cargo.toml
================================================
[package]
name = "pyo3-examples"
version = "0.0.0"
publish = false
edition = "2021"
rust-version = "1.83"
[dev-dependencies]
pyo3 = { path = "..", features = ["auto-initialize"] }
[[example]]
name = "decorator"
path = "decorator/src/lib.rs"
crate-type = ["cdylib"]
doc-scrape-examples = true
================================================
FILE: examples/README.md
================================================
# PyO3 Examples
These example crates are a collection of toy extension modules built with PyO3. They are all tested using `nox` in PyO3's CI.
Below is a brief description of each of these:
| Example | Description |
| ------- | ----------- |
| `decorator` | A project showcasing the example from the [Emulating callable objects](https://pyo3.rs/latest/class/call.html) chapter of the guide. |
| `maturin-starter` | A template project which is configured to use [`maturin`](https://github.com/PyO3/maturin) for development. |
| `setuptools-rust-starter` | A template project which is configured to use [`setuptools_rust`](https://github.com/PyO3/setuptools-rust/) for development. |
| `plugin` | Illustrates how to use Python as a scripting language within a Rust application |
Note that there are also other examples in the `pyo3-ffi/examples`
directory that illustrate how to create rust extensions using raw FFI calls into
the CPython C API instead of using PyO3's abstractions.
## Creating new projects from these examples
To copy an example, use [`cargo-generate`](https://crates.io/crates/cargo-generate). Follow the commands below, replacing `` with the example to start from:
```bash
$ cargo install cargo-generate
$ cargo generate --git https://github.com/PyO3/pyo3 examples/
```
(`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.)
================================================
FILE: examples/decorator/.template/Cargo.toml
================================================
[package]
authors = ["{{authors}}"]
name = "{{project-name}}"
version = "0.1.0"
edition = "2021"
[lib]
name = "decorator"
crate-type = ["cdylib"]
[dependencies]
pyo3 = "{{PYO3_VERSION}}"
================================================
FILE: examples/decorator/.template/pre-script.rhai
================================================
variable::set("PYO3_VERSION", "0.28.2");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");
================================================
FILE: examples/decorator/.template/pyproject.toml
================================================
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "{{project-name}}"
version = "0.1.0"
[project.optional-dependencies]
dev = ["pytest"]
================================================
FILE: examples/decorator/Cargo.toml
================================================
[package]
name = "decorator"
version = "0.1.0"
edition = "2021"
rust-version = "1.83"
[lib]
name = "decorator"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { path = "../../" }
[workspace]
================================================
FILE: examples/decorator/MANIFEST.in
================================================
include pyproject.toml Cargo.toml
recursive-include src *
================================================
FILE: examples/decorator/README.md
================================================
# decorator
A project showcasing the example from the [Emulating callable objects](https://pyo3.rs/latest/class/call.html) chapter of the guide.
## Building and Testing
To build this package, first install `maturin`:
```shell
pip install maturin
```
To build and test use `maturin develop`:
```shell
pip install -r requirements-dev.txt
maturin develop
pytest
```
Alternatively, install nox and run the tests inside an isolated environment:
```shell
nox
```
## Copying this example
Use [`cargo-generate`](https://crates.io/crates/cargo-generate):
```bash
$ cargo install cargo-generate
$ cargo generate --git https://github.com/PyO3/pyo3 examples/decorator
```
(`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.)
================================================
FILE: examples/decorator/cargo-generate.toml
================================================
[template]
ignore = [".nox"]
[hooks]
pre = [".template/pre-script.rhai"]
================================================
FILE: examples/decorator/noxfile.py
================================================
import nox
@nox.session
def python(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")
================================================
FILE: examples/decorator/pyproject.toml
================================================
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "decorator"
version = "0.1.0"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Rust",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest"]
================================================
FILE: examples/decorator/src/lib.rs
================================================
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyTuple};
use std::sync::atomic::{AtomicU64, Ordering};
/// A function decorator that keeps track how often it is called.
///
/// It otherwise doesn't do anything special.
#[pyclass(name = "Counter")]
pub struct PyCounter {
// Keeps track of how many calls have gone through.
//
// See the discussion at the end for why `AtomicU64` is used.
count: AtomicU64,
// This is the actual function being wrapped.
wraps: Py,
}
#[pymethods]
impl PyCounter {
// Note that we don't validate whether `wraps` is actually callable.
//
// While we could use `PyAny::is_callable` for that, it has some flaws:
// 1. It doesn't guarantee the object can actually be called successfully
// 2. We still need to handle any exceptions that the function might raise
#[new]
fn __new__(wraps: Py) -> Self {
PyCounter {
count: AtomicU64::new(0),
wraps,
}
}
#[getter]
fn count(&self) -> u64 {
self.count.load(Ordering::Relaxed)
}
#[pyo3(signature = (*args, **kwargs))]
fn __call__(
&self,
py: Python<'_>,
args: &Bound<'_, PyTuple>,
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult> {
let new_count = self.count.fetch_add(1, Ordering::Relaxed);
let name = self.wraps.getattr(py, "__name__")?;
println!("{name} has been called {new_count} time(s).");
// After doing something, we finally forward the call to the wrapped function
let ret = self.wraps.call(py, args, kwargs)?;
// We could do something with the return value of
// the function before returning it
Ok(ret)
}
}
#[pymodule]
pub fn decorator(module: &Bound<'_, PyModule>) -> PyResult<()> {
module.add_class::()?;
Ok(())
}
================================================
FILE: examples/decorator/tests/example.py
================================================
from decorator import Counter
@Counter
def say_hello():
print("hello")
say_hello()
say_hello()
say_hello()
say_hello()
assert say_hello.count == 4
================================================
FILE: examples/decorator/tests/test_.py
================================================
from decorator import Counter
def test_no_args():
@Counter
def say_hello():
print("hello")
say_hello()
say_hello()
say_hello()
say_hello()
assert say_hello.count == 4
def test_arg():
@Counter
def say_hello(name):
print(f"hello {name}")
say_hello("a")
say_hello("b")
say_hello("c")
say_hello("d")
assert say_hello.count == 4
def test_default_arg():
@Counter
def say_hello(name="default"):
print(f"hello {name}")
say_hello("a")
say_hello()
say_hello("c")
say_hello()
assert say_hello.count == 4
# https://github.com/PyO3/pyo3/discussions/2598
def test_discussion_2598():
@Counter
def say_hello():
if say_hello.count < 2:
print("hello from decorator")
say_hello()
say_hello()
================================================
FILE: examples/getitem/.template/Cargo.toml
================================================
[package]
authors = ["{{authors}}"]
name = "{{project-name}}"
version = "0.1.0"
edition = "2021"
[lib]
name = "getitem"
crate-type = ["cdylib"]
[dependencies]
pyo3 = "{{PYO3_VERSION}}"
================================================
FILE: examples/getitem/.template/pre-script.rhai
================================================
variable::set("PYO3_VERSION", "0.19.0");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");
================================================
FILE: examples/getitem/.template/pyproject.toml
================================================
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "{{project-name}}"
version = "0.1.0"
[project.optional-dependencies]
dev = ["pytest"]
================================================
FILE: examples/getitem/Cargo.toml
================================================
[package]
name = "getitem"
version = "0.1.0"
edition = "2021"
rust-version = "1.83"
[lib]
name = "getitem"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { path = "../../" }
[workspace]
================================================
FILE: examples/getitem/MANIFEST.in
================================================
include pyproject.toml Cargo.toml
recursive-include src *
================================================
FILE: examples/getitem/README.md
================================================
# getitem
A project showcasing how to create a `__getitem__` override that also showcases how to deal with multiple incoming types
## Relevant Documentation
Some of the relevant documentation links for this example:
* Converting Slices to Indices: https://docs.rs/pyo3/latest/pyo3/types/struct.PySlice.html#method.indices
* GetItem Docs: https://pyo3.rs/latest/class/protocols.html?highlight=__getitem__#mapping--sequence-types
* Extract: https://pyo3.rs/latest/conversions/traits.html?highlight=extract#extract-and-the-frompyobject-trait
* Downcast and getattr: https://pyo3.rs/v0.19.0/types.html?highlight=getattr#pyany
## Building and Testing
To build this package, first install `maturin`:
```shell
pip install maturin
```
To build and test use `maturin develop`:
```shell
pip install -r requirements-dev.txt
maturin develop
pytest
```
Alternatively, install nox and run the tests inside an isolated environment:
```shell
nox
```
## Copying this example
Use [`cargo-generate`](https://crates.io/crates/cargo-generate):
```bash
$ cargo install cargo-generate
$ cargo generate --git https://github.com/PyO3/pyo3 examples/decorator
```
(`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.)
================================================
FILE: examples/getitem/cargo-generate.toml
================================================
[template]
ignore = [".nox"]
[hooks]
pre = [".template/pre-script.rhai"]
================================================
FILE: examples/getitem/noxfile.py
================================================
import nox
@nox.session
def python(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")
================================================
FILE: examples/getitem/pyproject.toml
================================================
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "getitem"
version = "0.1.0"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Rust",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest"]
================================================
FILE: examples/getitem/src/lib.rs
================================================
// This is a very fake example of how to check __getitem__ parameter and handle appropriately
use pyo3::exceptions::PyTypeError;
use pyo3::prelude::*;
use pyo3::types::PySlice;
#[derive(FromPyObject)]
enum IntOrSlice<'py> {
Int(i32),
Slice(Bound<'py, PySlice>),
}
#[pyclass]
struct ExampleContainer {
// represent the maximum length our container is pretending to be
max_length: i32,
}
#[pymethods]
impl ExampleContainer {
#[new]
fn new() -> Self {
ExampleContainer { max_length: 100 }
}
fn __getitem__(&self, key: &Bound<'_, PyAny>) -> PyResult {
if let Ok(position) = key.extract::() {
return Ok(position);
} else if let Ok(slice) = key.cast::() {
// METHOD 1 - the use PySliceIndices to help with bounds checking and for cases when only start or end are provided
// in this case the start/stop/step all filled in to give valid values based on the max_length given
let index = slice.indices(self.max_length as isize).unwrap();
let _delta = index.stop - index.start;
// METHOD 2 - Do the getattr manually really only needed if you have some special cases for stop/_step not being present
// convert to indices and this will help you deal with stop being the max length
let start: i32 = slice.getattr("start")?.extract()?;
// This particular example assumes stop is present, but note that if not present, this will cause us to return due to the
// extract failing. Not needing custom code to deal with this is a good reason to use the Indices method.
let stop: i32 = slice.getattr("stop")?.extract()?;
// example of grabbing step since it is not always present
let _step: i32 = match slice.getattr("step")?.extract() {
// if no value found assume step is 1
Ok(v) => v,
Err(_) => 1 as i32,
};
// Use something like this if you don't support negative stepping and want to give users
// leeway on how they provide their ordering
let (start, stop) = if start > stop {
(stop, start)
} else {
(start, stop)
};
let delta = stop - start;
return Ok(delta);
} else {
return Err(PyTypeError::new_err("Unsupported type"));
}
}
fn __setitem__(&self, idx: IntOrSlice, value: u32) -> PyResult<()> {
match idx {
IntOrSlice::Slice(slice) => {
let index = slice.indices(self.max_length as isize).unwrap();
println!(
"Got a slice! {}-{}, step: {}, value: {}",
index.start, index.stop, index.step, value
);
}
IntOrSlice::Int(index) => {
println!("Got an index! {} : value: {}", index, value);
}
}
Ok(())
}
}
#[pymodule(name = "getitem")]
fn example(m: &Bound<'_, PyModule>) -> PyResult<()> {
// ? -https://github.com/PyO3/maturin/issues/475
m.add_class::()?;
Ok(())
}
================================================
FILE: examples/getitem/tests/test_getitem.py
================================================
import getitem
import pytest
def test_simple():
container = getitem.ExampleContainer()
assert container[3] == 3
assert container[4] == 4
assert container[-1] == -1
assert container[5:3] == 2
assert container[3:5] == 2
# test setitem, but this just displays, no return to check
container[3:5] = 2
container[2] = 2
# and note we will get an error on this one since we didn't
# add strings
with pytest.raises(TypeError):
container["foo"] = 2
================================================
FILE: examples/maturin-starter/.template/Cargo.toml
================================================
[package]
authors = ["{{authors}}"]
name = "{{project-name}}"
version = "0.1.0"
edition = "2021"
[lib]
name = "maturin_starter"
crate-type = ["cdylib"]
[dependencies]
pyo3 = "{{PYO3_VERSION}}"
================================================
FILE: examples/maturin-starter/.template/pre-script.rhai
================================================
variable::set("PYO3_VERSION", "0.28.2");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");
================================================
FILE: examples/maturin-starter/.template/pyproject.toml
================================================
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "{{project-name}}"
version = "0.1.0"
[project.optional-dependencies]
dev = ["pytest"]
================================================
FILE: examples/maturin-starter/Cargo.toml
================================================
[package]
name = "maturin-starter"
version = "0.1.0"
edition = "2021"
rust-version = "1.83"
[lib]
name = "maturin_starter"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { path = "../../" }
[features]
abi3 = ["pyo3/abi3-py37"]
[workspace]
================================================
FILE: examples/maturin-starter/MANIFEST.in
================================================
include pyproject.toml Cargo.toml
recursive-include src *
================================================
FILE: examples/maturin-starter/README.md
================================================
# maturin-starter
An example of a basic Python extension module built using PyO3 and [`maturin`](https://github.com/PyO3/maturin).
## Building and Testing
To build this package, first install `maturin`:
```shell
pip install maturin
```
To build and test use `maturin develop`:
```shell
pip install -r requirements-dev.txt
maturin develop && pytest
```
Alternatively, install nox and run the tests inside an isolated environment:
```shell
nox
```
## Copying this example
Use [`cargo-generate`](https://crates.io/crates/cargo-generate):
```bash
$ cargo install cargo-generate
$ cargo generate --git https://github.com/PyO3/pyo3 examples/maturin-starter
```
(`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.)
================================================
FILE: examples/maturin-starter/cargo-generate.toml
================================================
[template]
ignore = [".nox"]
[hooks]
pre = [".template/pre-script.rhai"]
================================================
FILE: examples/maturin-starter/maturin_starter/__init__.py
================================================
# import the contents of the Rust library into the Python extension
from .maturin_starter import *
from .maturin_starter import __all__
# optional: include the documentation from the Rust module
from .maturin_starter import __doc__ # noqa: F401
__all__ = __all__ + ["PythonClass"]
class PythonClass:
def __init__(self, value: int) -> None:
self.value = value
================================================
FILE: examples/maturin-starter/noxfile.py
================================================
import nox
@nox.session
def python(session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")
================================================
FILE: examples/maturin-starter/pyproject.toml
================================================
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "maturin-starter"
version = "0.1.0"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Rust",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest"]
================================================
FILE: examples/maturin-starter/src/lib.rs
================================================
use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::wrap_pymodule;
mod submodule;
#[pyclass]
struct ExampleClass {
#[pyo3(get, set)]
value: i32,
}
#[pymethods]
impl ExampleClass {
#[new]
pub fn new(value: i32) -> Self {
ExampleClass { value }
}
}
/// An example module implemented in Rust using PyO3.
#[pymodule]
fn maturin_starter(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::()?;
m.add_wrapped(wrap_pymodule!(submodule::submodule))?;
// Inserting to sys.modules allows importing submodules nicely from Python
// e.g. from maturin_starter.submodule import SubmoduleClass
let sys = PyModule::import(py, "sys")?;
let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.cast_into()?;
sys_modules.set_item("maturin_starter.submodule", m.getattr("submodule")?)?;
Ok(())
}
================================================
FILE: examples/maturin-starter/src/submodule.rs
================================================
use pyo3::prelude::*;
#[pyclass]
struct SubmoduleClass {}
#[pymethods]
impl SubmoduleClass {
#[new]
pub fn __new__() -> Self {
SubmoduleClass {}
}
pub fn greeting(&self) -> &'static str {
"Hello, world!"
}
}
#[pymodule]
pub fn submodule(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::()?;
Ok(())
}
================================================
FILE: examples/maturin-starter/tests/test_maturin_starter.py
================================================
from maturin_starter import ExampleClass, PythonClass
def test_python_class() -> None:
py_class = PythonClass(value=10)
assert py_class.value == 10
def test_example_class() -> None:
example = ExampleClass(value=11)
assert example.value == 11
def test_doc() -> None:
import maturin_starter
assert (
maturin_starter.__doc__ == "An example module implemented in Rust using PyO3."
)
================================================
FILE: examples/maturin-starter/tests/test_submodule.py
================================================
from maturin_starter.submodule import SubmoduleClass
def test_submodule_class() -> None:
submodule_class = SubmoduleClass()
assert submodule_class.greeting() == "Hello, world!"
================================================
FILE: examples/plugin/.template/Cargo.toml
================================================
[package]
authors = ["{{authors}}"]
name = "{{project-name}}"
version = "0.1.0"
edition = "2021"
[dependencies]
pyo3 = "{{PYO3_VERSION}}"
plugin_api = { path = "plugin_api" }
================================================
FILE: examples/plugin/.template/plugin_api/Cargo.toml
================================================
[package]
name = "plugin_api"
version = "0.1.0"
description = "Plugin API example"
edition = "2021"
[lib]
name = "plugin_api"
crate-type = ["cdylib", "rlib"]
[dependencies]
pyo3 = "{{PYO3_VERSION}}"
================================================
FILE: examples/plugin/.template/pre-script.rhai
================================================
variable::set("PYO3_VERSION", "0.28.2");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/plugin_api/Cargo.toml", "plugin_api/Cargo.toml");
file::delete(".template");
================================================
FILE: examples/plugin/Cargo.toml
================================================
[package]
name = "plugin_example"
version = "0.1.0"
edition = "2021"
rust-version = "1.83"
[dependencies]
pyo3 = { path = "../../", features = ["macros"] }
plugin_api = { path = "plugin_api" }
[workspace]
================================================
FILE: examples/plugin/README.md
================================================
# plugin
An example of a Rust app that uses Python for a plugin. A Python extension module built using PyO3 and [`maturin`](https://github.com/PyO3/maturin) is used to provide
interface types that can be used to exchange data between Rust and Python. This also deals with how to separately test and load python modules.
# Building and Testing
## Host application
To run the app itself, you only need to run
```shell
cargo run
```
It will build the app, as well as the plugin API, then run the app, load the plugin and show it working.
## Plugin API testing
The plugin API is in a separate crate `plugin_api`, so you can test it separately from the main app.
To build the API only package, install and build with `maturin`:
```shell
pip install maturin
cd plugin_api
maturin build
```
Alternatively, install nox and run the tests inside an isolated environment:
```shell
nox
```
## Copying this example
Use [`cargo-generate`](https://crates.io/crates/cargo-generate):
```bash
$ cargo install cargo-generate
$ cargo generate --git https://github.com/PyO3/pyo3 examples/plugin
```
(`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.)
================================================
FILE: examples/plugin/cargo-generate.toml
================================================
[template]
ignore = [".nox"]
[hooks]
pre = [".template/pre-script.rhai"]
================================================
FILE: examples/plugin/plugin_api/Cargo.toml
================================================
[package]
name = "plugin_api"
version = "0.1.0"
description = "Plugin API example"
edition = "2021"
[lib]
name = "plugin_api"
crate-type = ["cdylib", "rlib"]
[dependencies]
pyo3 = { path = "../../../" }
================================================
FILE: examples/plugin/plugin_api/noxfile.py
================================================
import nox
@nox.session
def python(session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")
================================================
FILE: examples/plugin/plugin_api/pyproject.toml
================================================
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "plugin_api"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
[project.optional-dependencies]
dev = ["pytest"]
================================================
FILE: examples/plugin/plugin_api/src/lib.rs
================================================
use pyo3::prelude::*;
///this is our Gadget that python plugin code can create, and rust app can then access natively.
#[pyclass]
pub struct Gadget {
#[pyo3(get, set)]
pub prop: usize,
//this field will only be accessible to rust code
pub rustonly: Vec,
}
#[pymethods]
impl Gadget {
#[new]
fn new() -> Self {
Gadget {
prop: 777,
rustonly: Vec::new(),
}
}
fn push(&mut self, v: usize) {
self.rustonly.push(v);
}
}
/// A Python module for plugin interface types
#[pymodule]
pub fn plugin_api(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::()?;
Ok(())
}
================================================
FILE: examples/plugin/plugin_api/tests/test_Gadget.py
================================================
import pytest
@pytest.fixture
def gadget():
import plugin_api as pa
g = pa.Gadget()
return g
def test_creation(gadget):
pass
def test_property(gadget):
gadget.prop = 42
assert gadget.prop == 42
def test_push(gadget):
gadget.push(42)
================================================
FILE: examples/plugin/plugin_api/tests/test_import.py
================================================
def test_import():
import plugin_api # noqa: F401
================================================
FILE: examples/plugin/python_plugin/gadget_init_plugin.py
================================================
import plugin_api
import rng
def start():
"""create an instance of Gadget, configure it and return to Rust"""
g = plugin_api.Gadget()
g.push(1)
g.push(2)
g.push(3)
g.prop = rng.get_random_number()
return g
================================================
FILE: examples/plugin/python_plugin/rng.py
================================================
def get_random_number():
# verified by the roll of a fair die to be random
return 4
================================================
FILE: examples/plugin/src/main.rs
================================================
use plugin_api::plugin_api as pylib_module;
use pyo3::prelude::*;
use pyo3::types::PyList;
fn main() -> Result<(), Box> {
//"export" our API module to the python runtime
pyo3::append_to_inittab!(pylib_module);
//spawn runtime
Python::initialize();
//import path for python
let path = "./python_plugin/";
//do useful work
Python::attach(|py| {
//add the current directory to import path of Python (do not use this in production!)
let syspath: Bound = py.import("sys")?.getattr("path")?.extract().map_err(PyErr::from)?;
syspath.insert(0, &path)?;
println!("Import path is: {:?}", syspath);
// Now we can load our python_plugin/gadget_init_plugin.py file.
// It can in turn import other stuff as it deems appropriate
let plugin = PyModule::import(py, "gadget_init_plugin")?;
// and call start function there, which will return a python reference to Gadget.
// Gadget here is a "pyclass" object reference
let gadget = plugin.getattr("start")?.call0()?;
//now we extract (i.e. mutably borrow) the rust struct from python object
{
//this scope will have mutable access to the gadget instance, which will be dropped on
//scope exit so Python can access it again.
let mut gadget_rs: PyRefMut<'_, plugin_api::Gadget> = gadget.extract().map_err( PyErr::from)?;
// we can now modify it as if it was a native rust struct
gadget_rs.prop = 42;
//which includes access to rust-only fields that are not visible to python
println!("rust-only vec contains {:?}", gadget_rs.rustonly);
gadget_rs.rustonly.clear();
}
//any modifications we make to rust object are reflected on Python object as well
let res: usize = gadget.getattr("prop")?.extract()?;
println!("{res}");
Ok(())
})
}
================================================
FILE: examples/setuptools-rust-starter/.template/Cargo.toml
================================================
[package]
authors = ["{{authors}}"]
name = "{{project-name}}"
version = "0.1.0"
edition = "2021"
[lib]
name = "setuptools_rust_starter"
crate-type = ["cdylib"]
[dependencies]
pyo3 = "{{PYO3_VERSION}}"
================================================
FILE: examples/setuptools-rust-starter/.template/pre-script.rhai
================================================
variable::set("PYO3_VERSION", "0.28.2");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/setup.cfg", "setup.cfg");
file::delete(".template");
================================================
FILE: examples/setuptools-rust-starter/.template/setup.cfg
================================================
[metadata]
name = {{project-name}}
version = 0.1.0
packages =
setuptools_rust_starter
[options]
include_package_data = True
zip_safe = False
================================================
FILE: examples/setuptools-rust-starter/Cargo.toml
================================================
[package]
name = "setuptools-rust-starter"
version = "0.1.0"
edition = "2021"
rust-version = "1.83"
[lib]
name = "setuptools_rust_starter"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { path = "../../" }
[workspace]
================================================
FILE: examples/setuptools-rust-starter/MANIFEST.in
================================================
include pyproject.toml Cargo.toml
recursive-include src *
================================================
FILE: examples/setuptools-rust-starter/README.md
================================================
# setuptools-rust-starter
An example of a basic Python extension module built using PyO3 and [`setuptools_rust`](https://github.com/PyO3/setuptools-rust).
## Building and Testing
To build this package, first install `setuptools_rust`:
```shell
pip install setuptools_rust
```
To build and test use `python setup.py develop`:
```shell
pip install -r requirements-dev.txt
python setup.py develop && pytest
```
Alternatively, install nox and run the tests inside an isolated environment:
```shell
nox
```
## Copying this example
Use [`cargo-generate`](https://crates.io/crates/cargo-generate):
```bash
$ cargo install cargo-generate
$ cargo generate --git https://github.com/PyO3/pyo3 examples/setuptools-rust-starter
```
(`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.)
================================================
FILE: examples/setuptools-rust-starter/cargo-generate.toml
================================================
[template]
ignore = [".nox"]
[hooks]
pre = [".template/pre-script.rhai"]
================================================
FILE: examples/setuptools-rust-starter/noxfile.py
================================================
import nox
import sys
@nox.session
def python(session: nox.Session):
if sys.version_info < (3, 9):
session.skip("Python 3.9 or later is required for setuptools-rust 1.11")
session.env["SETUPTOOLS_RUST_CARGO_PROFILE"] = "dev"
session.install(".[dev]")
session.run("pytest")
================================================
FILE: examples/setuptools-rust-starter/pyproject.toml
================================================
[build-system]
requires = ["setuptools>=62.4", "setuptools_rust>=1.11"]
[project]
name = "setuptools-rust-starter"
version = "0.1.0"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Rust",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest"]
[tool.setuptools.packages.find]
include = ["setuptools_rust_starter"]
[[tool.setuptools-rust.ext-modules]]
target = "setuptools_rust_starter._setuptools_rust_starter"
path = "Cargo.toml"
================================================
FILE: examples/setuptools-rust-starter/requirements-dev.txt
================================================
pytest>=3.5.0
setuptools_rust~=1.0.0
pip>=21.3
wheel
================================================
FILE: examples/setuptools-rust-starter/setuptools_rust_starter/__init__.py
================================================
# import the contents of the Rust library into the Python extension
from ._setuptools_rust_starter import *
from ._setuptools_rust_starter import __all__
# optional: include the documentation from the Rust module
from ._setuptools_rust_starter import __doc__ # noqa: F401
__all__ = __all__ + ["PythonClass"]
class PythonClass:
def __init__(self, value: int) -> None:
self.value = value
================================================
FILE: examples/setuptools-rust-starter/src/lib.rs
================================================
use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::wrap_pymodule;
mod submodule;
#[pyclass]
struct ExampleClass {
#[pyo3(get, set)]
value: i32,
}
#[pymethods]
impl ExampleClass {
#[new]
pub fn new(value: i32) -> Self {
ExampleClass { value }
}
}
/// An example module implemented in Rust using PyO3.
#[pymodule]
fn _setuptools_rust_starter(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::()?;
m.add_wrapped(wrap_pymodule!(submodule::submodule))?;
// Inserting to sys.modules allows importing submodules nicely from Python
// e.g. from setuptools_rust_starter.submodule import SubmoduleClass
let sys = PyModule::import(py, "sys")?;
let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.cast_into()?;
sys_modules.set_item("setuptools_rust_starter.submodule", m.getattr("submodule")?)?;
Ok(())
}
================================================
FILE: examples/setuptools-rust-starter/src/submodule.rs
================================================
use pyo3::prelude::*;
#[pyclass]
struct SubmoduleClass {}
#[pymethods]
impl SubmoduleClass {
#[new]
pub fn __new__() -> Self {
SubmoduleClass {}
}
pub fn greeting(&self) -> &'static str {
"Hello, world!"
}
}
#[pymodule]
pub fn submodule(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::()?;
Ok(())
}
================================================
FILE: examples/setuptools-rust-starter/tests/test_setuptools_rust_starter.py
================================================
from setuptools_rust_starter import ExampleClass, PythonClass
def test_python_class() -> None:
py_class = PythonClass(value=10)
assert py_class.value == 10
def test_example_class() -> None:
example = ExampleClass(value=11)
assert example.value == 11
def test_doc() -> None:
import setuptools_rust_starter
assert (
setuptools_rust_starter.__doc__
== "An example module implemented in Rust using PyO3."
)
================================================
FILE: examples/setuptools-rust-starter/tests/test_submodule.py
================================================
from setuptools_rust_starter.submodule import SubmoduleClass
def test_submodule_class() -> None:
submodule_class = SubmoduleClass()
assert submodule_class.greeting() == "Hello, world!"
================================================
FILE: examples/word-count/.template/Cargo.toml
================================================
[package]
authors = ["{{authors}}"]
name = "{{project-name}}"
version = "0.1.0"
edition = "2021"
[lib]
name = "word_count"
crate-type = ["cdylib"]
[dependencies]
pyo3 = "{{PYO3_VERSION}}"
rayon = "1.0.2"
================================================
FILE: examples/word-count/.template/pre-script.rhai
================================================
variable::set("PYO3_VERSION", "0.28.2");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");
================================================
FILE: examples/word-count/.template/pyproject.toml
================================================
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "{{project-name}}"
version = "0.1.0"
[project.optional-dependencies]
dev = ["pytest"]
[tool.pytest.ini_options]
addopts = "--benchmark-disable"
================================================
FILE: examples/word-count/Cargo.toml
================================================
[package]
name = "word-count"
version = "0.1.0"
edition = "2021"
rust-version = "1.83"
[lib]
name = "word_count"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { path = "../.." }
rayon = "1.0.2"
[workspace]
================================================
FILE: examples/word-count/MANIFEST.in
================================================
include pyproject.toml Cargo.toml
recursive-include src *
================================================
FILE: examples/word-count/README.md
================================================
# word-count
Demonstrates searching for a file in plain python, with rust singlethreaded and with rust multithreaded.
## Build
```shell
pip install .
```
## Usage
```python
from word_count import search_py, search, search_sequential
search_py("foo bar", "foo")
search("foo bar", "foo")
search_sequential("foo bar", "foo")
```
## Testing
To test install nox globally and run
```shell
nox
```
## Benchmark
To test install nox globally and run
```shell
nox -s bench
```
## Copying this example
Use [`cargo-generate`](https://crates.io/crates/cargo-generate):
```bash
$ cargo install cargo-generate
$ cargo generate --git https://github.com/PyO3/pyo3 examples/word-count
```
(`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.)
================================================
FILE: examples/word-count/cargo-generate.toml
================================================
[template]
ignore = [".nox"]
[hooks]
pre = [".template/pre-script.rhai"]
================================================
FILE: examples/word-count/noxfile.py
================================================
import nox
nox.options.sessions = ["test"]
@nox.session
def test(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")
@nox.session
def bench(session: nox.Session):
session.install(".[dev]")
session.run("pytest", "--benchmark-enable")
================================================
FILE: examples/word-count/pyproject.toml
================================================
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "word-count"
version = "0.1.0"
classifiers = [
"License :: OSI Approved :: MIT License",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Rust",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest", "pytest-benchmark"]
[tool.pytest.ini_options]
addopts = "--benchmark-disable"
================================================
FILE: examples/word-count/src/lib.rs
================================================
use pyo3::prelude::*;
use rayon::prelude::*;
/// Searches for the word, parallelized by rayon
#[pyfunction]
fn search(contents: &str, needle: &str) -> usize {
contents
.par_lines()
.map(|line| count_line(line, needle))
.sum()
}
/// Searches for a word in a classic sequential fashion
#[pyfunction]
fn search_sequential(contents: &str, needle: &str) -> usize {
contents.lines().map(|line| count_line(line, needle)).sum()
}
#[pyfunction]
fn search_sequential_detached(py: Python<'_>, contents: &str, needle: &str) -> usize {
py.detach(|| search_sequential(contents, needle))
}
/// Count the occurrences of needle in line, case insensitive
fn count_line(line: &str, needle: &str) -> usize {
let mut total = 0;
for word in line.split(' ') {
if word == needle {
total += 1;
}
}
total
}
#[pymodule]
fn word_count(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(search, m)?)?;
m.add_function(wrap_pyfunction!(search_sequential, m)?)?;
m.add_function(wrap_pyfunction!(search_sequential_detached, m)?)?;
Ok(())
}
================================================
FILE: examples/word-count/tests/test_word_count.py
================================================
from concurrent.futures import ThreadPoolExecutor
import pytest
import word_count
@pytest.fixture(scope="session")
def contents() -> str:
text = """
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
"""
return text * 1000
def test_word_count_rust_parallel(benchmark, contents):
count = benchmark(word_count.search, contents, "is")
assert count == 10000
def test_word_count_rust_sequential(benchmark, contents):
count = benchmark(word_count.search_sequential, contents, "is")
assert count == 10000
def test_word_count_python_sequential(benchmark, contents):
count = benchmark(word_count.search_py, contents, "is")
assert count == 10000
def run_rust_sequential_twice(
executor: ThreadPoolExecutor, contents: str, needle: str
) -> int:
future_1 = executor.submit(word_count.search_sequential_detached, contents, needle)
future_2 = executor.submit(word_count.search_sequential_detached, contents, needle)
result_1 = future_1.result()
result_2 = future_2.result()
return result_1 + result_2
def test_word_count_rust_sequential_twice_with_threads(benchmark, contents):
executor = ThreadPoolExecutor(max_workers=2)
count = benchmark(run_rust_sequential_twice, executor, contents, "is")
assert count == 20000
================================================
FILE: examples/word-count/word_count/__init__.py
================================================
from .word_count import search, search_sequential, search_sequential_detached
__all__ = [
"search_py",
"search",
"search_sequential",
"search_sequential_detached",
]
def search_py(contents: str, needle: str) -> int:
total = 0
for line in contents.splitlines():
for word in line.split(" "):
if word == needle:
total += 1
return total
================================================
FILE: guide/book.toml
================================================
[book]
title = "PyO3 user guide"
description = "PyO3 user guide"
authors = ["PyO3 Project and Contributors"]
[preprocessor.pyo3_version]
command = "python3 pyo3_version.py"
[preprocessor.tabs]
[output.html]
git-repository-url = "https://github.com/PyO3/pyo3/tree/main/guide"
edit-url-template = "https://github.com/PyO3/pyo3/edit/main/guide/{path}"
playground.runnable = false
additional-css = ["theme/tabs.css"]
additional-js = ["theme/tabs.js"]
================================================
FILE: guide/pyclass-parameters.md
================================================
`#[pyclass]` can be used with the following parameters:
| Parameter | Description |
| :- | :- |
| `constructor` | This is currently only allowed on [variants of complex enums][params-constructor]. It allows customization of the generated class constructor for each variant. It uses the same syntax and supports the same options as the `signature` attribute of functions and methods. |
| `crate = "some::path"` | Path to import the `pyo3` crate, if it's not accessible at `::pyo3`. |
| `dict` | Gives instances of this class an empty `__dict__` to store custom attributes. |
| `eq` | Implements `__eq__` using the `PartialEq` implementation of the underlying Rust datatype. |
| `eq_int` | Implements `__eq__` using `__int__` for simple enums. |
| `extends = BaseType` | Use a custom baseclass. Defaults to [`PyAny`][params-1] |
| `freelist = N` | Implements a [free list][params-2] of size N. This can improve performance for types that are often created and deleted in quick succession. Profile your code to see whether `freelist` is right for you. |
| `from_py_object` | Implement `FromPyObject` for this pyclass. Requires the pyclass to be `Clone`. |
| `frozen` | Declares that your pyclass is immutable. It removes the borrow checker overhead when retrieving a shared reference to the Rust struct, but disables the ability to get a mutable reference. |
| `generic` | Implements runtime parametrization for the class following [PEP 560](https://peps.python.org/pep-0560/). |
| `get_all` | Generates getters for all fields of the pyclass. |
| `hash` | Implements `__hash__` using the `Hash` implementation of the underlying Rust datatype. *Requires `eq` and `frozen`* |
| `immutable_type` | Makes the type object immutable. Supported on 3.14+ with the `abi3` feature active, or 3.10+ otherwise. |
| `mapping` | Inform PyO3 that this class is a [`Mapping`][params-mapping], and so leave its implementation of sequence C-API slots empty. |
| `module = "module_name"` | Python code will see the class as being defined in this module. Defaults to `builtins`. |
| `name = "python_name"` | Sets the name that Python sees this class as. Defaults to the name of the Rust struct. |
| `ord` | Implements `__lt__`, `__gt__`, `__le__`, & `__ge__` using the `PartialOrd` implementation of the underlying Rust datatype. *Requires `eq`* |
| `rename_all = "renaming_rule"` | Applies renaming rules to every getters and setters of a struct, or every variants of an enum. Possible values are: "camelCase", "kebab-case", "lowercase", "PascalCase", "SCREAMING-KEBAB-CASE", "SCREAMING_SNAKE_CASE", "snake_case", "UPPERCASE". |
| `sequence` | Inform PyO3 that this class is a [`Sequence`][params-sequence], and so leave its C-API mapping length slot empty. |
| `set_all` | Generates setters for all fields of the pyclass. |
| `new = "from_fields"` | Generates a default `__new__` constructor with all fields as parameters in the `new()` method. |
| `skip_from_py_object` | Prevents this PyClass from participating in the `FromPyObject: PyClass + Clone` blanket implementation. This allows a custom `FromPyObject` impl, even if `self` is `Clone`. |
| `str` | Implements `__str__` using the `Display` implementation of the underlying Rust datatype or by passing an optional format string `str=""`. *Note: The optional format string is only allowed for structs. `name` and `rename_all` are incompatible with the optional format string. Additional details can be found in the discussion on this [PR](https://github.com/PyO3/pyo3/pull/4233).* |
| `subclass` | Allows other Python classes and `#[pyclass]` to inherit from this class. Enums cannot be subclassed. |
| `unsendable` | Required if your struct is not [`Send`][params-3]. Rather than using `unsendable`, consider implementing your struct in a thread-safe way by e.g. substituting [`Rc`][params-4] with [`Arc`][params-5]. By using `unsendable`, your class will panic when accessed by another thread. Also note the Python's GC is multi-threaded and while unsendable classes will not be traversed on foreign threads to avoid UB, this can lead to memory leaks. |
| `weakref` | Allows this class to be [weakly referenceable][params-6]. |
All of these parameters can either be passed directly on the `#[pyclass(...)]` annotation, or as one or more accompanying `#[pyo3(...)]` annotations, e.g.:
```rust,ignore
// Argument supplied directly to the `#[pyclass]` annotation.
#[pyclass(name = "SomeName", subclass)]
struct MyClass {}
// Argument supplied as a separate annotation.
#[pyclass]
#[pyo3(name = "SomeName", subclass)]
struct MyClass {}
```
[params-1]: https://docs.rs/pyo3/latest/pyo3/types/struct.PyAny.html
[params-2]: https://en.wikipedia.org/wiki/Free_list
[params-3]: https://doc.rust-lang.org/std/marker/trait.Send.html
[params-4]: https://doc.rust-lang.org/std/rc/struct.Rc.html
[params-5]: https://doc.rust-lang.org/std/sync/struct.Arc.html
[params-6]: https://docs.python.org/3/library/weakref.html
[params-constructor]: https://pyo3.rs/latest/class.html#complex-enums
[params-mapping]: https://pyo3.rs/latest/class/protocols.html#mapping--sequence-types
[params-sequence]: https://pyo3.rs/latest/class/protocols.html#mapping--sequence-types
================================================
FILE: guide/pyo3_version.py
================================================
"""Simple mdbook preprocessor to inject pyo3 version into the guide.
It will replace:
- {{#PYO3_VERSION_TAG}} with the contents of the PYO3_VERSION_TAG environment var
- {{#PYO3_DOCS_URL}} with the location of docs (e.g. 'https://docs.rs/pyo3/0.13.2')
- {{#PYO3_CRATE_VERSION}} with a relevant toml snippet (e.g. 'version = "0.13.2"')
Tested against mdbook 0.5.0.
"""
import json
import os
import sys
# Set PYO3_VERSION in CI to build the correct version into links
PYO3_VERSION_TAG = os.environ.get("PYO3_VERSION_TAG", "main")
if PYO3_VERSION_TAG == "main":
PYO3_DOCS_URL = "https://pyo3.rs/main/doc"
PYO3_DOCS_VERSION = "latest"
PYO3_CRATE_VERSION = 'git = "https://github.com/pyo3/pyo3"'
else:
# v0.13.2 -> 0.13.2
version = PYO3_VERSION_TAG.lstrip("v")
PYO3_DOCS_URL = f"https://docs.rs/pyo3/{version}"
PYO3_DOCS_VERSION = version
PYO3_CRATE_VERSION = f'version = "{version}"'
def replace_item_content(item):
if not isinstance(item, dict) or "Chapter" not in item:
return
# Replace raw and url-encoded forms
item["Chapter"]["content"] = (
item["Chapter"]["content"]
.replace("{{#PYO3_VERSION_TAG}}", PYO3_VERSION_TAG)
.replace("{{#PYO3_DOCS_URL}}", PYO3_DOCS_URL)
.replace("{{#PYO3_DOCS_VERSION}}", PYO3_DOCS_VERSION)
.replace("{{#PYO3_CRATE_VERSION}}", PYO3_CRATE_VERSION)
)
for sub_item in item["Chapter"]["sub_items"]:
replace_item_content(sub_item)
for line in sys.stdin:
if line:
[context, book] = json.loads(line)
for item in book["items"]:
replace_item_content(item)
json.dump(book, fp=sys.stdout)
================================================
FILE: guide/src/SUMMARY.md
================================================
# Summary
[Introduction](index.md)
---
- [Getting started](getting-started.md)
- [Using Rust from Python](rust-from-python.md)
- [Python modules](module.md)
- [Python functions](function.md)
- [Function signatures](function/signature.md)
- [Error handling](function/error-handling.md)
- [Python classes](class.md)
- [Class customizations](class/protocols.md)
- [Basic object customization](class/object.md)
- [Emulating numeric types](class/numeric.md)
- [Emulating callable objects](class/call.md)
- [Thread safety](class/thread-safety.md)
- [Calling Python from Rust](python-from-rust.md)
- [Python object types](types.md)
- [Python exceptions](exception.md)
- [Calling Python functions](python-from-rust/function-calls.md)
- [Executing existing Python code](python-from-rust/calling-existing-code.md)
- [Type conversions](conversions.md)
- [Mapping of Rust types to Python types](conversions/tables.md)
- [Conversion traits](conversions/traits.md)
- [Using `async` and `await`](async-await.md)
- [Parallelism](parallelism.md)
- [Supporting Free-Threaded Python](free-threading.md)
- [Debugging](debugging.md)
- [Features reference](features.md)
- [Performance](performance.md)
- [Type stub generation and introspection](type-stub.md)
- [Advanced topics](advanced.md)
- [Building and distribution](building-and-distribution.md)
- [Supporting multiple Python versions](building-and-distribution/multiple-python-versions.md)
- [Useful crates](ecosystem.md)
- [Logging](ecosystem/logging.md)
- [Tracing](ecosystem/tracing.md)
- [Using `async` and `await`](ecosystem/async-await.md)
- [FAQ and troubleshooting](faq.md)
---
[Appendix A: Migration guide](migration.md)
[Appendix B: Trait bounds](trait-bounds.md)
[Appendix C: Python typing hints](python-typing-hints.md)
[CHANGELOG](changelog.md)
---
[Contributing](contributing.md)
================================================
FILE: guide/src/advanced.md
================================================
# Advanced topics
## FFI
PyO3 exposes much of Python's C API through the `ffi` module.
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.
================================================
FILE: guide/src/async-await.md
================================================
# Using `async` and `await`
*This feature is still in active development.*
*See [the related issue](https://github.com/PyO3/pyo3/issues/1632).*
`#[pyfunction]` and `#[pymethods]` attributes also support `async fn`.
```rust,no_run
# #![allow(dead_code)]
# #[cfg(feature = "experimental-async")] {
use std::{thread, time::Duration};
use futures::channel::oneshot;
use pyo3::prelude::*;
#[pyfunction]
#[pyo3(signature=(seconds, result=None))]
async fn sleep(seconds: f64, result: Option>) -> Option> {
let (tx, rx) = oneshot::channel();
thread::spawn(move || {
thread::sleep(Duration::from_secs_f64(seconds));
tx.send(()).unwrap();
});
rx.await.unwrap();
result
}
# }
```
*Python awaitables instantiated with this method can only be awaited in `asyncio` context.*
*Other Python async runtime may be supported in the future.*
## `Send + 'static` constraint
Resulting future of an `async fn` decorated by `#[pyfunction]` must be `Send + 'static` to be embedded in a Python object.
As a consequence, `async fn` parameters and return types must also be `Send + 'static`, so it is not possible to have a signature like `async fn does_not_compile<'py>(arg: Bound<'py, PyAny>) -> Bound<'py, PyAny>`.
However, there is an exception for method receivers, so async methods can accept `&self`/ `&mut self`.
Note that this means that the class instance is borrowed for as long as the returned future is not completed, even across yield points and while waiting for I/O operations to complete.
Hence, other methods cannot obtain exclusive borrows while the future is still being polled.
This is the same as how async methods in Rust generally work but it is more problematic for Rust code interfacing with Python code due to pervasive shared mutability.
This strongly suggests to prefer shared borrows `&self` over exclusive ones `&mut self` to avoid racy borrow check failures at runtime.
## Implicitly attached to the interpreter
Even if it is not possible to pass a `py: Python<'py>` token to an `async fn`, we're still attached to the interpreter during the execution of the future – the same as for a regular `fn` without `Python<'py>`/`Bound<'py, PyAny>` parameter
It is still possible to get a `Python` marker using [`Python::attach`]({{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.attach); because `attach` is reentrant and optimized, the cost will be negligible.
## Detaching from the interpreter across `.await`
There is currently no simple way to detach from the interpreter when awaiting a future, *but solutions are currently in development*.
Here is the advised workaround for now:
```rust,ignore
use std::{
future::Future,
pin::{Pin, pin},
task::{Context, Poll},
};
use pyo3::prelude::*;
struct AllowThreads(F);
impl Future for AllowThreads
where
F: Future + Unpin + Send,
F::Output: Send,
{
type Output = F::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {
let waker = cx.waker();
Python::attach(|py| {
py.detach(|| pin!(&mut self.0).poll(&mut Context::from_waker(waker)))
})
}
}
```
## Cancellation
Cancellation on the Python side can be caught using [`CancelHandle`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.CancelHandle.html) type, by annotating a function parameter with `#[pyo3(cancel_handle)]`.
```rust,no_run
# #![allow(dead_code)]
# #[cfg(feature = "experimental-async")] {
use futures::FutureExt;
use pyo3::prelude::*;
use pyo3::coroutine::CancelHandle;
#[pyfunction]
async fn cancellable(#[pyo3(cancel_handle)] mut cancel: CancelHandle) {
futures::select! {
/* _ = ... => println!("done"), */
_ = cancel.cancelled().fuse() => println!("cancelled"),
}
}
# }
```
## The `Coroutine` type
To make a Rust future awaitable in Python, PyO3 defines a [`Coroutine`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.Coroutine.html) type, which implements the Python [coroutine protocol](https://docs.python.org/3/library/collections.abc.html#collections.abc.Coroutine).
Each `coroutine.send` call is translated to a `Future::poll` call.
If a [`CancelHandle`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.CancelHandle.html) parameter is declared, the exception passed to `coroutine.throw` call is stored in it and can be retrieved with [`CancelHandle::cancelled`]({{#PYO3_DOCS_URL}}/pyo3/coroutine/struct.CancelHandle.html#method.cancelled); otherwise, it cancels the Rust future, and the exception is reraised;
*The type does not yet have a public constructor until the design is finalized.*
================================================
FILE: guide/src/building-and-distribution/multiple-python-versions.md
================================================
# Supporting multiple Python versions
PyO3 supports all actively-supported Python 3 and PyPy versions.
As much as possible, this is done internally to PyO3 so that your crate's code does not need to adapt to the differences between each version.
However, as Python features grow and change between versions, PyO3 cannot offer a completely identical API for every Python version.
This may require you to add conditional compilation to your crate or runtime checks for the Python version.
This section of the guide first introduces the `pyo3-build-config` crate, which you can use as a `build-dependency` to add additional `#[cfg]` flags which allow you to support multiple Python versions at compile-time.
Second, we'll show how to check the Python version at runtime.
This can be useful when building for multiple versions with the `abi3` feature, where the Python API compiled against is not always the same as the one in use.
## Conditional compilation for different Python versions
The `pyo3-build-config` exposes multiple [`#[cfg]` flags](https://doc.rust-lang.org/rust-by-example/attribute/cfg.html) which can be used to conditionally compile code for a given Python version.
PyO3 itself depends on this crate, so by using it you can be sure that you are configured correctly for the Python version PyO3 is building against.
This allows us to write code like the following
```rust,ignore
#[cfg(Py_3_7)]
fn function_only_supported_on_python_3_7_and_up() {}
#[cfg(not(Py_3_8))]
fn function_only_supported_before_python_3_8() {}
#[cfg(not(Py_LIMITED_API))]
fn function_incompatible_with_abi3_feature() {}
```
The following sections first show how to add these `#[cfg]` flags to your build process, and then cover some common patterns flags in a little more detail.
To see a full reference of all the `#[cfg]` flags provided, see the [`pyo3-build-cfg` docs](https://docs.rs/pyo3-build-config).
### Using `pyo3-build-config`
You can use the `#[cfg]` flags in just two steps:
1. Add `pyo3-build-config` with the [`resolve-config`](../features.md#resolve-config) feature enabled to your crate's build dependencies in `Cargo.toml`:
```toml
[build-dependencies]
pyo3-build-config = { {{#PYO3_CRATE_VERSION}}, features = ["resolve-config"] }
```
2. Add a [`build.rs`](https://doc.rust-lang.org/cargo/reference/build-scripts.html) file to your crate with the following contents:
```rust,ignore
fn main() {
// If you have an existing build.rs file, just add this line to it.
pyo3_build_config::use_pyo3_cfgs();
}
```
After these steps you are ready to annotate your code!
### Common usages of `pyo3-build-cfg` flags
The `#[cfg]` flags added by `pyo3-build-cfg` can be combined with all of Rust's logic in the `#[cfg]` attribute to create very precise conditional code generation.
The following are some common patterns implemented using these flags:
```text
#[cfg(Py_3_7)]
```
This `#[cfg]` marks code that will only be present on Python 3.7 and upwards.
There are similar options `Py_3_8`, `Py_3_9`, `Py_3_10` and so on for each minor version.
```text
#[cfg(not(Py_3_7))]
```
This `#[cfg]` marks code that will only be present on Python versions before (but not including) Python 3.7.
```text
#[cfg(not(Py_LIMITED_API))]
```
This `#[cfg]` marks code that is only available when building for the unlimited Python API (i.e. PyO3's `abi3` feature is not enabled).
This might be useful if you want to ship your extension module as an `abi3` wheel and also allow users to compile it from source to make use of optimizations only possible with the unlimited API.
```text
#[cfg(any(Py_3_9, not(Py_LIMITED_API)))]
```
This `#[cfg]` marks code which is available when running Python 3.9 or newer, or when using the unlimited API with an older Python version.
Patterns like this are commonly seen on Python APIs which were added to the limited Python API in a specific minor version.
```text
#[cfg(PyPy)]
```
This `#[cfg]` marks code which is running on PyPy.
## Checking the Python version at runtime
When building with PyO3's `abi3` feature, your extension module will be compiled against a specific [minimum version](../building-and-distribution.md#minimum-python-version-for-abi3) of Python, but may be running on newer Python versions.
For example with PyO3's `abi3-py38` feature, your extension will be compiled as if it were for Python 3.8.
If you were using `pyo3-build-config`, `#[cfg(Py_3_8)]` would be present.
Your user could freely install and run your abi3 extension on Python 3.9.
There's no way to detect your user doing that at compile time, so instead you need to fall back to runtime checks.
PyO3 provides the APIs [`Python::version()`] and [`Python::version_info()`] to query the running Python version.
This allows you to do the following, for example:
```rust
use pyo3::Python;
Python::attach(|py| {
// PyO3 supports Python 3.7 and up.
assert!(py.version_info() >= (3, 7));
assert!(py.version_info() >= (3, 7, 0));
});
```
[`Python::version()`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.version
[`Python::version_info()`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.version_info
================================================
FILE: guide/src/building-and-distribution.md
================================================
# Building and distribution
This chapter of the guide goes into detail on how to build and distribute projects using PyO3.
The way to achieve this is very different depending on whether the project is a Python module implemented in Rust, or a Rust binary embedding Python.
For both types of project there are also common problems such as the Python version to build for and the [linker](https://en.wikipedia.org/wiki/Linker_(computing)) arguments to use.
The material in this chapter is intended for users who have already read the PyO3 [README](./index.md).
It covers in turn the choices that can be made for Python modules and for Rust binaries.
There is also a section at the end about cross-compiling projects using PyO3.
There is an additional sub-chapter dedicated to [supporting multiple Python versions](./building-and-distribution/multiple-python-versions.md).
## Configuring the Python version
PyO3 uses a build script (backed by the [`pyo3-build-config`] crate) to determine the Python version and set the correct linker arguments.
By default it will attempt to use the following in order:
- Any active Python virtualenv.
- The `python` executable (if it's a Python 3 interpreter).
- The `python3` executable.
You can override the Python interpreter by setting the `PYO3_PYTHON` environment variable, e.g. `PYO3_PYTHON=python3.7`, `PYO3_PYTHON=/usr/bin/python3.9`, or even a PyPy interpreter `PYO3_PYTHON=pypy3`.
Once the Python interpreter is located, `pyo3-build-config` executes it to query the information in the `sysconfig` module which is needed to configure the rest of the compilation.
To validate the configuration which PyO3 will use, you can run a compilation with the environment variable `PYO3_PRINT_CONFIG=1` set.
An example output of doing this is shown below:
```console
$ PYO3_PRINT_CONFIG=1 cargo build
Compiling pyo3 v0.14.1 (/home/david/dev/pyo3)
error: failed to run custom build command for `pyo3 v0.14.1 (/home/david/dev/pyo3)`
Caused by:
process didn't exit successfully: `/home/david/dev/pyo3/target/debug/build/pyo3-7a8cf4fe22e959b7/build-script-build` (exit status: 101)
--- stdout
cargo:rerun-if-env-changed=PYO3_CROSS
cargo:rerun-if-env-changed=PYO3_CROSS_LIB_DIR
cargo:rerun-if-env-changed=PYO3_CROSS_PYTHON_VERSION
cargo:rerun-if-env-changed=PYO3_PRINT_CONFIG
-- PYO3_PRINT_CONFIG=1 is set, printing configuration and halting compile --
implementation=CPython
version=3.8
shared=true
abi3=false
lib_name=python3.8
lib_dir=/usr/lib
executable=/usr/bin/python
pointer_width=64
build_flags=
suppress_build_script_link_lines=false
```
The `PYO3_ENVIRONMENT_SIGNATURE` environment variable can be used to trigger rebuilds when its value changes, it has no other effect.
### Advanced: config files
If you save the above output config from `PYO3_PRINT_CONFIG` to a file, it is possible to manually override the contents and feed it back into PyO3 using the `PYO3_CONFIG_FILE` env var.
If your build environment is unusual enough that PyO3's regular configuration detection doesn't work, using a config file like this will give you the flexibility to make PyO3 work for you.
To see the full set of options supported, see the documentation for the [`InterpreterConfig` struct](https://docs.rs/pyo3-build-config/{{#PYO3_DOCS_VERSION}}/pyo3_build_config/struct.InterpreterConfig.html).
## Building Python extension modules
Python extension modules need to be compiled differently depending on the OS (and architecture) that they are being compiled for.
As well as multiple OSes (and architectures), there are also many different Python versions which are actively supported.
Packages uploaded to [PyPI](https://pypi.org/) usually want to upload prebuilt "wheels" covering many OS/arch/version combinations so that users on all these different platforms don't have to compile the package themselves.
Package vendors can opt-in to the "abi3" limited Python API which allows their wheels to be used on multiple Python versions, reducing the number of wheels they need to compile, but restricts the functionality they can use.
There are many ways to go about this: it is possible to use `cargo` to build the extension module (along with some manual work, which varies with OS).
The PyO3 ecosystem has two packaging tools, [`maturin`] and [`setuptools-rust`], which abstract over the OS difference and also support building wheels for PyPI upload.
PyO3 has some functionality for configuring projects when building Python extension modules:
- The `PYO3_BUILD_EXTENSION_MODULE` environment variable, which must be set when building Python extension modules. `maturin` and `setuptools-rust` set this automatically.
- The `abi3` Cargo feature and its version-specific `abi3-pyXY` companions, which are used to opt-in to the limited Python API in order to support multiple Python versions in a single wheel.
This section describes the packaging tools before describing how to build manually without them.
It then proceeds with an explanation of the `PYO3_BUILD_EXTENSION_MODULE` environment variable.
Finally, there is a section describing PyO3's `abi3` features.
### Packaging tools
The PyO3 ecosystem has two main choices to abstract the process of developing Python extension modules:
- [`maturin`] is a command-line tool to build, package and upload Python modules.
It makes opinionated choices about project layout meaning it needs very little configuration.
This makes it a great choice for users who are building a Python extension from scratch and don't need flexibility.
- [`setuptools-rust`] is an add-on for `setuptools` which adds extra keyword arguments to the `setup.py` configuration file.
It requires more configuration than `maturin`, however this gives additional flexibility for users adding Rust to an existing Python package that can't satisfy `maturin`'s constraints.
Consult each project's documentation for full details on how to get started using them and how to upload wheels to PyPI.
It should be noted that while `maturin` is able to build [manylinux](https://github.com/pypa/manylinux)-compliant wheels out-of-the-box, `setuptools-rust` requires a bit more effort, [relying on Docker](https://setuptools-rust.readthedocs.io/en/latest/building_wheels.html) for this purpose.
There are also [`maturin-starter`] and [`setuptools-rust-starter`] examples in the PyO3 repository.
### Manual builds
To build a PyO3-based Python extension manually, start by running `cargo build` as normal in a library project with the [`cdylib` crate type](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-crate-type-field) while the `PYO3_BUILD_EXTENSION_MODULE` environment variable is set.
Once built, symlink (or copy) and rename the shared library from Cargo's `target/` directory to your desired output directory:
- on macOS, rename `libyour_module.dylib` to `your_module.so`.
- on Windows, rename `libyour_module.dll` to `your_module.pyd`.
- on Linux, rename `libyour_module.so` to `your_module.so`.
You can then open a Python shell in the output directory and you'll be able to run `import your_module`.
If you're packaging your library for redistribution, you should indicate the Python interpreter your library is compiled for by including the [platform tag](#platform-tags) in its name.
This prevents incompatible interpreters from trying to import your library.
If you're compiling for PyPy you *must* include the platform tag, or PyPy will ignore the module.
#### Bazel builds
To use PyO3 with bazel one needs to manually configure PyO3, PyO3-ffi and PyO3-macros.
In particular, one needs to make sure that it is compiled with the right python flags for the version you intend to use.
For example see:
1. [github.com/abrisco/rules_pyo3](https://github.com/abrisco/rules_pyo3) -- General rules for building extension modules.
2. [github.com/OliverFM/pytorch_with_gazelle](https://github.com/OliverFM/pytorch_with_gazelle) -- for a minimal example of a repo that can use PyO3, PyTorch and Gazelle to generate python Build files.
3. [github.com/TheButlah/rules_pyo3](https://github.com/TheButlah/rules_pyo3) -- is somewhat dated.
#### Platform tags
Rather than using just the `.so` or `.pyd` extension suggested above (depending on OS), you can prefix the shared library extension with a platform tag to indicate the interpreter it is compatible with.
You can query your interpreter's platform tag from the `sysconfig` module.
Some example outputs of this are seen below:
```bash
# CPython 3.10 on macOS
.cpython-310-darwin.so
# PyPy 7.3 (Python 3.9) on Linux
$ python -c 'import sysconfig; print(sysconfig.get_config_var("EXT_SUFFIX"))'
.pypy39-pp73-x86_64-linux-gnu.so
```
So, for example, a valid module library name on CPython 3.10 for macOS is `your_module.cpython-310-darwin.so`, and its equivalent when compiled for PyPy 7.3 on Linux would be `your_module.pypy38-pp73-x86_64-linux-gnu.so`.
See [PEP 3149](https://peps.python.org/pep-3149/) for more background on platform tags.
#### macOS
On macOS, because the `PYO3_BUILD_EXTENSION_MODULE` environment variable disables linking to `libpython` ([see the next section](#the-extension-module-feature)), some additional linker arguments need to be set. `maturin` and `setuptools-rust` both pass these arguments for PyO3 automatically, but projects using manual builds will need to set these directly in order to support macOS.
The easiest way to set the correct linker arguments is to add a [`build.rs`](https://doc.rust-lang.org/cargo/reference/build-scripts.html) with the following content:
```rust,ignore
fn main() {
pyo3_build_config::add_extension_module_link_args();
}
```
Remember to also add `pyo3-build-config` to the `build-dependencies` section in `Cargo.toml`.
An alternative to using `pyo3-build-config` is add the following to a cargo configuration file (e.g. `.cargo/config.toml`):
```toml
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
```
Using the MacOS system python3 (`/usr/bin/python3`, as opposed to python installed via homebrew, pyenv, nix, etc.) may result in runtime errors such as `Library not loaded: @rpath/Python3.framework/Versions/3.8/Python3`.
The easiest way to set the correct linker arguments is to add a `build.rs` with the following content:
```rust,ignore
fn main() {
pyo3_build_config::add_python_framework_link_args();
}
```
Alternatively it can be resolved with another addition to `.cargo/config.toml`:
```toml
[build]
rustflags = [
"-C", "link-args=-Wl,-rpath,/Library/Developer/CommandLineTools/Library/Frameworks",
]
```
For more discussion on and workarounds for MacOS linking problems [see this issue](https://github.com/PyO3/pyo3/issues/1800#issuecomment-906786649).
Finally, don't forget that on MacOS the `extension-module` feature will cause `cargo test` to fail without the `--no-default-features` flag (see [the FAQ](https://pyo3.rs/main/faq.html#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror)).
### The `PYO3_BUILD_EXTENSION_MODULE` environment variable
By default PyO3 links to `libpython`.
This makes binaries, tests, and examples "just work".
However, Python extensions on Unix must not link to libpython for [manylinux](https://www.python.org/dev/peps/pep-0513/) compliance.
The downside of not linking to `libpython` is that binaries, tests, and examples (which usually embed Python) will fail to build.
As a result, PyO3 uses an envionment variable `PYO3_BUILD_EXTENSION_MODULE` to disable linking to `libpython`.
This should only be set when building a library for distribution.
`maturin >= 1.9.4` and `setuptools-rust >= 1.12` will set this for you automatically.
> [!NOTE]
> Historically PyO3 used an `extension-module` feature to perform the same function now done by the `PYO3_BUILD_EXTENSION_MODULE` env var.
> This feature caused linking to be disabled for all compile targets, including Rust tests and benchmarks.
>
> Projects are encouraged to migrate off the feature, as it caused [major development pain](faq.md#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror) due to the lack of linking.
### `Py_LIMITED_API`/`abi3`
By default, Python extension modules can only be used with the same Python version they were compiled against.
For example, an extension module built for Python 3.5 can't be imported in Python 3.8.
[PEP 384](https://www.python.org/dev/peps/pep-0384/) introduced the idea of the limited Python API, which would have a stable ABI enabling extension modules built with it to be used against multiple Python versions.
This is also known as `abi3`.
The advantage of building extension modules using the limited Python API is that package vendors only need to build and distribute a single copy (for each OS / architecture), and users can install it on all Python versions from the [minimum version](#minimum-python-version-for-abi3) and up.
The downside of this is that PyO3 can't use optimizations which rely on being compiled against a known exact Python version.
It's up to you to decide whether this matters for your extension module.
It's also possible to design your extension module such that you can distribute `abi3` wheels but allow users compiling from source to benefit from additional optimizations - see the [support for multiple python versions](./building-and-distribution/multiple-python-versions.md) section of this guide, in particular the `#[cfg(Py_LIMITED_API)]` flag.
There are three steps involved in making use of `abi3` when building Python packages as wheels:
1. Enable the `abi3` feature in `pyo3`.
This ensures `pyo3` only calls Python C-API functions which are part of the stable API, and on Windows also ensures that the project links against the correct shared object (no special behavior is required on other platforms):
```toml
[dependencies]
pyo3 = { {{#PYO3_CRATE_VERSION}}, features = ["abi3"] }
```
2. Ensure that the built shared objects are correctly marked as `abi3`.
This is accomplished by telling your build system that you're using the limited API.
[`maturin`] >= 0.9.0 and [`setuptools-rust`] >= 0.11.4 support `abi3` wheels.
See the [corresponding](https://github.com/PyO3/maturin/pull/353) [PRs](https://github.com/PyO3/setuptools-rust/pull/82) for more.
3. Ensure that the `.whl` is correctly marked as `abi3`.
For projects using `setuptools`, this is accomplished by passing `--py-limited-api=cp3x` (where `x` is the minimum Python version supported by the wheel, e.g. `--py-limited-api=cp35` for Python 3.5) to `setup.py bdist_wheel`.
#### Minimum Python version for `abi3`
Because a single `abi3` wheel can be used with many different Python versions, PyO3 has feature flags `abi3-py37`, `abi3-py38`, `abi3-py39` etc. to set the minimum required Python version for your `abi3` wheel.
For example, if you set the `abi3-py37` feature, your extension wheel can be used on all Python 3 versions from Python 3.7 and up.
`maturin` and `setuptools-rust` will give the wheel a name like `my-extension-1.0-cp37-abi3-manylinux2020_x86_64.whl`.
As your extension module may be run with multiple different Python versions you may occasionally find you need to check the Python version at runtime to customize behavior.
See [the relevant section of this guide](./building-and-distribution/multiple-python-versions.md#checking-the-python-version-at-runtime) on supporting multiple Python versions at runtime.
PyO3 is only able to link your extension module to abi3 version up to and including your host Python version.
E.g., if you set `abi3-py38` and try to compile the crate with a host of Python 3.7, the build will fail.
> [!NOTE]
> If you set more that one of these `abi3` version feature flags the lowest version always wins. For example, with both `abi3-py37` and `abi3-py38` set, PyO3 would build a wheel which supports Python 3.7 and up.
#### Building `abi3` extensions without a Python interpreter
As an advanced feature, you can build PyO3 wheel without calling Python interpreter with the environment variable `PYO3_NO_PYTHON` set.
Also, if the build host Python interpreter is not found or is too old or otherwise unusable, PyO3 will still attempt to compile `abi3` extension modules after displaying a warning message.
#### Missing features
Due to limitations in the Python API, there are a few `pyo3` features that do not work when compiling for `abi3`.
These are:
- `#[pyo3(text_signature = "...")]` does not work on classes until Python 3.10 or greater.
- The `dict` and `weakref` options on classes are not supported until Python 3.9 or greater.
- The buffer API is not supported until Python 3.11 or greater.
- Subclassing native types (e.g. `PyException`) is not supported until Python 3.12 or greater.
- Optimizations which rely on knowledge of the exact Python version compiled against.
## Embedding Python in Rust
If you want to embed the Python interpreter inside a Rust program, there are two modes in which this can be done: dynamically and statically.
We'll cover each of these modes in the following sections.
Each of them affect how you must distribute your program.
Instead of learning how to do this yourself, you might want to consider using a project like [PyOxidizer] to ship your application and all of its dependencies in a single file.
PyO3 automatically switches between the two linking modes depending on whether the Python distribution you have configured PyO3 to use ([see above](#configuring-the-python-version)) contains a shared library or a static library.
The static library is most often seen in Python distributions compiled from source without the `--enable-shared` configuration option.
### Dynamically embedding the Python interpreter
Embedding the Python interpreter dynamically is much easier than doing so statically.
This is done by linking your program against a Python shared library (such as `libpython.3.9.so` on UNIX, or `python39.dll` on Windows).
The implementation of the Python interpreter resides inside the shared library.
This means that when the OS runs your Rust program it also needs to be able to find the Python shared library.
This mode of embedding works well for Rust tests which need access to the Python interpreter.
It is also great for Rust software which is installed inside a Python virtualenv, because the virtualenv sets up appropriate environment variables to locate the correct Python shared library.
For distributing your program to non-technical users, you will have to consider including the Python shared library in your distribution as well as setting up wrapper scripts to set the right environment variables (such as `LD_LIBRARY_PATH` on UNIX, or `PATH` on Windows).
Note that PyPy cannot be embedded in Rust (or any other software).
Support for this is tracked on the [PyPy issue tracker](https://github.com/pypy/pypy/issues/3836).
### Statically embedding the Python interpreter
Embedding the Python interpreter statically means including the contents of a Python static library directly inside your Rust binary.
This means that to distribute your program you only need to ship your binary file: it contains the Python interpreter inside the binary!
On Windows static linking is almost never done, so Python distributions don't usually include a static library.
The information below applies only to UNIX.
The Python static library is usually called `libpython.a`.
Static linking has a lot of complications, listed below.
For these reasons PyO3 does not yet have first-class support for this embedding mode.
See [issue 416 on PyO3's GitHub](https://github.com/PyO3/pyo3/issues/416) for more information and to discuss any issues you encounter.
The [`auto-initialize`](features.md#auto-initialize) feature is deliberately disabled when embedding the interpreter statically because this is often unintentionally done by new users to PyO3 running test programs.
Trying out PyO3 is much easier using dynamic embedding.
The known complications are:
- To import compiled extension modules (such as other Rust extension modules, or those written in C), your binary must have the correct linker flags set during compilation to export the original contents of `libpython.a` so that extensions can use them (e.g. `-Wl,--export-dynamic`).
- The C compiler and flags which were used to create `libpython.a` must be compatible with your Rust compiler and flags, else you will experience compilation failures.
Significantly different compiler versions may see errors like this:
```text
lto1: fatal error: bytecode stream in file 'rust-numpy/target/release/deps/libpyo3-6a7fb2ed970dbf26.rlib' generated with LTO version 6.0 instead of the expected 6.2
```
Mismatching flags may lead to errors like this:
```text
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libpython3.9.a(zlibmodule.o): relocation R_X86_64_32 against `.data' can not be used when making a PIE object; recompile with -fPIE
```
If you encounter these or other complications when linking the interpreter statically, discuss them on [issue 416 on PyO3's GitHub](https://github.com/PyO3/pyo3/issues/416).
It is hoped that eventually that discussion will contain enough information and solutions that PyO3 can offer first-class support for static embedding.
### Import your module when embedding the Python interpreter
When you run your Rust binary with an embedded interpreter, any `#[pymodule]` created modules won't be accessible to import unless added to a table called `PyImport_Inittab` before the embedded interpreter is initialized.
This will cause Python statements in your embedded interpreter such as `import your_new_module` to fail.
You can call the macro [`append_to_inittab`]({{#PYO3_DOCS_URL}}/pyo3/macro.append_to_inittab.html) with your module before initializing the Python interpreter to add the module function into that table. (The Python interpreter will be initialized by calling `Python::initialize`, `with_embedded_python_interpreter`, or `Python::attach` with the [`auto-initialize`](features.md#auto-initialize) feature enabled.)
## Cross Compiling
Thanks to Rust's great cross-compilation support, cross-compiling using PyO3 is relatively straightforward.
To get started, you'll need a few pieces of software:
- A toolchain for your target.
- The appropriate options in your Cargo `.config` for the platform you're targeting and the toolchain you are using.
- A Python interpreter that's already been compiled for your target (optional when building "abi3" extension modules).
- A Python interpreter that is built for your host and available through the `PATH` or setting the [`PYO3_PYTHON`](#configuring-the-python-version) variable (optional when building "abi3" extension modules).
After you've obtained the above, you can build a cross-compiled PyO3 module by using Cargo's `--target` flag.
PyO3's build script will detect that you are attempting a cross-compile based on your host machine and the desired target.
When cross-compiling, PyO3's build script cannot execute the target Python interpreter to query the configuration, so there are a few additional environment variables you may need to set:
- `PYO3_CROSS`: If present this variable forces PyO3 to configure as a cross-compilation.
- `PYO3_CROSS_LIB_DIR`: This variable can be set to the directory containing the target's libpython DSO and the associated `_sysconfigdata*.py` file for Unix-like targets.
This variable is only needed when the output binary must link to libpython explicitly (e.g. when targeting Android or embedding a Python interpreter), or when it is absolutely required to get the interpreter configuration from `_sysconfigdata*.py`.
On Windows, this variable is not needed because PyO3 uses `raw-dylib` linking.
- `PYO3_CROSS_PYTHON_VERSION`: Major and minor version (e.g. 3.9) of the target Python installation.
This variable is only needed if PyO3 cannot determine the version to target from `abi3-py3*` features, or if `PYO3_CROSS_LIB_DIR` is not set, or if there are multiple versions of Python present in `PYO3_CROSS_LIB_DIR`.
- `PYO3_CROSS_PYTHON_IMPLEMENTATION`: Python implementation name ("CPython" or "PyPy") of the target Python installation.
CPython is assumed by default when this variable is not set, unless `PYO3_CROSS_LIB_DIR` is set for a Unix-like target and PyO3 can get the interpreter configuration from `_sysconfigdata*.py`.
An example might look like the following (assuming your target's sysroot is at `/home/pyo3/cross/sysroot` and that your target is `armv7`):
```sh
export PYO3_CROSS_LIB_DIR="/home/pyo3/cross/sysroot/usr/lib"
cargo build --target armv7-unknown-linux-gnueabihf
```
If there are multiple python versions at the cross lib directory and you cannot set a more precise location to include both the `libpython` DSO and `_sysconfigdata*.py` files, you can set the required version:
```sh
export PYO3_CROSS_PYTHON_VERSION=3.8
export PYO3_CROSS_LIB_DIR="/home/pyo3/cross/sysroot/usr/lib"
cargo build --target armv7-unknown-linux-gnueabihf
```
Or another example building for Windows (no `PYO3_CROSS_LIB_DIR` needed thanks to `raw-dylib`):
```sh
export PYO3_CROSS_PYTHON_VERSION=3.9
cargo build --target x86_64-pc-windows-gnu
```
Any of the `abi3-py3*` features can be enabled instead of setting `PYO3_CROSS_PYTHON_VERSION` in the above examples.
`PYO3_CROSS_LIB_DIR` can often be omitted when cross compiling extension modules for Unix, macOS, and Windows targets.
The following resources may also be useful for cross-compiling:
- [github.com/japaric/rust-cross](https://github.com/japaric/rust-cross) is a primer on cross compiling Rust.
- [github.com/rust-embedded/cross](https://github.com/rust-embedded/cross) uses Docker to make Rust cross-compilation easier.
[`pyo3-build-config`]: https://github.com/PyO3/pyo3/tree/main/pyo3-build-config
[`maturin-starter`]: https://github.com/PyO3/pyo3/tree/main/examples/maturin-starter
[`setuptools-rust-starter`]: https://github.com/PyO3/pyo3/tree/main/examples/setuptools-rust-starter
[`maturin`]: https://github.com/PyO3/maturin
[`setuptools-rust`]: https://github.com/PyO3/setuptools-rust
[PyOxidizer]: https://github.com/indygreg/PyOxidizer
================================================
FILE: guide/src/changelog.md
================================================
{{#include ../../CHANGELOG.md}}
================================================
FILE: guide/src/class/call.md
================================================
# Emulating callable objects
Classes can be callable if they have a `#[pymethod]` named `__call__`.
This allows instances of a class to behave similar to functions.
This method's signature must look like `__call__(, ...) -> object` - here, any argument list can be defined as for normal pymethods
## Example: Implementing a call counter
The following pyclass is a basic decorator - its constructor takes a Python object as argument and calls that object when called.
An equivalent Python implementation is linked at the end.
An example crate containing this pyclass can be found [in the PyO3 GitHub repository](https://github.com/PyO3/pyo3/tree/main/examples/decorator)
```rust,ignore
{{#include ../../../examples/decorator/src/lib.rs}}
```
Python code:
```python
{{#include ../../../examples/decorator/tests/example.py}}
```
Output:
```text
say_hello has been called 1 time(s).
hello
say_hello has been called 2 time(s).
hello
say_hello has been called 3 time(s).
hello
say_hello has been called 4 time(s).
hello
```
### Pure Python implementation
A Python implementation of this looks similar to the Rust version:
```python
class Counter:
def __init__(self, wraps):
self.count = 0
self.wraps = wraps
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.wraps.__name__} has been called {self.count} time(s)")
self.wraps(*args, **kwargs)
```
Note that it can also be implemented as a higher order function:
```python
def Counter(wraps):
count = 0
def call(*args, **kwargs):
nonlocal count
count += 1
print(f"{wraps.__name__} has been called {count} time(s)")
return wraps(*args, **kwargs)
return call
```
### What is the `AtomicU64` for?
A [previous implementation] used a normal `u64`, which meant it required a `&mut self` receiver to update the count:
```rust,ignore
#[pyo3(signature = (*args, **kwargs))]
fn __call__(
&mut self,
py: Python<'_>,
args: &Bound<'_, PyTuple>,
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult> {
self.count += 1;
let name = self.wraps.getattr(py, "__name__")?;
println!("{} has been called {} time(s).", name, self.count);
// After doing something, we finally forward the call to the wrapped function
let ret = self.wraps.call(py, args, kwargs)?;
// We could do something with the return value of
// the function before returning it
Ok(ret)
}
```
The problem with this is that the `&mut self` receiver means PyO3 has to borrow it exclusively, and hold this borrow across the `self.wraps.call(py, args, kwargs)` call.
This call returns control to the user's Python code which is free to call arbitrary things, *including* the decorated function.
If that happens PyO3 is unable to create a second unique borrow and will be forced to raise an exception.
As a result, something innocent like this will raise an exception:
```py
@Counter
def say_hello():
if say_hello.count < 2:
print(f"hello from decorator")
say_hello()
# RuntimeError: Already borrowed
```
The implementation in this chapter fixes that by never borrowing exclusively; all the methods take `&self` as receivers, of which multiple may exist simultaneously.
This requires a shared counter and the most straightforward way to implement thread-safe interior mutability (e.g. the type does not need to accept `&mut self` to modify the "interior" state) for a `u64` is to use [`AtomicU64`], so that's what is used here.
This shows the dangers of running arbitrary Python code - note that "running arbitrary Python code" can be far more subtle than the example above:
- Python's asynchronous executor may park the current thread in the middle of Python code, even in Python code that *you* control, and let other Python code run.
- Dropping arbitrary Python objects may invoke destructors defined in Python (`__del__` methods).
- Calling Python's C-api (most PyO3 apis call C-api functions internally) may raise exceptions, which may allow Python code in signal handlers to run.
- On the free-threaded build, users might use Python's `threading` module to work with your types simultaneously from multiple OS threads.
This is especially important if you are writing unsafe code; Python code must never be able to cause undefined behavior.
You must ensure that your Rust code is in a consistent state before doing any of the above things.
[previous implementation]: "Thread Safe Decorator · Discussion #2598 · PyO3/pyo3"
[`AtomicU64`]: "AtomicU64 in std::sync::atomic - Rust"
================================================
FILE: guide/src/class/numeric.md
================================================
# Emulating numeric types
At this point we have a `Number` class that we can't actually do any math on!
Before proceeding, we should think about how we want to handle overflows.
There are three obvious solutions:
- We can have infinite precision just like Python's `int`.
However that would be quite boring - we'd be reinventing the wheel.
- We can raise exceptions whenever `Number` overflows, but that makes the API painful to use.
- We can wrap around the boundary of `i32`.
This is the approach we'll take here.
To do that we'll just forward to `i32`'s `wrapping_*` methods.
## Fixing our constructor
Let's address the first overflow, in `Number`'s constructor:
```python
from my_module import Number
n = Number(1 << 1337)
```
```text
Traceback (most recent call last):
File "example.py", line 3, in
n = Number(1 << 1337)
OverflowError: Python int too large to convert to C long
```
Instead of relying on the default [`FromPyObject`] extraction to parse arguments, we can specify our own extraction function, using the `#[pyo3(from_py_with = ...)]` attribute.
Unfortunately PyO3 doesn't provide a way to wrap Python integers out of the box, but we can do a Python call to mask it and cast it to an `i32`.
```rust,no_run
# #![allow(dead_code)]
use pyo3::prelude::*;
fn wrap(obj: &Bound<'_, PyAny>) -> PyResult {
let val = obj.call_method1("__and__", (0xFFFFFFFF_u32,))?;
let val: u32 = val.extract()?;
// 👇 This intentionally overflows!
Ok(val as i32)
}
```
We also add documentation, via `///` comments, which are visible to Python users.
```rust,no_run
# #![allow(dead_code)]
use pyo3::prelude::*;
fn wrap(obj: &Bound<'_, PyAny>) -> PyResult {
let val = obj.call_method1("__and__", (0xFFFFFFFF_u32,))?;
let val: u32 = val.extract()?;
Ok(val as i32)
}
/// Did you ever hear the tragedy of Darth Signed The Overfloweth? I thought not.
/// It's not a story C would tell you. It's a Rust legend.
#[pyclass(module = "my_module")]
struct Number(i32);
#[pymethods]
impl Number {
#[new]
fn new(#[pyo3(from_py_with = wrap)] value: i32) -> Self {
Self(value)
}
}
```
With that out of the way, let's implement some operators:
```rust,no_run
use pyo3::exceptions::{PyZeroDivisionError, PyValueError};
# use pyo3::prelude::*;
#
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __add__(&self, other: &Self) -> Self {
Self(self.0.wrapping_add(other.0))
}
fn __sub__(&self, other: &Self) -> Self {
Self(self.0.wrapping_sub(other.0))
}
fn __mul__(&self, other: &Self) -> Self {
Self(self.0.wrapping_mul(other.0))
}
fn __truediv__(&self, other: &Self) -> PyResult {
match self.0.checked_div(other.0) {
Some(i) => Ok(Self(i)),
None => Err(PyZeroDivisionError::new_err("division by zero")),
}
}
fn __floordiv__(&self, other: &Self) -> PyResult {
match self.0.checked_div(other.0) {
Some(i) => Ok(Self(i)),
None => Err(PyZeroDivisionError::new_err("division by zero")),
}
}
fn __rshift__(&self, other: &Self) -> PyResult {
match other.0.try_into() {
Ok(rhs) => Ok(Self(self.0.wrapping_shr(rhs))),
Err(_) => Err(PyValueError::new_err("negative shift count")),
}
}
fn __lshift__(&self, other: &Self) -> PyResult {
match other.0.try_into() {
Ok(rhs) => Ok(Self(self.0.wrapping_shl(rhs))),
Err(_) => Err(PyValueError::new_err("negative shift count")),
}
}
}
```
### Unary arithmetic operations
```rust,no_run
# use pyo3::prelude::*;
#
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __pos__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
slf
}
fn __neg__(&self) -> Self {
Self(-self.0)
}
fn __abs__(&self) -> Self {
Self(self.0.abs())
}
fn __invert__(&self) -> Self {
Self(!self.0)
}
}
```
### Support for the `complex()`, `int()` and `float()` built-in functions
```rust,no_run
# use pyo3::prelude::*;
#
# #[pyclass]
# struct Number(i32);
#
use pyo3::types::PyComplex;
#[pymethods]
impl Number {
fn __int__(&self) -> i32 {
self.0
}
fn __float__(&self) -> f64 {
self.0 as f64
}
fn __complex__<'py>(&self, py: Python<'py>) -> Bound<'py, PyComplex> {
PyComplex::from_doubles(py, self.0 as f64, 0.0)
}
}
```
We do not implement the in-place operations like `__iadd__` because we do not wish to mutate `Number`.
Similarly we're not interested in supporting operations with different types, so we do not implement the reflected operations like `__radd__` either.
Now Python can use our `Number` class:
```python
from my_module import Number
def hash_djb2(s: str):
'''
A version of Daniel J. Bernstein's djb2 string hashing algorithm
Like many hashing algorithms, it relies on integer wrapping.
'''
n = Number(0)
five = Number(5)
for x in s:
n = Number(ord(x)) + ((n << five) - n)
return n
assert hash_djb2('l50_50') == Number(-1152549421)
```
### Final code
```rust
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use pyo3::exceptions::{PyValueError, PyZeroDivisionError};
use pyo3::prelude::*;
use pyo3::class::basic::CompareOp;
use pyo3::types::{PyComplex, PyString};
fn wrap(obj: &Bound<'_, PyAny>) -> PyResult {
let val = obj.call_method1("__and__", (0xFFFFFFFF_u32,))?;
let val: u32 = val.extract()?;
Ok(val as i32)
}
/// Did you ever hear the tragedy of Darth Signed The Overfloweth? I thought not.
/// It's not a story C would tell you. It's a Rust legend.
#[pyclass(module = "my_module")]
struct Number(i32);
#[pymethods]
impl Number {
#[new]
fn new(#[pyo3(from_py_with = wrap)] value: i32) -> Self {
Self(value)
}
fn __repr__(slf: &Bound<'_, Self>) -> PyResult {
// Get the class name dynamically in case `Number` is subclassed
let class_name: Bound<'_, PyString> = slf.get_type().qualname()?;
Ok(format!("{}({})", class_name, slf.borrow().0))
}
fn __str__(&self) -> String {
self.0.to_string()
}
fn __hash__(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.0.hash(&mut hasher);
hasher.finish()
}
fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult {
match op {
CompareOp::Lt => Ok(self.0 < other.0),
CompareOp::Le => Ok(self.0 <= other.0),
CompareOp::Eq => Ok(self.0 == other.0),
CompareOp::Ne => Ok(self.0 != other.0),
CompareOp::Gt => Ok(self.0 > other.0),
CompareOp::Ge => Ok(self.0 >= other.0),
}
}
fn __bool__(&self) -> bool {
self.0 != 0
}
fn __add__(&self, other: &Self) -> Self {
Self(self.0.wrapping_add(other.0))
}
fn __sub__(&self, other: &Self) -> Self {
Self(self.0.wrapping_sub(other.0))
}
fn __mul__(&self, other: &Self) -> Self {
Self(self.0.wrapping_mul(other.0))
}
fn __truediv__(&self, other: &Self) -> PyResult {
match self.0.checked_div(other.0) {
Some(i) => Ok(Self(i)),
None => Err(PyZeroDivisionError::new_err("division by zero")),
}
}
fn __floordiv__(&self, other: &Self) -> PyResult {
match self.0.checked_div(other.0) {
Some(i) => Ok(Self(i)),
None => Err(PyZeroDivisionError::new_err("division by zero")),
}
}
fn __rshift__(&self, other: &Self) -> PyResult {
match other.0.try_into() {
Ok(rhs) => Ok(Self(self.0.wrapping_shr(rhs))),
Err(_) => Err(PyValueError::new_err("negative shift count")),
}
}
fn __lshift__(&self, other: &Self) -> PyResult {
match other.0.try_into() {
Ok(rhs) => Ok(Self(self.0.wrapping_shl(rhs))),
Err(_) => Err(PyValueError::new_err("negative shift count")),
}
}
fn __xor__(&self, other: &Self) -> Self {
Self(self.0 ^ other.0)
}
fn __or__(&self, other: &Self) -> Self {
Self(self.0 | other.0)
}
fn __and__(&self, other: &Self) -> Self {
Self(self.0 & other.0)
}
fn __int__(&self) -> i32 {
self.0
}
fn __float__(&self) -> f64 {
self.0 as f64
}
fn __complex__<'py>(&self, py: Python<'py>) -> Bound<'py, PyComplex> {
PyComplex::from_doubles(py, self.0 as f64, 0.0)
}
}
#[pymodule]
mod my_module {
#[pymodule_export]
use super::Number;
}
# const SCRIPT: &'static std::ffi::CStr = cr#"
# def hash_djb2(s: str):
# n = Number(0)
# five = Number(5)
#
# for x in s:
# n = Number(ord(x)) + ((n << five) - n)
# return n
#
# assert hash_djb2('l50_50') == Number(-1152549421)
# assert hash_djb2('logo') == Number(3327403)
# assert hash_djb2('horizon') == Number(1097468315)
#
#
# assert Number(2) + Number(2) == Number(4)
# assert Number(2) + Number(2) != Number(5)
#
# assert Number(13) - Number(7) == Number(6)
# assert Number(13) - Number(-7) == Number(20)
#
# assert Number(13) / Number(7) == Number(1)
# assert Number(13) // Number(7) == Number(1)
#
# assert Number(13) * Number(7) == Number(13*7)
#
# assert Number(13) > Number(7)
# assert Number(13) < Number(20)
# assert Number(13) == Number(13)
# assert Number(13) >= Number(7)
# assert Number(13) <= Number(20)
# assert Number(13) == Number(13)
#
#
# assert (True if Number(1) else False)
# assert (False if Number(0) else True)
#
#
# assert int(Number(13)) == 13
# assert float(Number(13)) == 13
# assert Number.__doc__ == "Did you ever hear the tragedy of Darth Signed The Overfloweth? I thought not.\nIt's not a story C would tell you. It's a Rust legend."
# assert Number(12345234523452) == Number(1498514748)
# try:
# import inspect
# assert inspect.signature(Number).__str__() == '(value)'
# except ValueError:
# # Not supported with `abi3` before Python 3.10
# pass
# assert Number(1337).__str__() == '1337'
# assert Number(1337).__repr__() == 'Number(1337)'
"#;
#
# use pyo3::PyTypeInfo;
#
# fn main() -> PyResult<()> {
# Python::attach(|py| -> PyResult<()> {
# let globals = PyModule::import(py, "__main__")?.dict();
# globals.set_item("Number", Number::type_object(py))?;
#
# py.run(SCRIPT, Some(&globals), None)?;
# Ok(())
# })
# }
```
## Appendix: Writing some unsafe code
At the beginning of this chapter we said that PyO3 doesn't provide a way to wrap Python integers out of the box but that's a half truth.
There's not a PyO3 API for it, but there's a Python C API function that does:
```c
unsigned long PyLong_AsUnsignedLongMask(PyObject *obj)
```
We can call this function from Rust by using [`pyo3::ffi::PyLong_AsUnsignedLongMask`].
This is an *unsafe* function, which means we have to use an unsafe block to call it and take responsibility for upholding the contracts of this function.
Let's review those contracts:
- We must be attached to the interpreter.
If we're not, calling this function causes a data race.
- The pointer must be valid, i.e. it must be properly aligned and point to a valid Python object.
Let's create that helper function.
The signature has to be `fn(&Bound<'_, PyAny>) -> PyResult`.
- `&Bound<'_, PyAny>` represents a checked bound reference, so the pointer derived from it is valid (and not null).
- Whenever we have bound references to Python objects in scope, it is guaranteed that we're attached to the interpreter.
This reference is also where we can get a [`Python`] token to use in our call to [`PyErr::take`].
```rust,no_run
# #![allow(dead_code)]
use std::ffi::c_ulong;
use pyo3::prelude::*;
use pyo3::ffi;
fn wrap(obj: &Bound<'_, PyAny>) -> Result {
let py: Python<'_> = obj.py();
unsafe {
let ptr = obj.as_ptr();
let ret: c_ulong = ffi::PyLong_AsUnsignedLongMask(ptr);
if ret == c_ulong::MAX {
if let Some(err) = PyErr::take(py) {
return Err(err);
}
}
Ok(ret as i32)
}
}
```
[`PyErr::take`]: {{#PYO3_DOCS_URL}}/pyo3/prelude/struct.PyErr.html#method.take
[`Python`]: {{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html
[`FromPyObject`]: {{#PYO3_DOCS_URL}}/pyo3/conversion/trait.FromPyObject.html
[`pyo3::ffi::PyLong_AsUnsignedLongMask`]: {{#PYO3_DOCS_URL}}/pyo3/ffi/fn.PyLong_AsUnsignedLongMask.html
================================================
FILE: guide/src/class/object.md
================================================
# Basic object customization
Recall the `Number` class from the previous chapter:
```rust,no_run
# #![allow(dead_code)]
# fn main() {}
use pyo3::prelude::*;
#[pyclass]
struct Number(i32);
#[pymethods]
impl Number {
#[new]
fn new(value: i32) -> Self {
Self(value)
}
}
#[pymodule]
mod my_module {
#[pymodule_export]
use super::Number;
}
```
At this point Python code can import the module, access the class and create class instances - but nothing else.
```python
from my_module import Number
n = Number(5)
print(n)
```
```text
```
## String representations
It can't even print an user-readable representation of itself!
We can fix that by defining the `__repr__` and `__str__` methods inside a `#[pymethods]` block.
We do this by accessing the value contained inside `Number`.
```rust,no_run
# use pyo3::prelude::*;
#
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
// For `__repr__` we want to return a string that Python code could use to recreate
// the `Number`, like `Number(5)` for example.
fn __repr__(&self) -> String {
// We use the `format!` macro to create a string. Its first argument is a
// format string, followed by any number of parameters which replace the
// `{}`'s in the format string.
//
// 👇 Tuple field access in Rust uses a dot
format!("Number({})", self.0)
}
// `__str__` is generally used to create an "informal" representation, so we
// just forward to `i32`'s `ToString` trait implementation to print a bare number.
fn __str__(&self) -> String {
self.0.to_string()
}
}
```
To automatically generate the `__str__` implementation using a `Display` trait implementation, pass the `str` argument to `pyclass`.
```rust,no_run
# use std::fmt::{Display, Formatter};
# use pyo3::prelude::*;
#
# #[allow(dead_code)]
#[pyclass(str)]
struct Coordinate {
x: i32,
y: i32,
z: i32,
}
impl Display for Coordinate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {}, {})", self.x, self.y, self.z)
}
}
```
For convenience, a shorthand format string can be passed to `str` as `str=""` for **structs only**.
It expands and is passed into the `format!` macro in the following ways:
- `"{x}"` -> `"{}", self.x`
- `"{0}"` -> `"{}", self.0`
- `"{x:?}"` -> `"{:?}", self.x`
*Note: Depending upon the format string you use, this may require implementation of the `Display` or `Debug` traits for the given Rust types.*
*Note: the pyclass args `name` and `rename_all` are incompatible with the shorthand format string and will raise a compile time error.*
```rust,no_run
# use pyo3::prelude::*;
#
# #[allow(dead_code)]
#[pyclass(str="({x}, {y}, {z})")]
struct Coordinate {
x: i32,
y: i32,
z: i32,
}
```
### Accessing the class name
In the `__repr__`, we used a hard-coded class name.
This is sometimes not ideal, because if the class is subclassed in Python, we would like the repr to reflect the subclass name.
This is typically done in Python code by accessing `self.__class__.__name__`.
In order to be able to access the Python type information *and* the Rust struct, we need to use a `Bound` as the `self` argument.
```rust,no_run
# use pyo3::prelude::*;
# use pyo3::types::PyString;
#
# #[allow(dead_code)]
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __repr__(slf: &Bound<'_, Self>) -> PyResult {
// This is the equivalent of `self.__class__.__name__` in Python.
let class_name: Bound<'_, PyString> = slf.get_type().qualname()?;
// To access fields of the Rust struct, we need to borrow from the Bound object.
Ok(format!("{}({})", class_name, slf.borrow().0))
}
}
```
### Hashing
Let's also implement hashing.
We'll just hash the `i32`.
For that we need a [`Hasher`].
The one provided by `std` is [`DefaultHasher`], which uses the [SipHash] algorithm.
```rust,no_run
use std::collections::hash_map::DefaultHasher;
// Required to call the `.hash` and `.finish` methods, which are defined on traits.
use std::hash::{Hash, Hasher};
# use pyo3::prelude::*;
#
# #[allow(dead_code)]
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __hash__(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.0.hash(&mut hasher);
hasher.finish()
}
}
```
To implement `__hash__` using the Rust [`Hash`] trait implementation, the `hash` option can be used.
This option is only available for `frozen` classes to prevent accidental hash changes from mutating the object.
If you need an `__hash__` implementation for a mutable class, use the manual method from above.
This option also requires `eq`: According to the [Python docs](https://docs.python.org/3/reference/datamodel.html#object.__hash__) "If a class does not define an `__eq__()` method it should not define a `__hash__()` operation either"
```rust,no_run
# use pyo3::prelude::*;
#
# #[allow(dead_code)]
#[pyclass(frozen, eq, hash)]
#[derive(PartialEq, Hash)]
struct Number(i32);
```
> [!NOTE]
> When implementing `__hash__` and comparisons, it is important that the following property holds:
>
> ```text
> k1 == k2 -> hash(k1) == hash(k2)
> ```
>
> In other words, if two keys are equal, their hashes must also be equal. In addition you must take
> care that your classes' hash doesn't change during its lifetime. In this tutorial we do that by not
> letting Python code change our `Number` class. In other words, it is immutable.
>
> By default, all `#[pyclass]` types have a default hash implementation from Python.
> Types which should not be hashable can override this by setting `__hash__` to None.
> This is the same mechanism as for a pure-Python class. This is done like so:
>
> ```rust,no_run
> # use pyo3::prelude::*;
> #[pyclass]
> struct NotHashable {}
>
> #[pymethods]
> impl NotHashable {
> #[classattr]
> const __hash__: Option> = None;
> }
> ```
### Comparisons
PyO3 supports the usual magic comparison methods available in Python such as `__eq__`, `__lt__` and so on.
It is also possible to support all six operations at once with `__richcmp__`.
This method will be called with a value of `CompareOp` depending on the operation.
```rust,no_run
use pyo3::class::basic::CompareOp;
# use pyo3::prelude::*;
#
# #[allow(dead_code)]
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult {
match op {
CompareOp::Lt => Ok(self.0 < other.0),
CompareOp::Le => Ok(self.0 <= other.0),
CompareOp::Eq => Ok(self.0 == other.0),
CompareOp::Ne => Ok(self.0 != other.0),
CompareOp::Gt => Ok(self.0 > other.0),
CompareOp::Ge => Ok(self.0 >= other.0),
}
}
}
```
If you obtain the result by comparing two Rust values, as in this example, you can take a shortcut using `CompareOp::matches`:
```rust,no_run
use pyo3::class::basic::CompareOp;
# use pyo3::prelude::*;
#
# #[allow(dead_code)]
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
op.matches(self.0.cmp(&other.0))
}
}
```
It checks that the `std::cmp::Ordering` obtained from Rust's `Ord` matches the given `CompareOp`.
Alternatively, you can implement just equality using `__eq__`:
```rust
# use pyo3::prelude::*;
#
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __eq__(&self, other: &Self) -> bool {
self.0 == other.0
}
}
# fn main() -> PyResult<()> {
# Python::attach(|py| {
# let x = &Bound::new(py, Number(4))?;
# let y = &Bound::new(py, Number(4))?;
# assert!(x.eq(y)?);
# assert!(!x.ne(y)?);
# Ok(())
# })
# }
```
To implement `__eq__` using the Rust [`PartialEq`] trait implementation, the `eq` option can be used.
```rust,no_run
# use pyo3::prelude::*;
#
# #[allow(dead_code)]
#[pyclass(eq)]
#[derive(PartialEq)]
struct Number(i32);
```
To implement `__lt__`, `__le__`, `__gt__`, & `__ge__` using the Rust `PartialOrd` trait implementation, the `ord` option can be used.
*Note: Requires `eq`.*
```rust,no_run
# use pyo3::prelude::*;
#
# #[allow(dead_code)]
#[pyclass(eq, ord)]
#[derive(PartialEq, PartialOrd)]
struct Number(i32);
```
### Truthyness
We'll consider `Number` to be `True` if it is nonzero:
```rust,no_run
# use pyo3::prelude::*;
#
# #[allow(dead_code)]
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __bool__(&self) -> bool {
self.0 != 0
}
}
```
### Final code
```rust,no_run
# fn main() {}
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use pyo3::prelude::*;
use pyo3::class::basic::CompareOp;
use pyo3::types::PyString;
#[pyclass]
struct Number(i32);
#[pymethods]
impl Number {
#[new]
fn new(value: i32) -> Self {
Self(value)
}
fn __repr__(slf: &Bound<'_, Self>) -> PyResult {
let class_name: Bound<'_, PyString> = slf.get_type().qualname()?;
Ok(format!("{}({})", class_name, slf.borrow().0))
}
fn __str__(&self) -> String {
self.0.to_string()
}
fn __hash__(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.0.hash(&mut hasher);
hasher.finish()
}
fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult {
match op {
CompareOp::Lt => Ok(self.0 < other.0),
CompareOp::Le => Ok(self.0 <= other.0),
CompareOp::Eq => Ok(self.0 == other.0),
CompareOp::Ne => Ok(self.0 != other.0),
CompareOp::Gt => Ok(self.0 > other.0),
CompareOp::Ge => Ok(self.0 >= other.0),
}
}
fn __bool__(&self) -> bool {
self.0 != 0
}
}
#[pymodule]
mod my_module {
#[pymodule_export]
use super::Number;
}
```
[`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
[`Hasher`]: https://doc.rust-lang.org/std/hash/trait.Hasher.html
[`DefaultHasher`]: https://doc.rust-lang.org/std/collections/hash_map/struct.DefaultHasher.html
[SipHash]: https://en.wikipedia.org/wiki/SipHash
[`PartialEq`]: https://doc.rust-lang.org/stable/std/cmp/trait.PartialEq.html
================================================
FILE: guide/src/class/protocols.md
================================================
# Class customizations
Python's object model defines several protocols for different object behavior, such as the sequence, mapping, and number protocols.
Python classes support these protocols by implementing "magic" methods, such as `__str__` or `__repr__`.
Because of the double-underscores surrounding their name, these are also known as "dunder" methods.
PyO3 makes it possible for every magic method to be implemented in `#[pymethods]` just as they would be done in a regular Python class, with a few notable differences:
- `__new__` is replaced by the [`#[new]` attribute](../class.md#constructor).
- `__del__` is not yet supported, but may be in the future.
- `__buffer__` and `__release_buffer__` are currently not supported and instead PyO3 supports [`__getbuffer__` and `__releasebuffer__`](#buffer-objects) methods (these predate [PEP 688](https://peps.python.org/pep-0688/#python-level-buffer-protocol)), again this may change in the future.
- PyO3 adds [`__traverse__` and `__clear__`](#garbage-collector-integration) methods for controlling garbage collection.
- The Python C-API which PyO3 is implemented upon requires many magic methods to have a specific function signature in C and be placed into special "slots" on the class type object.
This limits the allowed argument and return types for these methods.
They are listed in detail in the section below.
If a magic method is not on the list above (for example `__init_subclass__`), then it should just work in PyO3.
If this is not the case, please file a bug report.
## Magic Methods handled by PyO3
If a function name in `#[pymethods]` is a magic method which is known to need special handling, it will be automatically placed into the correct slot in the Python type object.
The function name is taken from the usual rules for naming `#[pymethods]`: the `#[pyo3(name = "...")]` attribute is used if present, otherwise the Rust function name is used.
The magic methods handled by PyO3 are very similar to the standard Python ones on [this page](https://docs.python.org/3/reference/datamodel.html#special-method-names) - in particular they are the subset which have slots as [defined here](https://docs.python.org/3/c-api/typeobj.html).
When PyO3 handles a magic method, a couple of changes apply compared to other `#[pymethods]`:
- The Rust function signature is restricted to match the magic method.
- The `#[pyo3(signature = (...)]` and `#[pyo3(text_signature = "...")]` attributes are not allowed.
The following sections list all magic methods for which PyO3 implements the necessary special handling.
The given signatures should be interpreted as follows:
- All methods take a receiver as first argument, shown as ``.
It can be `&self`, `&mut self` or a `Bound` reference like `self_: PyRef<'_, Self>` and `self_: PyRefMut<'_, Self>`, as described [in the parent section](../class.md#inheritance).
- An optional `Python<'py>` argument is always allowed as the first argument.
- Return values can be optionally wrapped in `PyResult`.
- `object` means that any type is allowed that can be extracted from a Python
object (if argument) or converted to a Python object (if return value).
- Other types must match what's given, e.g. `pyo3::basic::CompareOp` for
`__richcmp__`'s second argument.
- For the comparison and arithmetic methods, extraction errors are not
propagated as exceptions, but lead to a return of `NotImplemented`.
- For some magic methods, the return values are not restricted by PyO3, but checked by the Python interpreter.
For example, `__str__` needs to return a string object.
This is indicated by `object (Python type)`.
### Basic object customization
- `__str__() -> object (str)`
- `__repr__() -> object (str)`
- `__hash__() -> isize`
Objects that compare equal must have the same hash value.
Any type up to 64 bits may be returned instead of `isize`, PyO3 will convert to an isize automatically (wrapping unsigned types like `u64` and `usize`).
Disabling Python's default hash
By default, all `#[pyclass]` types have a default hash implementation from Python.
Types which should not be hashable can override this by setting `__hash__` to `None`.
This is the same mechanism as for a pure-Python class.
This is done like so:
```rust,no_run
# use pyo3::prelude::*;
#
#[pyclass]
struct NotHashable {}
#[pymethods]
impl NotHashable {
#[classattr]
const __hash__: Option> = None;
}
```
- `__lt__(, object) -> object`
- `__le__(, object) -> object`
- `__eq__(, object) -> object`
- `__ne__(, object) -> object`
- `__gt__(, object) -> object`
- `__ge__(, object) -> object`
The implementations of Python's "rich comparison" operators `<`, `<=`, `==`, `!=`, `>` and `>=` respectively.
*Note that implementing any of these methods will cause Python not to generate a default `__hash__` implementation, so consider also implementing `__hash__`.*
Return type
The return type will normally be `bool` or `PyResult`, however any Python object can be returned.
- `__richcmp__(, object, pyo3::basic::CompareOp) -> object`
Implements Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`) in a single method.
The `CompareOp` argument indicates the comparison operation being performed.
You can use [`CompareOp::matches`] to adapt a Rust `std::cmp::Ordering` result to the requested comparison.
*This method cannot be implemented in combination with any of `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__`, or `__ge__`.*
*Note that implementing `__richcmp__` will cause Python not to generate a default `__hash__` implementation, so consider implementing `__hash__` when implementing `__richcmp__`.*
Return type
The return type will normally be `PyResult`, but any Python object can be returned.
If you want to leave some operations unimplemented, you can return `py.NotImplemented()`
for some of the operations:
```rust,no_run
use pyo3::class::basic::CompareOp;
use pyo3::types::PyNotImplemented;
# use pyo3::prelude::*;
# use pyo3::BoundObject;
#
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __richcmp__<'py>(&self, other: &Self, op: CompareOp, py: Python<'py>) -> PyResult> {
match op {
CompareOp::Eq => Ok((self.0 == other.0).into_pyobject(py)?.into_any()),
CompareOp::Ne => Ok((self.0 != other.0).into_pyobject(py)?.into_any()),
_ => Ok(PyNotImplemented::get(py).into_any()),
}
}
}
```
If the second argument `object` is not of the type specified in the signature, the generated code will automatically `return NotImplemented`.
- `__getattr__(, object) -> object`
- `__getattribute__(, object) -> object`
Differences between __getattr__ and __getattribute__
As in Python, `__getattr__` is only called if the attribute is not found
by normal attribute lookup. `__getattribute__`, on the other hand, is
called for *every* attribute access. If it wants to access existing
attributes on `self`, it needs to be very careful not to introduce
infinite recursion, and use `baseclass.__getattribute__()`.
- `__setattr__(, value: object) -> ()`
- `__delattr__(, object) -> ()`
Overrides attribute access.
- `__bool__() -> bool`
Determines the "truthyness" of an object.
- `__call__(, ...) -> object` - here, any argument list can be defined
as for normal `pymethods`
### Iterable objects
Iterators can be defined using these methods:
- `__iter__() -> object`
- `__next__() -> Option or IterNextOutput` ([see details](#returning-a-value-from-iteration))
Returning `None` from `__next__` indicates that that there are no further items.
Example:
```rust,no_run
use pyo3::prelude::*;
use std::sync::Mutex;
#[pyclass]
struct MyIterator {
iter: Mutex> + Send>>,
}
#[pymethods]
impl MyIterator {
fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
slf
}
fn __next__(slf: PyRefMut<'_, Self>) -> Option> {
slf.iter.lock().unwrap().next()
}
}
```
In many cases you'll have a distinction between the type being iterated over (i.e. the *iterable*) and the iterator it provides.
In this case, the iterable only needs to implement `__iter__()` while the iterator must implement both `__iter__()` and `__next__()`.
For example:
```rust,no_run
# use pyo3::prelude::*;
#[pyclass]
struct Iter {
inner: std::vec::IntoIter,
}
#[pymethods]
impl Iter {
fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
slf
}
fn __next__(mut slf: PyRefMut<'_, Self>) -> Option {
slf.inner.next()
}
}
#[pyclass]
struct Container {
iter: Vec,
}
#[pymethods]
impl Container {
fn __iter__(slf: PyRef<'_, Self>) -> PyResult> {
let iter = Iter {
inner: slf.iter.clone().into_iter(),
};
Py::new(slf.py(), iter)
}
}
# Python::attach(|py| {
# let container = Container { iter: vec![1, 2, 3, 4] };
# let inst = pyo3::Py::new(py, container).unwrap();
# pyo3::py_run!(py, inst, "assert list(inst) == [1, 2, 3, 4]");
# pyo3::py_run!(py, inst, "assert list(iter(iter(inst))) == [1, 2, 3, 4]");
# });
```
For more details on Python's iteration protocols, check out [the "Iterator Types" section of the library documentation](https://docs.python.org/library/stdtypes.html#iterator-types).
#### Returning a value from iteration
This guide has so far shown how to use `Option` to implement yielding values during iteration.
In Python a generator can also return a value.
This is done by raising a `StopIteration` exception.
To express this in Rust, return `PyResult::Err` with a `PyStopIteration` as the error.
### Awaitable objects
- `__await__() -> object`
- `__aiter__() -> object`
- `__anext__() -> Option`
### Mapping & Sequence types
The magic methods in this section can be used to implement Python container types.
They are two main categories of container in Python: "mappings" such as `dict`, with arbitrary keys, and "sequences" such as `list` and `tuple`, with integer keys.
The Python C-API which PyO3 is built upon has separate "slots" for sequences and mappings.
When writing a `class` in pure Python, there is no such distinction in the implementation - a `__getitem__` implementation will fill the slots for both the mapping and sequence forms, for example.
By default PyO3 reproduces the Python behaviour of filling both mapping and sequence slots.
This makes sense for the "simple" case which matches Python, and also for sequences, where the mapping slot is used anyway to implement slice indexing.
Mapping types usually will not want the sequence slots filled.
Having them filled will lead to outcomes which may be unwanted, such as:
- The mapping type will successfully cast to [`PySequence`].
This may lead to consumers of the type handling it incorrectly.
- Python provides a default implementation of `__iter__` for sequences, which calls `__getitem__` with consecutive positive integers starting from 0 until an `IndexError` is returned.
Unless the mapping only contains consecutive positive integer keys, this `__iter__` implementation will likely not be the intended behavior.
Use the `#[pyclass(mapping)]` annotation to instruct PyO3 to only fill the mapping slots, leaving the sequence ones empty.
This will apply to `__getitem__`, `__setitem__`, and `__delitem__`.
Use the `#[pyclass(sequence)]` annotation to instruct PyO3 to fill the `sq_length` slot instead of the `mp_length` slot for `__len__`.
This will help libraries such as `numpy` recognise the class as a sequence, however will also cause CPython to automatically add the sequence length to any negative indices before passing them to `__getitem__`. (`__getitem__`, `__setitem__` and `__delitem__` mapping slots are still used for sequences, for slice operations.)
- `__len__() -> usize`
Implements the built-in function `len()`.
- `__contains__(, object) -> bool`
Implements membership test operators.
Should return true if `item` is in `self`, false otherwise.
For objects that don’t define `__contains__()`, the membership test simply traverses the sequence until it finds a match.
Disabling Python's default contains
By default, all `#[pyclass]` types with an `__iter__` method support a
default implementation of the `in` operator. Types which do not want this
can override this by setting `__contains__` to `None`. This is the same
mechanism as for a pure-Python class. This is done like so:
```rust,no_run
# use pyo3::prelude::*;
#
#[pyclass]
struct NoContains {}
#[pymethods]
impl NoContains {
#[classattr]
const __contains__: Option> = None;
}
```
- `__getitem__(, object) -> object`
Implements retrieval of the `self[a]` element.
*Note:* Negative integer indexes are not handled specially by PyO3.
However, for classes with `#[pyclass(sequence)]`, when a negative index is accessed via `PySequence::get_item`, the underlying C API already adjusts the index to be positive.
- `__setitem__(, object, object) -> ()`
Implements assignment to the `self[a]` element.
Should only be implemented if elements can be replaced.
Same behavior regarding negative indices as for `__getitem__`.
- `__delitem__(