Compare commits

...

714 Commits

Author SHA1 Message Date
David Hewitt a5a3f3f7f2
allow `#[pymodule(...)]` to accept all relevant `#[pyo3(...)]` options (#4330) 2024-07-10 22:38:38 +00:00
Larry Z 6be80647cb
Prevent building in GIL-less environment (#4327)
* Prevent building in GIL-less environment

* Add change log

* add "yet" to phrasing

* Add testing to build script

* add link to issue

* Fix formatting issues

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-07-10 18:39:39 +00:00
David Hewitt 90c4799951
docs: fixups to 0.22 migration guide (#4332) 2024-07-10 14:18:13 +00:00
David Hewitt 3c65132da5
remove all functionality deprecated in PyO3 0.21 (#4323)
* remove all functionality deprecated in PyO3 0.21

* further adjustments after removing deprecated APIs
2024-07-10 13:38:30 +00:00
David Hewitt 32b6a1aef1
docs: use versioned links from docs to guide (#4331)
* use versioned links from docs to guide

* use standard python version in `guide-build` ci job
2024-07-10 12:30:01 +00:00
David Hewitt c7c1dff710
ci: check minimal-versions on MSRV feature powerset (#4273)
* ci: check minimal-versions on MSRV feature powerset

* fix msrv arg

* bump num-bigint floor to 0.4.2

* try fix build syntax
2024-07-10 11:36:31 +00:00
Giovanni Barillari 1279232701
Add Granian to example projects (#4329) 2024-07-10 09:16:23 +00:00
David Hewitt 97d60e869b
clean up hygiene tests (#4328) 2024-07-09 21:28:15 +00:00
David Hewitt 8652ac8e1c
remove all functionality deprecated in 0.20 (#4322)
* remove all functionality deprecated in 0.20

* bump version to 0.23.0-dev

* undo unintended revert

* fixup UI test
2024-07-09 16:44:27 +00:00
Alex Gaynor e73112f3f6
Automatically treat nested modules as submodules (#4308)
fixes #4286
2024-07-09 12:15:12 +00:00
David Hewitt 186c7d3315
docs: improve signposting to bound and traits (#4325)
* docs: improve signposting to bound and traits

* update UI tests
2024-07-09 11:53:11 +00:00
Sede Soukossi 1861d6d379
docs: Fix mismatch return value, remove redundant error propagation, and additional fixes (#4318)
* Fix mismatch return value, remove redundant error propagation, and fix typo

* Update guide/src/function/signature.md

Co-authored-by: Lily Foote <code@lilyf.org>

---------

Co-authored-by: Lily Foote <code@lilyf.org>
2024-07-08 20:30:44 +00:00
Icxolu 3c155d9fef
remove the gil-ref deprecations infrastructure (#4320) 2024-07-08 13:40:27 +00:00
David Hewitt d5c886f4c0
simplify implementation of `Py::clone_ref` (#4313) 2024-07-07 06:53:43 +00:00
David Hewitt 59c4fa3f24
release: 0.22.1 (#4314) 2024-07-06 22:00:28 +00:00
Alex Gaynor 9afc38ae41
fixes #4285 -- allow full-path to pymodule with nested declarative modules (#4288) 2024-07-05 09:16:06 +00:00
Owen Leung 5860c4f7e9
implement PartialEq for Pybool & bool (#4305)
* implement PartialEq for Pybool

implement PartialEq for Pybool

* fix failing cargodoc and add changelog file

* Use PR number for change log file

* Add false checking into test
2024-07-05 09:10:38 +00:00
David Hewitt 0af0227834
fix deprecation warning for trailing optional on `#[setter]` functions (#4304)
* fix deprecation warning for trailing optional on `#[setter]` functions

* add comment
2024-07-04 10:08:22 +00:00
Nathan Goldbaum ee9123a2d2
Fix link in the contribution guide (#4306) 2024-07-02 19:28:26 +00:00
Alex Gaynor ccd04475a3
refs #4286 -- allow setting submodule on declarative pymodules (#4301) 2024-07-02 11:24:47 +00:00
Alex Gaynor f3603a0a48
Avoid generating functions that are only ever const evaluated with declarative modules (#4297)
Refs #4286
2024-07-01 21:54:50 +00:00
Kyle Barron 872bd7e6f7
Add pyo3-arrow to README (#4302) 2024-07-01 16:26:17 +00:00
Ben Beasley 8f7450e33d
Fix 128-bit int regression on big-endian with Python <3.13 (#4291)
Fixes #4290.
2024-06-26 19:21:31 +00:00
jatoben 7c2f5e80de
Don't raise `TypeError` from generated equality method (#4287)
* Don't raise TypeError in derived equality method

* Add newsfragment
2024-06-26 05:41:42 +00:00
David Hewitt 2e2d4404a6
release: 0.22.0 (#4266) 2024-06-24 19:06:42 +00:00
David Hewitt 91d8683814
improve deprecation message on implicit trailing optionals (#4282) 2024-06-24 13:38:33 +00:00
Bruno Kolenbrander 6a0221ba2c
document FnType and refactor FnType::self_arg (#4276) 2024-06-23 10:36:19 +00:00
Aneesh Agrawal c67625d683
Revamp PyType name functions to match PEP 737 (#4196)
* Revamp PyType name functions to match PEP 737

PyType::name uses `tp_name`, which is not consistent.
[PEP 737](https://peps.python.org/pep-0737/) adds a new path forward,
so update PyType::name and add PyType::{module,fully_qualified_name}
to match the PEP.

* refactor conditional code to handle multiple Python versions better

* return `Bound<'py, str>`

* fixup

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-06-22 22:10:27 +00:00
David Hewitt a2f9399906
make datetime from timestamp tests compare against Python result (#4275)
* attemp to fix range for st.datetime

* remove example

* try fixing to utc

* make datetime from timestamp tests compare against Python result

---------

Co-authored-by: Cheukting <cheukting.ho@gmail.com>
2024-06-22 22:09:59 +00:00
Code Apprentice 908ef6ad84
Implement PartialEq for PyBytes and [u8] (#4259)
* Copy pasta implementation from types/string.rs

* changelog

* I think I don't need a special implementation for 3.10 or ABI

* Copy pasta tests

* Fix comment with correct type

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Fix implementation

* Use slice in tests

* Try renaming changelog file

* Fix doc example

* Fix doc example

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
2024-06-22 22:08:57 +00:00
Weijie Guo c4d18e5ee3
Change `search_lib_dir`'s return type to Result (#4043)
* Change `search_lib_dir`'s return type to Result

* add changelog

* add coverage and a hint to `PYO3_CROSS_LIB_DIR`

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-06-22 14:01:33 +00:00
David Hewitt 0b967d427a
use `ffi::MemberGef` for `#[pyo3(get)]` fields of `Py<T>` (#4254)
* use `ffi::MemberGef` for `#[pyo3(get)]` fields of `Py<T>`

* tidy up implementation

* make it work on MSRV :(

* fix docs and newsfragment

* clippy

* internal docs and coverage

* review: mejrs
2024-06-21 23:33:34 +00:00
David Hewitt 41fb9572b6
docs: update docstring on `Python` for `Bound` API (#4274) 2024-06-21 11:30:57 +00:00
Alex Gaynor 9ff3d237c1
Update dependencies to reflect minimal versions (#4272)
Based on testing locally with `rm -f Cargo.lock && cargo +nightly check --tests --all -Z minimal-versions`
2024-06-21 10:05:09 +00:00
Icxolu 56341cbc81
emit c-string literals on Rust 1.77 or later (#4269)
* emit c-string literals on Rust 1.77 or later

* only clone `PyO3CratePath` instead of the whole `Ctx`

Co-authored-by: mejrs <59372212+mejrs@users.noreply.github.com>

---------

Co-authored-by: mejrs <59372212+mejrs@users.noreply.github.com>
Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-06-21 08:49:33 +00:00
David Hewitt ca82681615
ci: minor cleanups following 1.63 MSRV (#4239)
* ci: minor cleanups following 1.63 MSRV

* correct `invalid_pymethods_duplicates` UI test

* fix `nightly` feature
2024-06-21 07:02:31 +00:00
Bruno Kolenbrander a983b2fe7b
Bump diagnostic_namespace rust version (#4268) 2024-06-20 21:56:17 +00:00
David Hewitt 30add032b5
improve code generated by `c_str!` macro (#4270)
* improve code generated by `c_str!` macro

* fix clippy
2024-06-20 21:41:16 +00:00
Bruno Kolenbrander b25b3b3a7b
Improve the span and message for return types of pymethod/functions (#4220)
* Improve the span and message for return types of pymethod/functions

* Don't pass the span

* fixup trybuild output

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-06-20 09:23:40 +00:00
WÁNG Xuěruì e6b2216b04
Add several missing wrappers to PyAnyMethods (#4264) 2024-06-20 08:16:06 +00:00
David Hewitt 0e142f05dd
add `c_str!` macro to create `&'static CStr` (#4255)
* add `c_str!` macro to create `&'static CStr`

* newsfragment, just export as `pyo3::ffi::c_str`

* fix doc link

* fix doc

* further `c_str!` based cleanups

* [review]: mejrs

Co-authored-by: Bruno Kolenbrander <59372212+mejrs@users.noreply.github.com>

* rustfmt

* build fixes

* clippy

* allow lint on MSRV

* fix GraalPy import

---------

Co-authored-by: Bruno Kolenbrander <59372212+mejrs@users.noreply.github.com>
2024-06-18 18:09:36 +00:00
Alex Gaynor ddff8bea25
Stabilize declarative modules (#4257) 2024-06-17 01:28:20 +00:00
Alex Gaynor baae9291cc
Update tests for numpy 2.0 (#4258) 2024-06-17 00:05:55 +00:00
Code Apprentice 79591f2161
Add error messages for unsupported macro features on compilation (#4194)
* First implementation

* tweak error message wording

* Fix boolean logic

* Remove redundant parens

* Add test for weakref error

* Fix test

* Reword error message

* Add expected error output

* Rinse and repeat for `dict`

* Add test output file

* Ignore Rust Rover config files

* cargo fmt

* Add newsfragment

* Update newsfragments/4194.added.md

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Use ensure_spanned! macro

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Use ensure_spanned! macro for weakref error, too

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Revert "Ignore Rust Rover config files"

This reverts commit 6c8a2eec581ed250ec792d8465772d649b0a3199.

* Update wording for error message

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Update weakref error message, too

* Refactor constant to a pyversions module

* Fix compiler errors

* Another wording update

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* And make weakref wording the same

* Fix compiler error due to using weakref in our own code

* Fix after merge

* apply conditional pyclass

* update conditional compilation in tests

---------

Co-authored-by: cojmeister <luqas.c@gmail.com>
Co-authored-by: David Hewitt <mail@davidhewitt.dev>
Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
2024-06-16 10:23:03 +00:00
David Hewitt 9648d595a5
implement `PartialEq<str>` for `Bound<'py, PyString>` (#4245)
* implement `PartialEq<str>` for `Bound<'py, PyString>`

* fixup conditional code

* document equality semantics for `Bound<'_, PyString>`

* fix doc example
2024-06-16 08:19:21 +00:00
David Hewitt 0b2f19b3c9
fix `__dict__` on Python 3.9 with limited API (#4251)
* fix `__dict__` on Python 3.9 with limited API

* [review] Icxolu suggestions

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* [review] Icxolu

* missing import

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
2024-06-16 07:57:44 +00:00
David Hewitt 591cdb0bf8
implement `PyModuleMethods::filename` on PyPy (#4249) 2024-06-14 19:08:35 +00:00
Icxolu 5749a08b63
ci: updates for Rust 1.79 (#4244)
* ci: updates for Rust 1.79

* ci: fix beta clippy

* ci: fix `dead_code` warning on nightly
2024-06-13 18:24:13 +00:00
David Hewitt f66124a79b
pypy/graalpy: set `Py_LIMITED_API` when `abi3` requested (#4237)
* pypy/graalpy: set `Py_LIMITED_API` when `abi3` requested

* add newsfragment
2024-06-10 07:26:05 +00:00
JRRudy1 d2dca2169c
Added `as_super` methods to `PyRef` and `PyRefMut`. (#4219)
* Added `PyRef::as_super` and `PyRefMut::as_super` methods, including docstrings and tests. The implementation of these methods also required adding `#[repr(transparent)]` to the `PyRef` and `PyRefMut` structs.

* Added newsfragment entry.

* Changed the `AsRef<U>`/`AsMut<U>` impls for `PyRef` and `PyRefMut` to use the new `as_super` methods. Added the `PyRefMut::downgrade` associated function for converting `&PyRefMut` to `&PyRef`. Updated tests and docstrings to better demonstrate the new functionality.

* Fixed newsfragment filename.

* Removed unnecessary re-borrows flagged by clippy.

* retrigger checks

* Updated `PyRef::as_super`, `PyRefMut::as_super`, and `PyRefMut::downgrade` to use `.cast()` instead of `as _` pointer casts. Fixed typo.

* Updated `PyRef::as_super` and `PyRefMut::downgrade` to use `ptr_from_ref` for the initial cast to `*const _` instead of `as _` casts.

* Added `pyo3::internal_tricks::ptr_from_mut` function alongside the `ptr_from_ref` added in PR #4240. Updated `PyRefMut::as_super` to use this method instead of `as *mut _`.

* Updated the user guide to recommend `as_super` for accessing the base class instead of `as_ref`, and updated the subsequent example/doctest to demonstrate this functionality.

* Improved tests for the `as_super` methods.

* Updated newsfragment to include additional changes.

* Fixed formatting.

---------

Co-authored-by: jrudolph <jrudolph@anl.gov>
2024-06-09 07:17:23 +00:00
David Hewitt 9c67057745
refactor: use `ptr_from_ref` and ptr `.cast()` (#4240)
* refactor: use `ptr_from_ref` and ptr `.cast()`

* fix unused imports
2024-06-07 22:47:27 +00:00
Michael Gilbert b8fb367582
feat: Add 'ord' option for PyClass and corresponding tests (#4202)
* feat: Add 'ord' option for PyClass and corresponding tests

Updated the macros back-end to include 'ord' as an option for PyClass allowing for Python-style ordering comparison of enum variants. Additionally, test cases to verify the proper functioning of this new feature have been introduced.

* update: fix formatting with cargo fmt

* update: documented added feature in newsfragments

* update: updated saved errors for comparison test for invalid pyclass args

* update: removed nested match arms and extended cases for ordering instead

* update: alphabetically ordered entries

* update: added section to class documentation with example for using ord argument.

* refactor: reduced duplication of code using closure to process tokens.

* update: used ensure_spanned macro to emit compile time errors for uses of ord on complex enums or structs, updated test errors for bad compile cases

* fix: remove errant character

* update: added note about PartialOrd being required.

* feat: implemented ordering for structs and complex enums.  Retained the equality logic for simple enums until PartialEq is deprecated.

* update: adjusted compile time error checks for missing PartialOrd implementations.  Refactored growing set of comparison tests for simple and complex enums and structs into separate test file.

* fix: updated with clippy findings

* update: added not to pyclass parameters on ord (assumes that eq will be implemented and merged first)

* update: rebased on main after merging of `eq` feature

* update: format update

* update: update all test output and doc tests

* Update guide/src/class.md

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Update pyo3-macros-backend/src/pyclass.rs

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Update newsfragments/4202.added.md

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Update guide/pyclass-parameters.md

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* update: added note about `ord` implementation with example.

* fix doc formatting

---------

Co-authored-by: Michael Gilbert <git.3mc1o@aleeas.com>
Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
2024-06-07 19:08:53 +00:00
David Hewitt fbb6f20c2b
migration: close all 0.20->0.21 items (#4238) 2024-06-07 14:26:36 +00:00
Thomas Tanon 74619143b6
Declarative modules: make sure to emit doc comments and other attributes (#4236)
* Declarative modules: make sure to emmit doc comments and other attributes

* Adds a test

* Apply suggestions from code review

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-06-06 21:19:37 +00:00
Adam Reichold c644c0b0b8
Lazy-initialize the global reference pool to reduce its overhead when unused (#4178)
* Add benchmarks exercising the global reference count decrement pool.

* Lazy-initialize the global reference pool to reduce its overhead when unused
2024-06-06 08:45:16 +00:00
Thomas Tanon 11d67b3acc
Automated module= field creation in declarative modules (#4213)
* Automated module= field creation in declarative modules

Sets automatically the "module" field of all contained classes and submodules in a declarative module

Adds the "module" field to pymodule attributes in order to set the name of the parent modules. By default, the module is assumed to be a root module

* fix guide test error

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-06-06 07:54:26 +00:00
Cheuk Ting Ho 37a5f6a94e
remove internal APIs from pyo3-ffi (#4201)
* remove internal APIs from pyo3-ffi

* fix formating

* add conditional import

* remove _Py_c_neg/abs/pow

* fix formating

* adding changelog

* expose PyAnyMethods::neg/pos/abs and use them

* Update src/types/any.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Update src/types/any.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Adding details to changelog

* update docs

* remove PyREsultExt import for GraalPy

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-06-05 21:21:44 +00:00
A. Cody Schuffelen 93ef056711
Use `Ident::parse_any` for `name` attributes (#4226)
This makes it possible to use rust keywords as the name of python class
methods and standalone functions. For example:

```
struct MyClass {
}

impl MyClass {
    #[new]
    fn new() -> Self {
        MyClass {}
    }

    #[pyo3(name = "struct")]
    fn struct_method(&self) -> usize {
        42
    }
}

fn struct_function() -> usize {
    42
}
```

From the [`syn::Ident`
documentation](https://docs.rs/syn/2.0.66/syn/struct.Ident.html):

> An identifier constructed with `Ident::new` is permitted to be a Rust
keyword, though parsing one through its
[`Parse`](https://docs.rs/syn/2.0.66/syn/parse/trait.Parse.html)
implementation rejects Rust keywords. Use `input.call(Ident::parse_any)`
when parsing to match the behaviour of `Ident::new`.

Fixes issue #4225
2024-06-03 19:45:36 +00:00
Icxolu 7e5884c40b
fix incorrect `__richcmp__` for `eq_int` only simple enums (#4224)
* fix incorrect `__richcmp__` for `eq_int` only simple enums

* add tests for deprecated simple enum eq behavior

* only emit deprecation warning if neither `eq` nor `eq_int` were given

* require `eq` for `eq_int`
2024-06-03 18:49:36 +00:00
Icxolu 36cdeb29c1
fix incorrect raw identifier handling for the `name` attribute of `pyfunction`s (#4229) 2024-06-03 17:32:35 +00:00
liammcinroy b4b780b475
Allow module= attribute in complex enum variants (#4228)
* Allow module= attribute in complex enum variants

* stderr test update

* towncrier

* inherit `module`, rather than specifying everywhere.

* clippy fix
2024-06-03 07:57:04 +00:00
David Hewitt 88b6f23e3b
fix calling POOL.update_counts() when no `gil-refs` feature (#4200)
* fix calling POOL.update_counts() when no `gil-refs` feature

* fixup conditional compilation

* always increment gil count

* correct test

* clippy fix

* fix clippy

* Deduplicate construction of GILGuard::Assumed.

* Remove unsafe-block-with-unsafe-function triggering errors in our MSRV builds.

---------

Co-authored-by: Adam Reichold <adam.reichold@t-online.de>
2024-06-02 11:11:14 +00:00
JRRudy1 5d47c4ae4c
Added `ToPyObject` and `IntoPy<PyObject>` impls for `PyBackedStr`. (#4205)
* Added `ToPyObject` and `Into<PyObject>` impls for `PyBackedStr`.

* Create 4205.added.md

* Added attributes limiting the `ToPyObject` and `IntoPy<PyObject>` impls to the case where `cfg(any(Py_3_10, not(Py_LIMITED_API)))`. When this cfg does not apply, the conversion is less trivial since the `storage` is actually `PyBytes`, not `PyString`.

* Fixed imports format.

* Updated the `ToPyObject` and `IntoPy<PyObject>` impls to support the `cfg(not(any(Py_3_10, not(Py_LIMITED_API))))` case by converting the `PyBytes` back to `PyString`.

* Added `ToPyObject` and `IntoPy<PyObject>` impls for `PyBackedBytes`.

* Added tests for the `PyBackedBytes` conversion impls.

* Updated newsfragment entry to include the `PyBackedBytes` impls.

* Changed the `IntoPy` and `ToPyObject` impls for `PyBackedBytes` to produce `PyBytes` regardless of the backing variant. Updated tests to demonstrate this.

* retrigger checks

* Updated `PyBackedStr` conversion tests to extract the result as a `PyBackedStr` instead of `&str` since the latter is not supported under some `cfg`'s.

* Fixed `IntoPy<PyObject> for PyBackedBytes` impl to create `bytes` for both storage types as intended. Updated test to properly catch the error.

---------

Co-authored-by: jrudolph <jrudolph@anl.gov>
2024-06-01 21:09:14 +00:00
Icxolu a7a5c10b8a
add pyclass `hash` option (#4206)
* add pyclass `hash` option

* add newsfragment

* require `frozen` option for `hash`

* simplify `hash` without `frozen` error message

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* require `eq` for `hash`

* prevent manual `__hash__` with `#pyo3(hash)`

* combine error messages

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-06-01 14:20:20 +00:00
Jasper van Brakel 25c1db4767
Add weakref Python types (#3835)
* Add vscode folder to gitignore

* Initial work on PyWeakRef (weakref.ReferenceType)

* Add documentation for PyWeakRef::upgrade

* Add missing docs for PyWeakRef

* Add PyWeakProxy

* Add PyWeakCallableProxy

* Add PyWeakRefMethods::upgrade_exact and prevent unnecessary panicing

* Change PyWeakRefMethods to exclude infeasible errors.

As a result of the checks  made about the objectpointers in PyO3, all
 errors in PyWeakref_GetObject are already caught. Therefor there is no
 need to check for these errors in the non-ffi layer.

* Add towncrier changes

* Update weakref type doctests to use `py.run_bound`

* Fix to adhere to MSRV

* Make weakref tests independent from macros feature

* Change Weakref tests

* Remove forgotten Debug marcos

* Processed .gitignore and PyResultExt feedback

* Change new methods of weakref types to remove dangling pointers

* Change to reflect deprecation of PyErr::value for PyErr::value_bound

* Change Tests so different class name in older python versions is accounted for

* Change formatting

* Make tests ABI3 compatible

* Prevent the use of PyClass in test for weakref under abi3 Python 3.7 and 3.8

* Disable weakref types when targeting PyPy

* Remove needless borrow from CallableProxy test

* Add Borrowed variants of upgrade and upgrade exact to trait

* Added tests for weakref borrow_upgrade methods

* Change PyWeakRefMethods method names to be more consistent

* Change weakref constructors to take PyAny for main target

* Add track_caller to all panicing weakref methods

* Add PyWeakRefMethods::upgrade*_as_unchecked

* Fix PyWeakProxy and PyWeakCallableProxy Documentation

* Replace deprecated wrap_pyfunction with bound equivalent

* Add (Generic) PyWeakref Type

* Reworked Proxy types into one (PyWeakrefProxy)

* Rename PyWeakRef to PyWeakrefReference

* Change PyWeakrefReference to only use type pointer when it exists

* Remove `#[track_caller]` annotations for now

* Make the gil-refs function feature dependent

* Remove unused AsPyPointer import

* Change docs links to PyNone to not include private module

* Fix string based examples

* Change tests to work for Python 3.13

* Fix cargo clippy for Python 3.13

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-05-31 19:13:50 +00:00
Icxolu d1a7cf400a
add pyclass `eq` option (#4210)
* add pyclass `eq` option

* prevent manual impl of `__richcmp__` or `__eq__` with `#[pyclass(eq)]`

* add simple enum `eq_int` option

* rearrange names to fix deprecation warning

* add newsfragment and migration

* update docs

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-05-31 14:13:30 +00:00
David Brochart cb347370ff
Fix typo (#4222) 2024-05-31 09:44:09 +00:00
David Hewitt 4fe5e8c689
ci: turn off gh-pages benchmarks (#4209)
* ci: turn off gh-pages benchmarks

* update benchmark badge
2024-05-28 08:19:50 +00:00
JRRudy1 934c663612
Added `From<Bound<'py, T>>` impl for `PyClassInitializer<T>`. (#4214)
* Added `From<Bound<'py, T>>` impl for PyClassInitializer<T>.

* Added newsfragment entry.

* Added tests for pyclass constructors returning `Py<Self>` and `Bound<Self>`.

* Fixed tests.

* Updated tests to properly cover the new impl.

---------

Co-authored-by: jrudolph <jrudolph@anl.gov>
2024-05-28 01:49:52 +00:00
David Hewitt 388d1760b5
ci: start testing on 3.13-dev (#4184)
* ci: start testing on 3.13-dev

* ffi fixes for 3.13 beta 1

* support 3.13

* move gevent to be binary-only

* adjust for div_ceil

* fixup pytests
2024-05-25 22:41:26 +00:00
Cheuk Ting Ho d21045cbc1
adding new getter for type obj (#4197)
* adding new getter for type obj

* fixing limited api build

* fix formating ssues from clippy

* add changelog info

* Update newsfragments/4197.added.md

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Update src/types/typeobject.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Update src/types/typeobject.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Update src/types/typeobject.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Update src/types/typeobject.rs

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* using uncheck downcast

* fix formating

* move import

* Update src/types/typeobject.rs

Co-authored-by: Matt Hooks <me@matthooks.com>

* Update src/types/typeobject.rs

Co-authored-by: Matt Hooks <me@matthooks.com>

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
Co-authored-by: Matt Hooks <me@matthooks.com>
2024-05-25 22:39:48 +00:00
David Hewitt 2c654b2906
ci: adjust test to avoid type inference (#4199) 2024-05-21 19:27:20 +00:00
Cheuk Ting Ho 81ba9a8cd5
Include import hook in getting-started.md (#4198) 2024-05-21 18:24:06 +00:00
David Hewitt 3e4b3c5c52
docs: attempt to clarify magic methods supported by PyO3 (#4190)
* docs: attempt to clarify magic methods supported by PyO3

* Update guide/src/class/protocols.md

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
2024-05-19 20:13:11 +00:00
Adam Reichold 674708cb4c
Remove OWNED_OBJECTS thread local when GILPool is disabled. (#4193) 2024-05-19 13:40:55 +00:00
David Hewitt ac273a1612
docs: minor updates to pyenv installs (#4189) 2024-05-19 13:39:29 +00:00
Alex Gaynor fe79f54817
feature gate deprecated APIs for `GILPool` (#4181) 2024-05-17 11:31:52 +00:00
Bruno Kolenbrander fff053bde7
Emit a better error for abi3 inheritance (#4185)
* Emit a better error for abi3 inheritance

* Update tests/test_compile_error.rs

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-05-17 10:55:41 +00:00
Alex Gaynor 1c64a03ea0
Move GIL counting from GILPool to GILGuard (#4188) 2024-05-17 04:25:41 +00:00
newcomertv 88f2f6f4d5
feat: support pyclass on tuple enums (#4072)
* feat: support pyclass on tuple enums

* cargo fmt

* changelog

* ruff format

* rebase with adaptation for FnArg refactor

* fix class.md from pr comments

* add enum tuple variant getitem implementation

* fmt

* progress toward getitem and len impl on derive pyclass for complex enum tuple

* working getitem and len slots for complex tuple enum pyclass derivation

* refactor code generation

* address PR concerns
- take py from function argument on get_item
- make more general slot def implementation
- remove unnecessary function arguments
- add testcases for uncovered cases including future feature match_args

* add tracking issue

* fmt

* ruff

* remove me

* support match_args for tuple enum

* integrate FnArg now takes Cow

* fix empty and single element tuples

* use impl_py_slot_def for cimplex tuple enum slots

* reverse erroneous doc change

* Address latest comments

* formatting suggestion

* fix :
- clippy beta
- better compile error (+related doc and test)

---------

Co-authored-by: Chris Arderne <chris@translucent.app>
2024-05-17 02:59:00 +00:00
Alex Gaynor 8de1787580
Change `GILGuard` to be able to represent a GIL that was already held (#4187)
See #4181
2024-05-16 21:55:05 +00:00
David Hewitt 7790dab480
emit rustc-check-cfg only on rust 1.80+ (#4168) 2024-05-15 11:11:49 +00:00
Icxolu 10152a7078
feature gate `PyCell` (#4177)
* feature gate `PyCell`

* feature gate `HasPyGilRef` completely

* bump version
2024-05-12 18:30:08 +00:00
Alex Gaynor 57500d9b09
Updates comments regarding the reference pool that were inaccurate (#4176) 2024-05-11 16:48:38 +00:00
Adam Reichold c5f9001985
Remove deferred reference count increments and make the global reference pool optional (#4095)
* Add feature controlling the global reference pool to enable avoiding its overhead.

* Document reference-pool feature in the performance guide.

* Invert semantics of feature to disable reference pool so the new behaviour becomes opt-in

* Remove delayed reference count increments as we cannot prevent reference count errors as long as these are available

* Adjust tests to be compatible with disable-reference-pool feature

* Adjust tests to be compatible with py-clone feature

* Adjust the GIL benchmark to the updated reference pool semantics.

* Further extend and clarify the documentation of the py-clone and disable-reference-pool features

* Replace disable-reference-pool feature by pyo3_disable_reference_pool conditional compilation flag

Such a flag is harder to use and thereby also harder to abuse. This seems
appropriate as this is purely a performance-oriented change which show only be
enabled by leaf crates and brings with it additional highly implicit sources of
process aborts.

* Add pyo3_leak_on_drop_without_reference_pool to turn aborts into leaks when the global reference pool is disabled and the GIL is not held
2024-05-11 14:48:45 +00:00
Icxolu 033caa8fd1
split more impl blocks (#4175) 2024-05-11 13:48:17 +00:00
Icxolu 444be3bafa
feature gate deprecated APIs for `Python` (#4173) 2024-05-10 18:28:30 +00:00
Icxolu 1e8e09dce3
feature gate `as/into_gil_ref` APIs (Part 3) (#4172) 2024-05-10 17:03:57 +00:00
Icxolu aef0a05719
deprecate implicit default for trailing optional arguments (#4078)
* deprecate "trailing optional arguments" implicit default behaviour

* add newsfragment

* generate individual deprecation messages per function

* add migration guide entry
2024-05-10 10:34:58 +00:00
Alex Gaynor 104328ce14
feature gate deprecated more APIs for `Py` (#4169) 2024-05-10 05:54:08 +00:00
David Hewitt f3c7b90def
remove function pointer wrappers no longer needed for MSRV (#4167) 2024-05-09 22:22:17 +00:00
Icxolu 21c02484d0
feature gate APIs using `into_gil_ref` (Part 2) (#4166) 2024-05-09 22:21:48 +00:00
Icxolu 7beb64a8ca
allow constructor customization of complex enum variants (#4158)
* allow `#[pyo3(signature = ...)]` on complex enum variants to specify constructor signature

* rename keyword to `constructor`

* review feedback

* add docs in guide

* add newsfragment
2024-05-09 21:08:23 +00:00
David Matos 2d19b7e2a7
Add `num-rational` support for Python's `fractions.Fraction` type (#4148)
* Add `num-rational` support for Python's `fractions.Fraction` type

* Add newsfragment

* Use Bound instead

* Handle objs which atts are incorrect

* Add extra test

* Add tests for wasm32 arch

* add type for wasm32 clipppy
2024-05-09 15:37:53 +00:00
Icxolu 635cb8075c
feature gate APIs using `into_gil_ref` (Part 1) (#4160) 2024-05-09 07:58:44 +00:00
Icxolu d803f7f8df
store the `FnArg` ident as a `Cow` instead of a reference (#4157)
This allow also storing idents that were generated
as part of the macro instead of only ones the were
present in the source code. This is needed for
example in complex enum tuple variants.
2024-05-08 08:04:42 +00:00
Icxolu 72be1cddba
emit `cargo:rustc-check-cfg=CHECK_CFG` for `pyo3`s config names (#4163) 2024-05-08 05:46:00 +00:00
Icxolu 7263fa92ef
feature gate deprecated APIs for `PyBool` (#4159) 2024-05-04 17:45:27 +00:00
deedy5 ef13bc66e9
Add `pyreqwest_impersonate` to example projects (#4123) 2024-05-04 07:48:15 +00:00
Icxolu e835ff0ec3
handle `#[pyo3(from_py_with = ...)]` on dunder (`__magic__`) methods (#4117)
* handle `#[pyo3(from_py_with = ...)]` on dunder (__magic__) methods

* add newsfragment
2024-05-04 07:39:40 +00:00
Heran Lin c10c7429d8
docs: Remove out-dated information for pyenv (#4138) 2024-05-04 07:32:27 +00:00
Alex Gaynor d1a0c7278f
feature gate deprecated APIs for `PyCFunction` (#4154) 2024-05-03 19:50:38 +00:00
Icxolu c08f6c77a6
feature gate deprecated APIs for `marshal` (#4149) 2024-05-03 18:15:25 +00:00
Alex Gaynor f3ab62cb7e
feature gate deprecated APIs for `PyModule` (#4151) 2024-05-03 17:10:49 +00:00
Alex Gaynor 93cfb51ebb
feature gate deprecated APIs for `PyMemoryView` (#4152) 2024-05-03 16:02:19 +00:00
Icxolu 7cbb85476c
fix `check-guide` ci workflow (#4146) 2024-05-03 10:17:14 +00:00
Icxolu cd3f3ed67c
ci: updates for Rust 1.78 (#4150)
* ci: updates for Rust 1.78

* ci: fix clippy

* restrict `invalid_pymethods_duplicates` to unlimited api with `full`
2024-05-03 07:42:30 +00:00
Icxolu 9a808c35c6
fix `clippy-beta` ci workflow (#4147) 2024-05-01 19:05:51 +00:00
Icxolu a454f6e9cc
feature gate deprecated APIs for `PyFloat` and `PyComplex` (#4145) 2024-05-01 17:13:49 +00:00
Alex Gaynor 5534a7bee8
feature gate deprecated APIs for `PyBuffer` (#4144) 2024-05-01 12:18:12 +00:00
Icxolu dc9a41521a
feature gate deprecated APIs for `Py` (#4142) 2024-05-01 10:57:03 +00:00
Alex Gaynor 261d27d197
feature gate deprecated APIs for `PySlice` (#4141) 2024-04-30 23:55:43 +00:00
Icxolu 2f3a33fda1
feature gate deprecated APIs for `PyList` (#4127) 2024-04-30 22:00:31 +00:00
Icxolu 82c00a2fe4
port `PyAny` tests to `Bound` API (#4140) 2024-04-30 21:49:00 +00:00
Icxolu 4616838ee1
port `PySequence` tests to `Bound` API (#4139) 2024-04-30 18:53:40 +00:00
Icxolu 22c5cff039
feature gate deprecated APIs for `PyErr` and exceptions (#4136) 2024-04-30 16:58:53 +00:00
Icxolu d5452bcd8d
feature gate deprecated APIs for `PyType`, `PyTypeInfo` and `PySuper` (#4134) 2024-04-28 21:03:51 +00:00
Alex Gaynor 9e1960ea34
Update MSRV to 1.63 (#4129)
* Bump MSRV to 1.63

* Drop parking_lot in favor of std::sync

* Make portable-atomic dep conditional

* Remove no longer required cfg
2024-04-28 16:11:28 +00:00
Icxolu 6fb972b232
feature gate deprecated APIs for `PyEllipsis`, `PyNone` and `PyNotImplemented` (#4132) 2024-04-27 15:34:13 +00:00
Icxolu 059c8b3862
feature gate deprecated APIs for `PyBytes` and `PyPyByteArray` (#4131) 2024-04-27 11:34:27 +00:00
Alex Gaynor 8ff5e5b0ab
Fix lychee for guide (#4130)
* Fix lychee for guide

* Update nightly in netlify
2024-04-27 05:12:11 +00:00
David Matos c66ed292ec
Disable PyUnicode_DATA on PyPy (#4116)
* Disable PYUNICODE_DATA on PyPy

* Add newsfragment

* Adjust import on PyString
2024-04-26 06:00:13 +00:00
Icxolu 8734b76f60
feature gate deprecated APIs for `PyIterator` (#4119) 2024-04-25 17:44:42 +00:00
Alexander Clausen 3cb286e0d2
docs: fix typo in trait-bounds.md (#4124) 2024-04-25 15:36:29 +00:00
Icxolu 6c2e6f8bcc
feature gate deprecated APIs for `PyFrozenSet` (#4118) 2024-04-24 21:13:27 +00:00
Icxolu c951bf86de
feature gate deprecated APIs for `PyCapsule` (#4112) 2024-04-24 17:34:19 +00:00
Icxolu 013a4476ea
feature gate deprecated APIs for `datetime` types (#4111) 2024-04-24 17:33:53 +00:00
Bruno Kolenbrander f5fee94afc
Scope macro imports more tightly (#4088) 2024-04-23 18:01:41 +00:00
Icxolu 5d2f5b5702
feature gate deprecated APIs for `PyDict` (#4108) 2024-04-23 05:48:27 +00:00
Georg Brandl c2ac9a98e2
fix vectorcall argument handling (#4104)
Fixes #4093

- Make PY_VECTORCALL_ARGUMENTS_OFFSET a size_t like on CPython
- Remove the assert in PyVectorcall_NARGS and use checked cast
2024-04-22 08:56:39 +00:00
Icxolu da2d1aa8b4
feature gate deprecated APIs for `PyString` (#4101) 2024-04-22 07:20:32 +00:00
Icxolu b0ad1e10aa
feature gate deprecated APIs for `PyTuple` (#4107) 2024-04-22 07:19:01 +00:00
David Hewitt 947b372dcc
change `PyAnyMethods::dir` to be fallible (#4100) 2024-04-19 19:35:52 +00:00
David Hewitt cd28e1408e
add `#[track_caller]` to all `Py`/`Bound`/`Borrowed` methods which panic (#4098) 2024-04-19 11:44:36 +00:00
Icxolu d42c00d21d
feature gate deprecated APIs for `PySet` (#4096) 2024-04-19 07:24:26 +00:00
dependabot[bot] b11174e96d
Update heck requirement from 0.4 to 0.5 (#3966)
* Update heck requirement from 0.4 to 0.5

---
updated-dependencies:
- dependency-name: heck
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* newsfragment

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-04-18 10:22:34 +00:00
David Hewitt 2c205d4586
release notes for 0.21.2 (#4091) 2024-04-18 08:59:02 +00:00
dependabot[bot] e64eb72903
build(deps): update chrono-tz requirement from >= 0.6, < 0.9 to >= 0.6, < 0.10 (#4061)
* build(deps): update chrono-tz requirement from >= 0.6, < 0.9 to >= 0.6, < 0.10

Updates the requirements on [chrono-tz](https://github.com/chronotope/chrono-tz) to permit the latest version.
- [Release notes](https://github.com/chronotope/chrono-tz/releases)
- [Changelog](https://github.com/chronotope/chrono-tz/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono-tz/compare/v0.6.0...v0.9.0)

---
updated-dependencies:
- dependency-name: chrono-tz
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* newsfragment

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-04-18 08:58:51 +00:00
Jacob Zhong 03c50a1839
Change the types of `PySliceIndices` and `PySlice::indices (#3761)
* Change the type of `PySliceIndices::slicelength` and `PySlice::indices()`

* Fix example

* Fix fmt
2024-04-18 07:33:07 +00:00
Bruno Kolenbrander 9761abf3a5
Specify higher target-lexicon version (#4087) 2024-04-17 01:07:11 +00:00
Icxolu 03f59eaf45
fix declarative module compile error with `create_exception!` (#4086)
* fix declarative module compile error with `create_exception!`

* add newsfragment
2024-04-16 20:12:18 +00:00
David Matos 2ad2a3f208
docs: Make contributing.md slightly more clear for newer contributors (#4080)
* docs: Make contributing.md slightly more clear for newer contributors

* Remove accidental backticks

Rearrange overview of commands

* Placed on wrong line

* Add extra overview command
2024-04-16 08:17:41 +00:00
David Hewitt a5201c04af
Deprecate the `PySet::empty` gil-ref constructor (#4082)
* Deprecate the `PySet::empty` gil-ref constructor

* add newsfragment
2024-04-14 21:38:40 +00:00
David Matos 8ed5c17b93
Allow use of scientific notation on rust decimal (#4079)
* Allow use of scientific notation on rust decimal

* Add newsfragment

* Pull out var

* Fix clippy warning

* Modify let bindings location
2024-04-14 20:07:17 +00:00
Icxolu cc7e16f4d6
Refactoring of `FnArg` (#4033)
* refactor `FnArg`

* add UI tests

* use enum variant types

* add comment

* remove dead code

* remove last FIXME

* review feedback davidhewitt
2024-04-14 14:19:57 +00:00
Adam Reichold 721100a9e9
Suppress non_local_definitions lint as we often want the non-local effects in macro code (#4074)
* Resolve references to legacy numerical constants and use the associated constants instead

* Suppress non_local_definitions lint as we often want the non-local effects in macro code
2024-04-13 12:59:44 +00:00
Adam Reichold 4e5167db42
Extend guide on interaction between method receivers and lifetime elision. (#4069) 2024-04-13 07:57:13 +00:00
messense 30348b4d3f
Link libpython for AIX target (#4073) 2024-04-13 07:43:06 +00:00
Icxolu ee5216f406
fix declarative-modules compile error (#4054)
* fix declarative-modules compile error

* add newsfragment
2024-04-12 06:34:27 +00:00
Icxolu c8b59d7117
add `#[doc(hidden)]` to the Rust module created by `#[pymodule]` (#4067)
* add `#[doc(hidden)]` to the Rust module created by `#[pymodule]`

* add newsfragment
2024-04-12 06:34:08 +00:00
Liam 9a6b1962d3
Minor: Fix a typo in Contributing.md (#4066) 2024-04-11 21:11:51 +00:00
dependabot[bot] 631c25f2f9
build(deps): bump peaceiris/actions-gh-pages from 3 to 4 (#4062)
Bumps [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages) from 3 to 4.
- [Release notes](https://github.com/peaceiris/actions-gh-pages/releases)
- [Changelog](https://github.com/peaceiris/actions-gh-pages/blob/main/CHANGELOG.md)
- [Commits](https://github.com/peaceiris/actions-gh-pages/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peaceiris/actions-gh-pages
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 21:08:44 +00:00
Jeong, Heon 47f9ef4174
Fix typo (#4052)
Fix incorrect closing brackets
2024-04-11 21:07:56 +00:00
Joseph Perez 2f0869a6d6
fix: allow impl of Ungil for deprecated PyCell in nightly (#4053) 2024-04-06 22:36:52 +00:00
Icxolu 7a00b4d357
add descriptive error msg for `__traverse__` receivers other than `&self` (#4045)
* add descriptive error msg for `__traverse__` receivers other than `self`

* add newsfragment

* improve error message
2024-04-04 19:08:51 +00:00
David Hewitt a4aea230ed
fix compile error for multiple async method arguments (#4035) 2024-04-02 17:43:51 +00:00
David Hewitt c1f11fb4bd
release: 0.21.1 (#4032) 2024-04-01 18:51:58 +00:00
David Hewitt 8cabd2619c
docs: updates to guide for PyO3 0.21 feedback (#4031)
* docs: add notes on smart pointer conversions

* guide: add more notes on `.extract::<&str>()` to migration guide
2024-04-01 15:18:57 +00:00
Icxolu 8f87b8636d
refactor `#[setter]` argument extraction (#4002) 2024-04-01 12:10:18 +00:00
Icxolu 63ba371db0
added various std traits for `PyBackedStr` and `PyBackedBytes` (#4020)
* added various std traits for `PyBackedStr` and `PyBackedBytes`

* add newsfragment

* add tests
2024-04-01 07:10:40 +00:00
David Hewitt 336b1c982b
add `import_exception_bound!` macro (#4027)
* add `import_exception_bound!` macro

* newsfragment and tidy up
2024-03-31 19:55:31 +00:00
Weijie Guo 3af9a1f4e0
docs: fix example in types.md (#4028) 2024-03-31 08:03:38 +00:00
David Hewitt cff4aa3cc8
ci: defer test-debug to the merge queue (#4023) 2024-03-30 23:26:34 +00:00
David Hewitt 9d932c1061
add `#[inline]` hints on many `Bound` and `Borrowed` methods (#4024) 2024-03-30 22:10:21 +00:00
David Hewitt 22e8dd10e1
add benchmark for class / method calls (#4016) 2024-03-30 20:29:39 +00:00
Weijie Guo 74d9d23ba0
async method should allow args not only receiver (#4015)
* async method should allow args not only receiver

* add changelog md
2024-03-30 08:48:19 +00:00
Rikus Honey 4d033c4497
Fix broken hyperlink to types.md (#4018) 2024-03-30 07:39:00 +00:00
geo7 0e093a5911
Update getting-started.md (#4004)
Missing word
2024-03-29 22:45:47 +00:00
Alex Gaynor de03ca270a
Remove dead code in macros backend (#4011) 2024-03-29 21:28:08 +00:00
David Hewitt c6f25e42a2
ci: relax check in pyobject_drop test (#4014) 2024-03-29 15:51:38 +00:00
Alex Gaynor ae3ac7d872
Fixed error string when failing to import an exception (#4012) 2024-03-29 15:49:50 +00:00
Alex Gaynor 60e3f44dcf
Ensure all arguments to _run are strings (#4013)
Otherwise you get errors like https://github.com/PyO3/pyo3/actions/runs/8480723060/job/23236947935?pr=4012
2024-03-29 14:16:45 +00:00
David Hewitt cf74624de9
refactor to remove add_to_module functions from generated code (#4009)
* refactor to remove add_to_module functions from generated code

* mrsv, newsfragment

* clippy
2024-03-29 11:54:33 +00:00
David Hewitt b053e83c08
implement `Send` and `Sync` for `PyBackedStr` and `PyBackedBytes` (#4007) 2024-03-29 11:28:42 +00:00
Icxolu dd1710256d
use `extract_argument` for `#[setter]` extraction (#3998)
* use `extract_argument` for `#[setter]` extraction

* add newsfragment
2024-03-27 15:41:04 +00:00
David Hewitt bcfc848703
docs: fix link in CHANGELOG (#4001) 2024-03-27 11:48:56 +00:00
tison 7c03d65cda
chore: add Python scripting in database example (#3999) 2024-03-27 11:16:22 +00:00
Icxolu 35faeff6f1
handle `#[pyo3(from_py_with = "")]` in `#[setter]` methods (#3995)
* handle `#[pyo3(from_py_with = "")]` in `#[setter]` methods

* add newsfragment
2024-03-26 18:53:11 +00:00
David Hewitt 1be2fad9bf
release: 0.21.0 (#3983) 2024-03-25 23:36:08 +00:00
Tim Felgentreff 9a38e709bb
Basic GraalPy Support (#3247)
* graalpy: recognize graalpy implementation when building

* graalpy: global Ellipse, None, NotImplemented, True, and False are only available as pointers

* graalpy: PyObject struct is opaque, use functions for everything

* graalpy: missing many of the same functions as pypy

* graalpy: do not have 128bit conversion functions

* graalpy: add functions for datetime accessor macros

* graalpy: add implementations for list macro functions

* graalpy: skip tuple macros

* graalpy: always use extern Py_CompileString function

* graalpy: disable assertion that does not apply to graalpy

* graalpy: floatobject structure is opaque on graalpy

* graalpy: ignore gc dependent test

* graalpy: add CI config

* graalpy: run rust fmt

* graalpy: add changelog entry

* graalpy: discover interpreter on PATH

* graalpy: interpreter id is not applicable to graalpy (just like pypy)

* graalpy: skip tests that cannot work on GraalPy

* graalpy: fix constructing normalized Err instances

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* graalpy: correct capi library name, but skip rust tests due to missing symbols

* graalpy: no support for C extensions on windows in latest release

* graalpy: declare support versions

* graalpy: frame, code, method, and function objects access from C API is mostly missing

* graalpy: take care only to expose C structure that GraalPy allocates

* graalpy: Bail out if graalpy version is less than what we support

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-03-25 18:54:52 +00:00
Icxolu 54ffaecd65
add `AsRef` impls for `PyBackedStr` and `PyBackedBytes` (#3991)
* add `AsRef` impls for `PyBackedStr` and `PyBackedBytes`

* add newsfragment
2024-03-25 16:21:27 +00:00
David Hewitt 7d319a906e
ci: tidy up benchmarks a little (#3986) 2024-03-23 20:17:33 +00:00
David Hewitt aeb74c7093
ci: use macos-14 runners (#3985)
* ci: use macos-14 runners

* use arm64 python architecture
2024-03-23 20:16:37 +00:00
Lily Foote 009cd32a44
Add into_mapping and into_sequence methods to Bound types (#3982)
* Add Bound<'py, PyDict>.into_mapping method

* Add Bound<'py, PyTuple>.into_sequence method

* Add Bound<'py, PyList>.into_sequence method

* Add newsfragment

* Add tests for into_mapping and into_sequence
2024-03-23 01:13:24 +00:00
David Hewitt 9808f7111c
ci: add `update-ui-tests` nox session (#3979)
* ci: add `update-ui-tests` nox session

* defer error messages after the group closes

* fix syntax warnings
2024-03-22 22:43:08 +00:00
David Hewitt 20e477a7cd
avoid reference count cycle in tuple extraction (#3976) 2024-03-22 22:43:05 +00:00
David Hewitt 990886efda
docs: better document `FromPyObject` for `&str` changes in migration guide (#3974)
* docs: better document `FromPyObject` for `&str` changes in migration guide

* review: LilyFoote
2024-03-22 22:16:28 +00:00
David Hewitt d0f5b6af46
ci: updates for Rust 1.77 (#3978)
* ci: updates for Rust 1.77

* move `SendablePtr` inside of test which uses it
2024-03-22 20:43:23 +00:00
David Hewitt 351c6a0a49
deprecate optional GIL Ref in function argument (#3975) 2024-03-21 07:24:40 +00:00
David Hewitt 870a4bb20d
deprecate GIL refs in function argument (#3847)
* deprecate GIL Refs in function arguments

* fix deprecated gil refs in function arguments

* add notes on deprecations limitations to migration guide

* Apply suggestions from code review

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* review: Icxolu

* fix proto method extract failure for option

* fix gil refs in examples

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
2024-03-20 22:35:08 +00:00
David Hewitt cedac43dbb
add Bound::as_unbound (#3973)
* add Bound::as_unbound

* Update src/instance.rs
2024-03-20 12:52:09 +00:00
Icxolu 2736cf670c
deprecate gil-refs in `from_py_with` (Part 2) (#3972)
* deprecate `from_py_with` in `#[derive(FromPyObject)]` (NewType)

* deprecate `from_py_with` in `#[derive(FromPyObject)]` (Enum, Struct)
2024-03-20 09:27:38 +00:00
David Hewitt caf80eca66
handle clippy `new_without_default` warnings (#3971)
* handle clippy `new_without_default` warnings

* add newsfragment
2024-03-19 21:41:27 +00:00
David Hewitt 02e188e4b4
adjust path for GIL Refs deprecation warnings (#3968) 2024-03-19 21:08:20 +00:00
David Hewitt cbed7c11b6
ci: tidy ups for 3.7 (#3969) 2024-03-19 19:36:22 +00:00
David Hewitt e29fac9c46
docs: use details to condense migration guide (#3961) 2024-03-19 08:59:05 +00:00
Icxolu b06e95727b
deprecate gil-refs in `from_py_with` (#3967)
* deprecate gil-refs in `from_py_with`

* review feedback davidhewitt
2024-03-19 08:58:41 +00:00
David Hewitt ebeea943fe
ci: fixes to actions caches (#3970) 2024-03-19 08:14:42 +00:00
Icxolu da24f0cf93
exposes `Borrowed::to_owned` as public API (#3963)
* exposes `Borrowed::to_owned` as public API

* add newsfragment
2024-03-17 09:17:09 +00:00
David Hewitt dcba984b51
deprecate `GILPool` (#3947)
* deprecate `GILPool`

* review: adamreichold

* fix deprecation warnings in tests
2024-03-15 10:25:27 +00:00
Icxolu 5c86dc35c1
allow borrowed extracts with `gil-refs` disabled (#3959) 2024-03-15 07:53:52 +00:00
Bruno Kolenbrander 989d2c53ab
Fix non_local_definitions lint triggers (#3955) 2024-03-12 22:59:33 +00:00
Thomas Tanon 7cde95bba4
Documents experimental-declarative-modules feature (#3953)
* Documents experimental-declarative-modules feature

* More details on experimental-declarative-modules progress
2024-03-12 22:57:31 +00:00
Icxolu ee89b2e8e2
deprecate `wrap_pyfunction` with `py` argument (#3954)
* deprecate `wrap_pyfunction` with `py` argument

The Python token in `wrap_pyfunction` is not handled automatically by
`WrapPyFunctionArg`, for backwards compatibility. This uses deref
specialization to deprecate this variant.

* merge `Extractor`s

* add deprecation ui test, revert closure variant due to test failure

* fix nightly
2024-03-12 22:57:03 +00:00
Georg Brandl a7fa1bdf22
fix: remove "track_caller" cfg check (#3951)
This "cfg" value does not seem to exist (any more?), and #[track_caller]
is used a lot elsewhere without cfg_attr.

Found using cargo check -Zcheck-cfg.
2024-03-11 12:40:26 +00:00
acceptacross 93323bc922
chore: remove repetitive words (#3950)
Signed-off-by: acceptacross <csqcqs@gmail.com>
2024-03-11 10:15:03 +00:00
David Hewitt 67b1b35013
release: 0.21.0-beta.0 (#3944) 2024-03-10 22:16:18 +00:00
David Hewitt db0a98c040
ci: pin pytest < 8.1 (#3946) 2024-03-10 16:17:15 +00:00
David Hewitt 9145fcfe19
docs: major rewrite for Bound API (#3906)
* wip bound docs

* Update guide/src/python_from_rust/calling-existing-code.md

Co-authored-by: Lily Foote <code@lilyf.org>

* continue to move and tidy up

* Apply suggestions from code review

Co-authored-by: Lily Foote <code@lilyf.org>

* update URL

* complete python-from-rust.md

* progress on types.md; probably more to go

* update doctest paths

* review: Icxolu

* finish updating `types.md` to Bound API

* update remainder of the guide to Bound API

* Update guide/src/performance.md

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Update guide/src/types.md

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Update src/lib.rs

* review: Icxolu

* Update guide/src/python-from-rust.md

Co-authored-by: Adam Reichold <adamreichold@users.noreply.github.com>

* Update guide/src/async-await.md

Co-authored-by: Adam Reichold <adamreichold@users.noreply.github.com>

* review: adamreichold

---------

Co-authored-by: Lily Foote <code@lilyf.org>
Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
Co-authored-by: Adam Reichold <adamreichold@users.noreply.github.com>
2024-03-10 15:51:51 +00:00
David Hewitt 75af678f43
docs: use kebab-case instead of snake_case for guide URLs (#3942)
* guide: use kebab-case instead of snake_case

* fixup doctest names

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* review: Icxolu

* fix relative url

* also remap latest pyo3

* fixup python_from_rust

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
2024-03-09 20:10:58 +00:00
Icxolu 908e661237
deprecate gil-refs in "self" position (#3943)
* deprecate gil-refs in "self" position

* feature gate explicit gil-ref tests

* fix MSRV

* adjust bracketing

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-03-09 09:52:12 +00:00
David Hewitt 14d1d2a9ee
docs: use `lychee` to check link URLs (#3941)
* guide: install `mdbook-linkcheck`

* use `shutil` to copy license files

* move from `mdbook-linkcheck` to `lychee`

* clean guide & doc build products before build

* fix more broken links

* review: mejrs
2024-03-08 14:10:47 +00:00
David Hewitt 770d9b7f01
add `FromPyObjectBound` trait for extracting `&str` without GIL Refs (#3928)
* add `FromPyObjectBound` adjustment for `&str` without GIL Refs

* review: alex, Icxolu feedback

* add newsfragment

* add newsfragment for `FromPyObject` trait change

* make some examples compatible with abi3 < 3.10

* seal `FromPyObjectBound`

* fixup chrono_tz conversion
2024-03-08 07:43:48 +00:00
Icxolu 31c4820010
deprecate `&PyModule` as `#[pymodule]` argument type (#3936)
* deprecate `&PyModule` as `#[pymodule]` argument type

* cleanup

* add ui tests

* fix deprecations in tests

* fix maturin and setuptools-rust starters

* run `deprecated` ui test only when `gil-refs` as disabled
2024-03-08 00:28:11 +00:00
Thomas Tanon fbd531195a
PyAddToModule: Properly propagate initialization error (#3919)
Better than panics
2024-03-06 18:20:02 +00:00
Thomas Tanon 0f7ddb19a5
PyPy: remove PyCode (PyCode_Type does not exist) (#3934) 2024-03-06 18:19:43 +00:00
Thomas Tanon d35f41e0bf
Chrono: allow deprecation warning until upgrading to 0.4.35+ (#3935)
* Chrono: allow deprecation warning until upgrading to 0.4.35+

Requiring 0.4.35 is blocked on MSRV (Chrono: 1.61, PyO3: 1.56)

* also allow deprecated chrono in example

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-03-06 17:49:43 +00:00
David Hewitt 57bbc32e7c
add `experimental-async` feature (#3931)
* add `experimental-async` feature

* gate async doctests on feature
2024-03-06 00:54:45 +00:00
Thomas Tanon fe84fed966
Allow inline struct, enum, fn and mod inside of declarative modules (#3902)
* Inline struct, enum, fn and mod inside of declarative modules

* remove news fragment

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-03-06 00:31:56 +00:00
David Hewitt b08ee4b7e1
remove `gil-refs` feature from `full` feature (#3930) 2024-03-04 22:23:35 +00:00
Lily Foote 811a3e5d00
Add IntoIterator for &Bound types (#3923)
* Add IntoIterator for &Bound<'py, PyList>

* Add a test for Bound<'_, PyList>.into_iter

* Implement IntoIterator for more &Bound types

* Remove some explicit .iter() calls

* Implement IntoIterator for &Bound<'py, PyIterator>
2024-03-04 21:24:38 +00:00
Bruno Kolenbrander 4114dcb1a0
Thread pyo3's path through the builder functions (#3907)
* Thread pyo3's path through the builder functions

* preserve span of pyo3_path

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-03-04 07:54:04 +00:00
Icxolu 70a7aa808d
deprecate the use of `PyCell` in favor of `Bound` and `Py` (#3916)
* deprecate the use of `PyCell` in favor of `Bound` and `Py`

* update `FromPyObject` for `T: PyClass + Clone` impl

* move `PyCell` deprecation to the `gil-refs` feature gate and add a migration note
2024-03-03 14:47:25 +00:00
Icxolu 00eb014623
docs: update Python function section of the guide (#3925)
* docs: update Python function section of the guide

* update `pass_module` types

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-03-03 09:15:46 +00:00
David Hewitt 2e56f659ed
split `PyCell` and `PyClassObject` concepts (#3917)
* add test for refguard ref counting

* split `PyCell` and `PyClassObject` concepts

* rework `create_cell` to `create_class_object`

* Apply suggestions from code review

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* review: Icxolu feedback

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
2024-03-03 07:00:59 +00:00
Icxolu 81be11e67a
docs: update Python modules section of the guide (#3924) 2024-03-02 22:55:05 +00:00
Lily Foote d94827720e
Delete duplicate test code (#3926)
These used to explicitly call `.iter()`, but that was removed in b65cbb9
to remove lints.
2024-03-02 22:53:28 +00:00
Icxolu 1c5265e1c2
deprecate `from_borrowed_ptr` methods (#3915)
* deprecate `from_borrowed_ptr` methods

This deprecates the methods on the `Python`
marker, aswell as `FromPyPointer`

* use `BoundRef` to defer ref cnt inc until after the error case
2024-03-01 20:51:53 +00:00
Icxolu 1d224610c3
docs: update `Python classes` section of the guide (#3914)
* docs: update `Python classes` section of the guide

* review feedback davidhewitt

* migration guide entry
2024-03-01 09:21:47 +00:00
Lily Foote 56683ed553
deprecate Py::as_ref (#3864)
* Deprecate Py::as_ref

* Reword as_ref deprecation note

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Tidy up remaining uses of Py::as_ref

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Pass hello into println! explicitly

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-02-29 07:15:34 +00:00
Matthew Neeley 68ec6de0c9
Use single-arg form of `#[pymodule]` function in docs and tests (#3899)
* Use single-arg form for `#[pymodule]` functions in docs and tests

* Update guide/src/function.md

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Add test of two-argument module function

* Fix new test

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
2024-02-28 22:36:50 +00:00
David Hewitt a582fa0163
docs: update discord invite to permanent one (#3913) 2024-02-28 20:51:40 +00:00
Icxolu 55833365b5
seals `PyAnyMethods` and friends (#3909)
* seals `PyAnyMethods` and friends

This seals these new traits, preventing downstream
crates from implementing them on their types.
These traits are mainly a workaround for arbitrary
self receiver types, so this gives us more
flexibility if these need to be changed in the
future.

* move `PyResultExt` seal
2024-02-28 19:36:55 +00:00
David Hewitt 8a12970c96
update `extract_argument` to use Bound APIs (#3708)
* update `extract_argument` to use `Bound` APIs

* tidy up borrow in macros expression

* update `trybuild` output

* more concise form for `DowncastError::new`

Co-authored-by: Lily Foote <code@lilyf.org>

* use `Borrowed` instead of newtype

* use `Borrowed::from_ptr` methods in extract_argument

* update UI tests

* avoid double-negative `#[cfg]` clauses

Co-authored-by: Lily Foote <code@lilyf.org>

* review: LilyFoote, Icxolu feedback

---------

Co-authored-by: Lily Foote <code@lilyf.org>
2024-02-28 19:36:20 +00:00
Matthew Neeley a15e4b1a11
Allow pymodule functions to take a single Bound<'_, PyModule> arg (#3905) 2024-02-27 22:24:14 +00:00
Icxolu 6f03a5464f
cleans up `PyCFunction::internal_new` (#3910)
This deduplicates some code around `PyCFunction::internal_new`
2024-02-27 22:15:35 +00:00
Lily Foote a3ad28b70c
Pymodule bound (#3897)
* Support Bound in pymodule and pyfunction macros

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Remove spurious $ character

Co-authored-by: Matthew Neeley <mneeley@gmail.com>

* Rework PyCFunction::new_bound signatures

This allows us to remove the awkward `PyFunctionArgumentsBound` enum.

* Use BoundRef instead of BoundModule

* support argument deduction for `wrap_pyfunction_bound!`

* support `wrap_pyfunction!` with `Bound` input/output

* Fix docs link to `wrap_pyfunction_bound!`

* Revert back to wrap_pyfunction!

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
Co-authored-by: Matthew Neeley <mneeley@gmail.com>
2024-02-27 19:19:52 +00:00
David Hewitt 93704047a5
store `Bound<T>` inside `PyRef` and `PyRefMut` (#3860)
* store `Bound<T>` inside `PyRef` and `PyRefMut`

* update `FromPyObject` for `PyRef` to use `extract_bound`

* review: Icxolu feedback
2024-02-27 18:56:22 +00:00
Lily Foote 5c41ea0ade
Implement `From<Bound<'py, T>>` for PyErr (#3881)
* Implement `From<Bound<'py, T>>` for PyErr

* Replace PyErr::from_value_bound calls with .into

* Fix From<MyError> expected error message

* Add a trait bound to From<Bound<'py, T>> for PyErr
2024-02-26 23:14:41 +00:00
David Hewitt cd1c0dbf39
ci: move cross compile tests to their own jobs (#3887)
* ci: move cross compile tests to their own jobs

* don't run cross-compile jobs on regular PRs
2024-02-26 21:47:35 +00:00
David Hewitt 8e2219b0d9
silence non-local-definitions nightly lint (#3901)
* silence non-local-definitions nightly lint

* add newsfragment

* add FIXMEs for `non_local_definitions`

* also allow `non_local_definitions` in doctests
2024-02-26 20:28:04 +00:00
David Hewitt 404161c969
ci: apply correct permissions for cache cleanup job (#3898) 2024-02-25 09:49:22 +00:00
Icxolu 7c10ff4327
allow `Bound<'_, T>` in #[pymethods] `self` position (#3896)
* allow `Bound<'_, T>` in #[pymethods] `self` position

* rename `TryFromPyCell` -> `TryFromBoundRef`

* remove unneccessary unsafe
2024-02-25 07:13:36 +00:00
David Hewitt 8f1b99e1e9
move chat discussions to Discord (#3892)
* move chat discussions to Discord

* guide: add some more signposting to the PyO3 Discord
2024-02-24 22:35:01 +00:00
Thomas Tanon e0e3981e17
#[pymodule] mod some_module { ... } v3 (#3815)
* #[pymodule] mod some_module { ... } v3

Based on #2367 and #3294

Allows to export classes, native classes, functions and submodules and provide an init function

See test/test_module.rs for an example

Future work:
- update examples, README and guide
- investigate having #[pyclass] and #[pyfunction] directly in the #[pymodule]

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
Co-authored-by: Georg Brandl <georg@python.org>

* tests: group exported imports

* Consolidate pymodule macro code to avoid duplicates

* Makes pymodule_init take Bound<'_, PyModule>

* Renames #[pyo3] to #[pymodule_export]

* Gates #[pymodule] mod behind the experimental-declarative-modules feature

* Properly fails on functions inside of declarative modules

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
Co-authored-by: Georg Brandl <georg@python.org>
2024-02-24 13:50:18 +00:00
Icxolu c06bb8f1f1
reexport `PyAnyMethods` and friends from `pyo3::types` (#3895)
* reexport `PyAnyMethods` and friends from `pyo3::types`

* remove duplicated imports
2024-02-24 13:46:59 +00:00
David Matos 0f29feca8f
Tidy up deprecation message on bound api (#3893) 2024-02-24 13:25:06 +00:00
Lily Foote e145ae851a
Update new_closure_bound closure signature (#3883)
* Update new_closure_bound closure signature

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Use anonymous lifetimes in closure bounds

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Take &Bound in PyCFunction closures

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-02-23 14:07:54 +00:00
David Hewitt fbf2e91914
macros: exact dependency on `pyo3-build-config` (#3889) 2024-02-23 12:30:46 +00:00
David Hewitt 11d143d0c9
release: 0.20.3 (#3890) 2024-02-23 12:30:38 +00:00
David Hewitt 9186da3892
ci: run fmt unconditionally (#3888) 2024-02-23 08:16:37 +00:00
Alexander Hill 0f92b670b2
docs: Add lcov / codecov options for nox coverage and update docs (#3825)
* docs: Clarify use of llmv-cov plugin

* Mention first-run

* Update Contributing.md

* Add options

* fix
2024-02-23 06:49:44 +00:00
Icxolu 6a815875a0
port `PyErr::from_type` to `Bound` API (#3885) 2024-02-23 06:31:51 +00:00
David Hewitt 5ca810236d
ci: rework GitHub caching strategy (#3886)
* ci: rework GitHub caching strategy

* clean up some more redundant imports flagged by nightly
2024-02-23 00:51:50 +00:00
Lily Foote 22a23ffb31
Tidy some usage of `py.from_borrowed_ptr` and `py.from_borrowed_ptr_or_opt` (#3877)
* Tidy some usage of py.from_borrowed_ptr

* Add BoundRef::ref_from_ptr_or_opt
2024-02-22 23:06:55 +00:00
Icxolu 8bd82da939
add missing deprecation for `PyDict::from_sequence` (#3884) 2024-02-22 22:52:25 +00:00
Icxolu 4f8ee96881
fix `AsRef` and `Deref` impls on `Bound<T>` (#3879)
* fix `AsRef` and `Deref` of `Bound<T>` to `Bound<PyAny>`

* cleanup unnessesary `.as_any()` calls

* remove trait bound on `AsRef` impl

* add comment for `Deref` trait bound

* rename marker trait
2024-02-22 22:38:42 +00:00
David Hewitt 9e74c858c2
add `PyModule::new_bound` and `PyModule::import_bound` (#3775)
* add `PyModule::new` and `PyModule::import_bound`

* review: Icxolu feedback
2024-02-22 09:35:47 +00:00
David Hewitt c4f66657c5
fix `either` feature conditional compilation, again (#3834)
* fix `either` feature conditional compilation, again

* test feature powerset in CI

* install `rust-src` for feature powerset tests

* review: adamreichold feedback

* Fix one more case of redundant imports.

* just check feature powerset for now

---------

Co-authored-by: Adam Reichold <adam.reichold@t-online.de>
2024-02-22 08:05:37 +00:00
Lily Foote 5ddcd46980
Deprecate `py.from_owned_ptr` methods (#3875)
* Deprecate `py.from_owned_ptr` methods

* Refactor PyString.to_str

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-02-22 00:05:08 +00:00
Juniper Tyree 885883bf68
Add `Py::drop_ref` method (#3871)
* add Py::drop_ref method

* add changelog entry

* fix ffi import

* integrate review feedback

* Add a test

* Fix some build errors

* Fix some more build errors
2024-02-21 22:56:03 +00:00
Icxolu 61bc02d927
deprecate `PyCell::new` in favor of `Py::new` or `Bound::new` (#3872)
* deprecate `PyCell::new` in favor of `Py::new` or `Bound::new`

* update deprecation warning

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-02-20 07:45:47 +00:00
Lily Foote a93900686e
Deprecate Py::into_ref (#3867)
* Migrate into_ref calls to Bound api

* Mark Py::into_ref as deprecated
2024-02-20 07:10:45 +00:00
David Hewitt 8ac7834f98
docs: update example for storing Py<T> in structs (#3876) 2024-02-20 07:08:49 +00:00
David Hewitt 7a03a6fe6d
ci: disable some benchmarks volatile on codspeed (#3861) 2024-02-19 22:40:08 +00:00
Lily Foote 76dabd4e60
Replace as_ref(py) with Bound APIs (#3863) 2024-02-19 22:39:54 +00:00
David Hewitt 9a36b50789
update `py.import` -> `py.import_bound` in benches (#3868) 2024-02-19 22:15:36 +00:00
David Hewitt 96b8c9facf
migrate some final `FromPyObject` implementations to the `Bound` API (#3869)
* update `Py::extract` to use `extract_bound`

* update docstring of `FromPyObject`

* move `Option` conversions to new module & update

* move `Cell` conversions to new module & update
2024-02-19 22:14:26 +00:00
David Hewitt ececa86a7d
ci: cancel in-progress benches job on push (#3874) 2024-02-19 22:14:00 +00:00
David Hewitt 4efc4b82a3
ci: fix redundant import warnings on nightly (#3873) 2024-02-19 22:07:05 +00:00
Icxolu 0bb9cab6d3
port `PyComplex::from_complex` to `Bound` API (#3866)
* port `PyComplex::from_complex` to `Bound` API

* add `PyComplexMethods` to the prelude
2024-02-18 23:37:02 +00:00
David Hewitt a85ed34c45
add Bound API constructors from borrowed pointers (#3858)
* make `Borrowed` ptr constructors public

* introduce `Bound::from_borrowed_ptr` constructors

* clippy `assert_eq` -> `assert`

* rerrange function order and correct docstrings
2024-02-18 22:03:43 +00:00
Lily Foote b4dc854585
Convert LazyTypeObject to use the Bound API (#3855) 2024-02-18 22:01:50 +00:00
Icxolu 4ce9c35983
port `Python::get_type` to `Bound` API (#3846)
* port `Python::get_type` to `Bound` API

* fix `is_subclass_and_is_instance` FIXME
2024-02-18 18:27:19 +00:00
David Hewitt f04ad56df4
implement `PyTypeMethods` (#3705)
* implement `PyTypeMethods`

* introduce `PyType` bound constructors

* `from_type_ptr_bound` instead of `from_type_ptr_borrowed`

* correct conditional code

* just make `from_type_ptr_bound` create an owned `Bound`

* correct docstrings

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>

* Rework as `PyType::from_borrowed_type_ptr`

* correct doc link to `from_borrowed_type_ptr`

Co-authored-by: Lily Foote <code@lilyf.org>

* remove unneeded lifetime name

---------

Co-authored-by: Icxolu <10486322+Icxolu@users.noreply.github.com>
Co-authored-by: Lily Foote <code@lilyf.org>
2024-02-18 03:07:48 +00:00
Icxolu 1d295a12a0
port `PyObject::downcast` to `Bound` API (#3856)
* port `PyObject::downcast` to `Bound` API

* relax traits bounds for unchecked variant in `Bound` API

* deprecate `Python::(checked_)cast_as`

* reword deprecation warning
2024-02-18 01:11:43 +00:00
Lily Foote 0dd568d397
Use the new bound API instead of .as_ref(py) (#3853)
* Use the new bound API instead of .as_ref(py)

* Move import into a nested scope

* Use to_cow instead of to_str for compatibility

`to_str` is not available before Python 3.10 on the limited api.

* Relax &self lifetimes

* Use Bound<'py, PyAny> in test Mapping signatures

* Use .as_bytes(py)

* Simplify ThrowCallback::throw signature

* Avoid .as_any call with Py api instead of Bound
2024-02-18 00:09:56 +00:00
Lily Foote 5f42c02e4f
Remove stray " character from docstring (#3852) 2024-02-17 17:04:35 +00:00
Icxolu c33d330b18
deprecate `PyFrozenSet::empty` (#3851) 2024-02-17 13:40:54 +00:00
Icxolu 1d8d81db2d
port `PyFrozenSetBuilder` to `Bound` API (#3850) 2024-02-17 12:21:41 +00:00
David Hewitt eb90b81d44
always use a Python iterator for sets and frozensets (#3849)
* always use a Python iterator for sets and frozensets

* add newsfragment
2024-02-17 10:57:53 +00:00
David Hewitt 65cf5808d9
docs: add note about mapping to dangling pointer with `Bound` API (#3805) 2024-02-17 10:35:28 +00:00
Lily Foote 940804fe0d
Pyerr value bound (#3820)
* Implement PyErr::value_bound

* Use PyErr::value_bound in conversions

* Implement PyErr::from_value_bound

* Remove unnecessary clone

* Return a reference from PyErr::value_bound

* Avoid clone in PyErr::from_value_bound

* Use PyErr::from_value_bound instead of from_value

* Remove unnecessary .as_borrowed() calls

* Remove unused import

* Simplify UnraisableCapture.hook

* Use Bound::from_owned_ptr_or_opt in fn cause

* Update PyErrState::lazy to take Py<PyAny>

This is easier to work with elsewhere than `&PyAny` or
`Bound<'py, PyAny>`.

* Add Bound PyUnicodeDecodeError constructors

* Update PyErr::from_value_bound to take Bound

* Simplify PyErr::from_value

* Simplify PyErr::value

* Remove unnecessary reference

* Simplify Pyerr::cause implementation

* Simplify PyUnicodeDecodeError::new_bound
2024-02-17 00:27:45 +00:00
alm c24478e8bc
docs: fix link in Contributing.md (#3845) 2024-02-16 16:53:25 +00:00
David Hewitt ec6d587218
support `Bound` for `classmethod` and `pass_module` (#3831)
* support `Bound` for `classmethod` and `pass_module`

* `from_ref_to_ptr` -> `ref_from_ptr`

* add detailed docs to `ref_from_ptr`
2024-02-16 00:36:11 +00:00
Icxolu 05aedc9032
port `PyErr::warn` to `Bound` API (#3842)
* port `PyErr::new_type`

* port `PyErr::warn` and `PyErr::warn_explicit`
2024-02-16 00:12:43 +00:00
David Hewitt dc8b948201
add `PyBackedStr` and `PyBackedBytes` (#3802)
* add `PyBackedStr` and `PyBackedBytes`

* review: adamreichold feedback

* use `NonNull<[u8]>`

* clippy and newsfragment

* drop binding unused after refactoring

---------

Co-authored-by: Adam Reichold <adam.reichold@t-online.de>
2024-02-15 07:58:20 +00:00
Icxolu f3ddd023c9
convert `PyBuffer` to `Bound` API (#3836) 2024-02-14 22:10:59 +00:00
Icxolu 9902633116
allow `from_py_with` on function args to take a `fn(&Bound) -> PyResult` (#3837) 2024-02-14 22:03:04 +00:00
Icxolu 0c12d9137f
port `Python::import` to `Bound` API (#3832)
* port `Python::import` to `Bound` API

* tidy up imports in tests/test_datetime_import.rs

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-02-14 00:24:37 +00:00
Lily Foote a1e77c5a66
Document using as_borrowed in the Bound migration (#3833) 2024-02-13 23:30:16 +00:00
David Hewitt f5eafe23f2
add maximum Python version check (#3821)
* add maximum Python version check

* restore dependency of `pyo3-macros-backend` on `pyo3-build-config`

* fix clippy-all noxfile job
2024-02-13 21:52:53 +00:00
David Hewitt e308c8d3ac
ci: don't test gevent on pypy (#3830) 2024-02-13 00:14:55 +00:00
Icxolu fbfeb2ff03
update `#[derive(FromPyObject)]` to use `extract_bound` (#3828)
* update `#[derive(FromPyObject)]` to use `extract_bound`

* type inference for `from_py_with` using function pointers
2024-02-13 00:09:41 +00:00
David Hewitt 94b7d7e434
add `DowncastIntoError::into_inner` (#3829) 2024-02-12 21:40:05 +00:00
David Hewitt 5b9b76fe58
add `_bound` constructors for datetime types (#3778)
* add `_bound` constructors for datetime types

* review: Icxolu feedback

* update uses of deprecated timezone_utc
2024-02-12 20:49:58 +00:00
Lily Foote 1279467d27
Pyerr isinstance (#3826)
* Implement PyErr::is_instance_bound

* Update is_instance_bound to take a reference

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Remove spurious clone

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-02-12 08:32:51 +00:00
Icxolu c359f5ca1d
deprecate `PyDict::new` constructor (#3823)
* deprecate `PyDict::new`

* update benchmarks

* convert `test_frompyobject`
2024-02-11 23:55:56 +00:00
Kushal Das c983dc9773
Adds johnnycanencrypt project link (#3822) 2024-02-11 22:52:56 +00:00
Lily Foote baf5c8ec0a
Implement PyErr::get_type_bound (#3819)
* Implement PyErr::get_type_bound

* Update docs for PyErr::get_type_bound

* Fix doctest for cloning PyErr

* Import the whole prelude in docs example

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Remove unnecessary self lifetime

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Remove more unnecessary self lifetimes

* Use variables to avoid dangling pointers

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Avoid using ffi in fn ptype on Py_3_12

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Add missing imports to fn ptype

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-02-11 21:07:28 +00:00
Jose c56cd3dd65
docs: clarify --pretty option to expand (#3810)
* Update debugging.md

Added clarification. --pretty no longer works, and it breaks even on nightly at least on cargo 1.78.0-nightly (cdf84b69d 2024-02-02) and
rustc 1.78.0-nightly (256b6fb19 2024-02-06).

* Update guide/src/debugging.md

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-02-11 20:52:49 +00:00
Jose f721c8c2b7
docs: fix link to README on building_and_distribution.md (#3809)
* Update building_and_distribution.md

Link to README not working.

* Update guide/src/building_and_distribution.md

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
2024-02-11 20:52:14 +00:00
David Hewitt 55488d3880
Merge pull request #3818 from davidhewitt/datetime-segv
fix segmentation fault when `datetime` module is invalid
2024-02-11 09:04:32 +00:00
David Hewitt 5b1104131f fix segmentation fault when `datetime` module is invalid 2024-02-11 02:44:31 +00:00
David Hewitt 07ea89d460
Merge pull request #3817 from Icxolu/into-pydict
port `IntoPyDict` to `Bound` API
2024-02-11 02:26:03 +00:00
David Hewitt 6ee9c4ec5a
Merge pull request #3812 from Hamatti/docs-clarify-nox-installation
docs: Clarify the requirement to install nox before building documentation
2024-02-11 01:55:58 +00:00
Icxolu e45fbe493c port `IntoPyDict` to `Bound` API 2024-02-10 15:47:26 +01:00
Juha-Matti Santala 559761b2f1 docs: Clarify the requirement to install nox
Installing nox was mentioned in a later section when building
the user guide but not at this point earlier in the guide where
nox was needed for the first time.
2024-02-10 16:20:37 +02:00
David Hewitt fa53d81e5f
Merge pull request #3801 from davidhewitt/encode-utf8
add `PyStringMethods::encode_utf8`
2024-02-10 14:16:32 +00:00
David Hewitt 45f2b0aba5
Merge pull request #3816 from Icxolu/python-run
port `Python::run` to `Bound` API
2024-02-09 21:42:51 +00:00
Icxolu 4d423b0c67 port `Python::run` to `Bound` API 2024-02-09 22:09:16 +01:00
David Hewitt b7fb9e672e
Merge pull request #3782 from davidhewitt/type-check-bound
add `bound` method variants for `PyTypeInfo`
2024-02-09 20:22:44 +00:00
David Hewitt 2fedea24b3
Merge pull request #3806 from Icxolu/python-eval
port `Python::eval` to `Bound` API
2024-02-09 17:18:57 +00:00
Icxolu 33dc33ecec port `Python::eval` to `Bound` API 2024-02-09 17:52:00 +01:00
David Hewitt 367eeaeeab add `bound` method variants for `PyTypeInfo` 2024-02-08 22:27:05 +00:00
David Hewitt 9bb001108b
Merge pull request #3813 from davidhewitt/nightly-2024-02-08
ci: allow some dead code warnings on nightly
2024-02-08 21:32:57 +00:00
David Hewitt bcb7b88c23 ci: updates for rust 1.76 2024-02-08 21:13:58 +00:00
David Hewitt 3541506a16 ci: allow some dead code warnings on nightly 2024-02-08 20:52:03 +00:00
David Hewitt 030a618e0d
Merge pull request #3800 from snuderl/PyCFunction-bound-api
PyCFunction bound api
2024-02-06 18:28:40 +00:00
Blaž Šnuderl aa3c938b5e PyCFunction bound api 2024-02-06 18:44:53 +01:00
David Hewitt 059e485a95
Merge pull request #3560 from Jgfrausing/patch-1
docs: Include section on how to disable signals in python
2024-02-06 09:01:17 +00:00
Jonatan G. Frausing b74d733244 docs: include section that disables signal in python 2024-02-06 08:33:44 +00:00
David Hewitt f7bfa9ab11
Merge pull request #3804 from davidhewitt/chrono-conversions
docs: add `chrono` conversions to types table
2024-02-06 08:12:53 +00:00
David Hewitt dd4df29bad docs: add `chrono` conversions to types table 2024-02-05 21:50:25 +00:00
David Hewitt 911723389a
Merge pull request #3803 from PyO3/dependabot/github_actions/codecov/codecov-action-4
Bump codecov/codecov-action from 3 to 4
2024-02-05 19:50:55 +00:00
David Hewitt 020ed39327
Merge pull request #3779 from davidhewitt/bound-from-ptr
expose `Bound::from_owned_ptr` etc
2024-02-05 19:26:57 +00:00
David Hewitt c85d72bb0e connect CODECOV_TOKEN to codecov action 2024-02-05 18:50:18 +00:00
dependabot[bot] ec0be57c68
Bump codecov/codecov-action from 3 to 4
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 18:43:19 +00:00
David Hewitt 86f294f6e6 expose `Bound::from_owned_ptr` etc 2024-02-05 18:27:43 +00:00
David Hewitt 662eecfb44 add `PyStringMethods::encode_utf8` 2024-02-05 13:28:46 +00:00
David Hewitt ecb4ecbe22
Merge pull request #3789 from btel/patch-1
docs: add example for wrapping generic classes
2024-02-05 12:03:20 +00:00
David Hewitt 02f1df69b5
Merge pull request #3792 from davidhewitt/bound-pyclass-2
pyclass methods for `Bound`
2024-02-05 10:38:30 +00:00
Adam Reichold c995426c81
Merge pull request #3799 from davidhewitt/move-cow
move `Cow[u8]` conversions into `conversions::std::slice` module
2024-02-05 10:18:33 +00:00
Bartosz Telenczuk 64a6a02bf0 add example for wrapping generic classes 2024-02-05 11:00:09 +01:00
David Hewitt 42843de47b pyclass methods for `Bound` 2024-02-05 09:41:22 +00:00
David Hewitt 7281268840 move `Cow[u8]` conversions into `conversions::std::slice` module 2024-02-05 08:52:07 +00:00
Bruno Kolenbrander 7938d4cadc
Merge pull request #3798 from davidhewitt/beta-map-clone
ci: fix beta clippy `map_clone` warning
2024-02-05 08:48:14 +00:00
David Hewitt de93d15eeb ci: fix beta clippy `map_clone` warning 2024-02-05 07:57:27 +00:00
David Hewitt c9c6f928a1
Merge pull request #3797 from snuderl/PyEllipsis-and-NotImplemented-get-bound-api
PyEllipsis and PyNotImplemented new get_bound api
2024-02-05 07:50:03 +00:00
Blaž Šnuderl f1384f3582 Implement PyNone.get() using PyNone.get_bound() 2024-02-05 08:06:59 +01:00
Blaž Šnuderl 1fd0aa2b19 Use new method to implement old 2024-02-05 08:03:25 +01:00
Blaz Snuderl 8388b14369 PyNotImplemented get_bound 2024-02-04 20:08:52 +01:00
Blaz Snuderl 8354590ae6 PyEllipsis get_bound method 2024-02-04 20:01:15 +01:00
David Hewitt 5dbb51b9ce
Merge pull request #3784 from davidhewitt/more-extract-bound
migrate many `FromPyObject` implementations to `Bound` API
2024-02-04 15:58:22 +00:00
David Hewitt c5b470202d
Merge pull request #3796 from Icxolu/marshal
convert `marshal` to `Bound` API
2024-02-04 15:56:00 +00:00
Icxolu 304c8e655a convert `marshal` to `Bound` API 2024-02-04 16:25:00 +01:00
David Hewitt 0d4df9c19d adjust `FromPyObject` implementations to always use `'py` lifetime 2024-02-04 14:22:03 +00:00
David Hewitt 2a741a21e6 migrate many `FromPyObject` implementations to `Bound` API 2024-02-04 14:22:03 +00:00
David Hewitt cd9c21f89f
Merge pull request #3793 from snuderl/PyNone-new-api
Implement new API for PyNone #3684
2024-02-04 14:20:32 +00:00
Blaž Šnuderl d1e967e9ea Uncomment a test 2024-02-04 07:31:29 +01:00
Blaž Šnuderl eca943ea35 Add new get_bound and mark old get as deprecated 2024-02-04 07:30:28 +01:00
Blaž Šnuderl 7efd412a63 Merge branch 'main' into PyNone-new-api 2024-02-04 07:29:32 +01:00
David Hewitt 975f182e68
Merge pull request #3794 from davidhewitt/revert-python-none
Revert "Merge pull request #3578 from davidhewitt/typed-helpers"
2024-02-03 21:17:28 +00:00
David Hewitt 76d1b34cd5 Revert "Merge pull request #3578 from davidhewitt/typed-helpers"
This reverts commit 7b07d6d21b, reversing
changes made to 99858236bd.
2024-02-03 20:56:23 +00:00
Blaz Snuderl b1863c73df clippy 2024-02-03 21:25:47 +01:00
Blaz Snuderl 507ea28b27 test 2024-02-03 21:14:31 +01:00
Blaž Šnuderl 9641b11752 hmm 2024-02-03 20:57:46 +01:00
Blaž Šnuderl a2a6062adc fmt 2024-02-03 20:48:25 +01:00
Blaž Šnuderl 7e94da576d Fix doctests 2024-02-03 20:44:48 +01:00
Blaž Šnuderl 5e9d97d1c6 Implement new API for PyNone #3684 2024-02-03 19:09:12 +01:00
David Hewitt d8c5e7943c
Merge pull request #3790 from Icxolu/bool
add `Bound` constructor for `PyBool`
2024-02-03 13:10:30 +00:00
Icxolu af21a9dc74 add `Bound` constructor for `PyBool` 2024-02-03 12:06:50 +01:00
David Hewitt 8f8d4d33fa
Merge pull request #3776 from davidhewitt/bound-extract
migrate `FromPyObject` for `Bound` and `Py` to new APIs
2024-02-02 23:10:47 +00:00
David Hewitt 57735540e8
Merge pull request #3736 from Tpt/tpt/systemtime
Adds conversion between SystemTime and datetime
2024-02-01 14:05:51 +00:00
David Hewitt a60c1821af implement `PyFunctionArgument` for `&Bound<T>` 2024-02-01 13:22:53 +00:00
David Hewitt d35a6a1fd6
Merge pull request #3785 from davidhewitt/bound-as-any
add `Bound::as_any` and `Bound::into_any` (and same for `Py`)
2024-02-01 10:52:25 +00:00
David Hewitt 49a57dfd18 clean up implementations in `src/instance.rs` 2024-02-01 10:14:58 +00:00
David Hewitt 516c085131
Merge pull request #3777 from davidhewitt/bytes-new-bound
add `PyBytes::new_bound`
2024-02-01 09:17:26 +00:00
David Hewitt 4437e8f616 add `Py::as_any` and `Py::into_any` 2024-02-01 09:07:36 +00:00
David Hewitt cbc97f8ea9 add `Bound::as_any` and `Bound::into_any` 2024-02-01 09:01:33 +00:00
David Hewitt 4c94be51a7 add `PyBytes::new_bound` 2024-02-01 08:52:28 +00:00
David Hewitt aa1a9864f7
Merge pull request #3786 from Icxolu/bytearray
add `Bound` constructors for `PyByteArray` and `PyMemoryView`
2024-01-31 08:11:54 +00:00
Icxolu b14dbcf29f add `Bound` constructors for `PyMemoryView` 2024-01-30 22:52:31 +01:00
Icxolu e704a760b7 add `Bound` constructors for `PyByteArray` 2024-01-30 22:52:31 +01:00
David Hewitt 6040d93032
Merge pull request #3781 from davidhewitt/intern-bound
change return type of `intern!` macro to `&Bound<PyString>`
2024-01-30 13:57:29 +00:00
David Hewitt c93073075b
Merge pull request #3783 from davidhewitt/remove-pool-bench
remove bench of `GILPool::new`
2024-01-30 13:30:31 +00:00
David Hewitt 2f00eb1423 for now just change return type of `intern!` 2024-01-30 13:28:07 +00:00
David Hewitt 0bb9de1aba remove bench of `GILPool::new` 2024-01-30 11:45:06 +00:00
David Hewitt aa139ad422 add `intern_bound!` macro 2024-01-30 10:58:19 +00:00
David Hewitt fed8bcadaf add remaining bound string constructors 2024-01-30 09:13:24 +00:00
David Hewitt 718be9fac5
Merge pull request #3770 from Icxolu/capsule
implement `PyCapsuleMethods`
2024-01-29 21:23:43 +00:00
David Hewitt fbe6f158ec
Merge pull request #3780 from PyO3/dependabot/github_actions/dorny/paths-filter-3
Bump dorny/paths-filter from 2 to 3
2024-01-29 21:06:38 +00:00
dependabot[bot] ab90403953
Bump dorny/paths-filter from 2 to 3
Bumps [dorny/paths-filter](https://github.com/dorny/paths-filter) from 2 to 3.
- [Release notes](https://github.com/dorny/paths-filter/releases)
- [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dorny/paths-filter/compare/v2...v3)

---
updated-dependencies:
- dependency-name: dorny/paths-filter
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-29 20:31:27 +00:00
Icxolu e323fcbb9e implement `PyCapsuleMethods` 2024-01-29 18:22:40 +01:00
David Hewitt a3eb328378 migrate `FromPyObject` for `Bound` and `Py` to `extract_bound` 2024-01-29 13:46:46 +00:00
David Hewitt 0d421b1ca2
Merge pull request #3774 from davidhewitt/string-new-bound
add `PyString::new_bound`
2024-01-29 13:29:16 +00:00
David Hewitt c47565666d add `PyString::new_bound` 2024-01-29 13:14:00 +00:00
David Hewitt 7549a21154
Merge pull request #3773 from davidhewitt/float-new-bound
add `PyFloat::new_bound`
2024-01-29 11:52:45 +00:00
David Hewitt 7f2d1d2aa5
Merge pull request #3772 from davidhewitt/dict-new-bound
add `PyDict::new_bound` without deprecation
2024-01-29 11:16:04 +00:00
David Hewitt 345e122bbf add `PyFloat::new_bound` 2024-01-29 11:06:34 +00:00
David Hewitt d4d08b24b0 add `PyDict::new_bound` without deprecation 2024-01-29 10:17:54 +00:00
David Hewitt c54d8976db
Merge pull request #3706 from davidhewitt/frompyobject2
Add `extract_bound` method to FromPyObject
2024-01-28 07:48:03 +00:00
David Hewitt ffaa03e3f1 Migrate some conversions to `extract_bound` 2024-01-28 07:22:51 +00:00
David Hewitt 595ca4b3c1 Add `extract_bound` method to `FromPyObject` 2024-01-28 07:22:51 +00:00
David Hewitt eb8d11f42f
Merge pull request #3769 from davidhewitt/remove-test-pep-587
remove `test_pep_587`
2024-01-27 22:33:35 +00:00
David Hewitt ed7263faa2
Merge pull request #3767 from Icxolu/complex
implement `PyComplexMethods`
2024-01-27 22:06:02 +00:00
David Hewitt 5ccc46e459 remove `test_pep_587` 2024-01-27 21:57:43 +00:00
David Hewitt 796e4192b7
Merge pull request #3755 from davidhewitt/list-bound
add list bound constructors
2024-01-27 21:27:22 +00:00
Icxolu 37e2a4d9c9 implement `PyComplexMethods` 2024-01-27 22:26:53 +01:00
David Hewitt 7927a2e211 add bench for tuple `get_borrowed_item` 2024-01-27 21:12:55 +00:00
David Hewitt 1657109ae0 documentation updates for `PyList::new_bound` 2024-01-27 21:12:55 +00:00
David Hewitt 57a49a2b12 update tuple benchmarks for bound API 2024-01-27 21:12:55 +00:00
David Hewitt 674f7282d8 `ToPyObject` and `IntoPy` for `Borrowed` 2024-01-27 21:12:55 +00:00
David Hewitt eed196329d add list bound constructors 2024-01-27 21:12:55 +00:00
David Hewitt 0973da27e9
Merge pull request #3743 from davidhewitt/set-bound-constructors
add bound constructors for `PySet` and `PyFrozenSet`
2024-01-27 17:55:42 +00:00
Tpt f83544910f Adds conversion between SystemTime and datetime 2024-01-27 17:43:51 +01:00
David Hewitt 5f320d7a04
Merge pull request #3765 from davidhewitt/remove-py-newref
remove internal uses of `_Py_NewRef`
2024-01-27 13:35:29 +00:00
David Hewitt f09ad1e28f
Merge pull request #3763 from Icxolu/slice-traceback
implement `PyTracebackMethods` and `PySliceMethods`
2024-01-27 12:42:37 +00:00
David Hewitt 87e0610b58 remove internal uses of `_Py_NewRef` 2024-01-27 12:07:46 +00:00
Icxolu 7fddd983b4 update `test_compile_error` ui test output 2024-01-27 12:37:26 +01:00
Icxolu 7918815cee implement `PySliceMethods` 2024-01-27 11:34:32 +01:00
Icxolu f86053e2c2 implement `PyTracebackMethods` 2024-01-27 11:34:32 +01:00
David Hewitt f449fc0fc1
Merge pull request #3753 from PyO3/dependabot/github_actions/actions/cache-4
Bump actions/cache from 3 to 4
2024-01-23 23:24:37 +00:00
David Hewitt 173d0afb7b
Merge pull request #3757 from davidhewitt/pypy-7.3.15
update ffi structures for PyPy 7.3.15
2024-01-23 23:24:22 +00:00
David Hewitt 11b5ae7f5f update ffi structures for PyPy 7.3.15 2024-01-23 20:34:19 +00:00
David Hewitt 3af73fabcf
Merge pull request #3754 from Xuanwo/patch-1
docs: Update opendal's repo name
2024-01-23 08:54:17 +00:00
Xuanwo 4d40f4183f
docs: Update opendal's repo name
Apache OpenDAL is now a graduated project, remove the incubator prefix in the repo name.
2024-01-23 14:39:38 +08:00
dependabot[bot] bcfbbf198d
Bump actions/cache from 3 to 4
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 20:07:15 +00:00
David Hewitt bf32986b1d
Merge pull request #3751 from mkovaxx/fix_ugly_enum_example
docs: make complex enum example in class.md more readable
2024-01-20 08:19:33 +00:00
Mate Kovacs f32becacc7 fix ugly example 2024-01-20 11:47:27 +09:00
David Hewitt d1b072222a
Merge pull request #3582 from mkovaxx/pyclass_complex_enum
Full ADT support with pyclass for complex enums
2024-01-19 14:17:28 +00:00
Mate Kovacs 3ed5ddb0ec feat: support pyclass on complex enums 2024-01-18 22:04:42 +09:00
David Hewitt 4e24e680e8 update set benchmarks 2024-01-17 10:49:21 +00:00
David Hewitt 06c95432c6 set & frozenset bound constructors 2024-01-17 09:45:41 +00:00
David Hewitt 43504cd15a
Merge pull request #3742 from samuelcolvin/int-extraction-performance
improve performance of successful int extract by ~30% by avoiding calls to `__index__` where redundant
2024-01-16 17:14:39 +00:00
Samuel Colvin 0e876d94d6
improve performance of successful int extract by ~30%
add newsfragment

formatting

skip slow path on 3.8+

formatting

cfg if,else

formatting again

dedicated macro, change int_convert_u64_or_i64 too

add float tests

force index call for PyLong_AsUnsignedLongLong

perform PyLong check for 3.8 too

perform PyLong check for <3.10
2024-01-16 13:51:19 +00:00
David Hewitt 7366b1a386
Merge pull request #3730 from Tpt/chrono-tz
Conversion between chrono_tz::Tz and zoneinfo.ZoneInfo
2024-01-15 14:48:27 +00:00
David Hewitt 48e74b7829
Merge pull request #3734 from jadedpasta/ffi-pytype-getmodulebydef
ffi: Add definition for PyType_GetModuleByDef
2024-01-13 08:49:36 +00:00
jadedpasta 83f0f22efe ffi: Add definition for PyType_GetModuleByDef
This API is available starting in 3.11. It is not part of the Stable ABI.

PyType_GetModuleByDef searches the MRO of the given type for a module
matching the given module spec. It can be useful for users use that
`pyo3_ffi` directly and want to support multiple interpreters/per
interpreter GIL. It is useful to obtain access to the module state from
special methods like `tp_new` that can't use the METH_METHOD calling
convention.

This API is not supported on PyPy yet, but guess the symbol name for the future.
2024-01-12 22:13:33 -06:00
David Hewitt f8878a7440
Merge pull request #3731 from PyO3/dependabot/github_actions/actions/setup-python-5
Bump actions/setup-python from 4 to 5
2024-01-12 17:51:13 +00:00
David Hewitt 590ce70bed
Merge pull request #3740 from davidhewitt/nightly-lints
allow dead_code in `IPowModulo`
2024-01-12 16:27:24 +00:00
David Hewitt 0db6dce9ce
Merge pull request #3739 from davidhewitt/pypy-buffer-hidden-fields
fix size of pypy private fields in Py_buffer definition
2024-01-12 16:27:10 +00:00
David Hewitt 8fef7a5848 fix size of pypy private fields in Py_buffer definition 2024-01-12 15:59:14 +00:00
David Hewitt ab699a0727 allow dead_code in `IPowModulo` 2024-01-12 15:55:21 +00:00
David Hewitt 1520b058e8
Merge pull request #3738 from davidhewitt/nightly-lints
fix some nightly lints 2024-01-12
2024-01-12 13:52:50 +00:00
David Hewitt 4504a7c96e fix some nightly lints 2024-01-12 2024-01-12 13:34:17 +00:00
dependabot[bot] 800943ab2d
Bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-08 20:14:38 +00:00
Tpt 72f0c73925 Conversion between chrono_tz::Tz and zoneinfo.ZoneInfo 2024-01-08 15:19:49 +01:00
David Hewitt 4b172874dc
Merge pull request #3727 from davidhewitt/relnotes-0.20.2
release notes for 0.20.2
2024-01-05 07:26:23 +00:00
David Hewitt 026c0daf57 release notes for 0.20.2 2024-01-04 21:34:07 +00:00
David Hewitt 58746bb4f9
Merge pull request #3711 from davidhewitt/call-bound
add `call_bound` and `call_method_bound`
2024-01-03 13:43:30 +00:00
David Hewitt 50e33d86c7 add `call_bound` and `call_method_bound` 2024-01-03 13:24:14 +00:00
Adam Reichold f2eb121f76
Merge pull request #3723 from davidhewitt/fix-doc-build
Fix doc build
2024-01-02 21:02:27 +00:00
Adam Reichold 68240e16a7 Include the experimental-inspect feature for the docs.rs build thereby making it equivalent to a full build. 2024-01-02 20:47:52 +00:00
Adam Reichold c1c62f1f3c Add CI job to test the equivalent of a docs.rs build. 2024-01-02 20:47:46 +00:00
Adam Reichold e2c6eb86f9 Fix missing feature flags in implementation of Either conversion. 2024-01-02 20:47:42 +00:00
David Hewitt eceb28bc79
Merge pull request #3702 from davidhewitt/bound-iterator-constructor
introduce PyIterator::from_bound_object
2024-01-02 15:34:54 +00:00
David Hewitt 783e98b1a8 introduce `PyIterator::from_bound_object` 2024-01-02 14:11:26 +00:00
David Hewitt febf7a061d
Merge pull request #3721 from PyO3/fix-build-config-dep
Use a definite version specification when depending on pyo3-build-config.
2024-01-02 14:00:02 +00:00
Adam Reichold 03285835bb
Use a definite version specification when depending on pyo3-build-config.
We already do this for other internal pyo3-* dependencies and it seems prudent
to apply this to pyo3-build-config as well.
2024-01-02 09:32:38 +01:00
David Hewitt 5ea2e5c048
Merge pull request #3715 from davidhewitt/relnotes-0.20.1
release notes for 0.20.1
2023-12-31 08:59:01 +00:00
David Hewitt 0ca97b5e53
Merge pull request #3681 from davidhewitt/tuple2-try2
implement `PyTupleMethods`
2023-12-30 22:07:43 +00:00
David Hewitt 4cf58c8303 implement `IntoPy<Py<PyTuple>>` for `Bound<PyTuple>` 2023-12-30 21:37:46 +00:00
David Hewitt 823b5feed3 improve tuple methods test coverage 2023-12-30 21:37:46 +00:00
David Hewitt 53d25f7ff2 add new `PyTuple` constructors 2023-12-30 21:37:46 +00:00
David Hewitt 375e3d4eee implement `PyTupleMethods` 2023-12-30 21:37:46 +00:00
David Hewitt 8fa5294d93 release notes for 0.20.1 2023-12-30 21:34:45 +00:00
Alex Gaynor 54390bc50b
Merge pull request #3712 from alex/binops
add PyAnyMethods for binary operators
2023-12-30 13:56:45 +00:00
Alex Gaynor 339660c117 add PyAnyMethods for binary operators
also pow

fixes #3709
2023-12-29 18:45:18 -05:00
David Hewitt e1fcb4efce
Merge pull request #3703 from davidhewitt/module2
implement `PyModuleMethods`
2023-12-29 22:30:08 +00:00
David Hewitt e852a4b502 be more lax with ordering in `invalid_result_conversion` ui test 2023-12-29 22:06:33 +00:00
David Hewitt 9a5668572b implement `PyModuleMethods` 2023-12-29 21:46:46 +00:00
David Hewitt 6776b90e15
Merge pull request #3707 from davidhewitt/gil-refs-feature
add `gil-refs` feature to aid migration
2023-12-29 14:56:19 +00:00
David Hewitt a9f867c2cb begin drafting `Bound<T>` migration guide 2023-12-29 14:42:31 +00:00
David Hewitt 3da1aac2dd add `gil-refs` feature to aid migration 2023-12-29 14:42:31 +00:00
David Hewitt 54b214bb93
Merge pull request #3710 from davidhewitt/rust-1.75
ci: updates for Rust 1.75
2023-12-29 13:51:28 +00:00
David Hewitt 46c3190a17 clean up remnants of deprecated & removed features 2023-12-29 13:36:46 +00:00
David Hewitt e42d8cf612 ci: updates for Rust 1.75 2023-12-29 13:36:46 +00:00
David Hewitt 066e33488b
Merge pull request #3704 from davidhewitt/sansyrox-intro
Add link to YouTube introduction to PyO3
2023-12-26 16:50:20 +00:00
David Hewitt ac5db1fb4b Add link to YouTube introduction to PyO3 2023-12-26 15:30:28 +00:00
Adam Reichold 0344921326
Merge pull request #3694 from davidhewitt/set-frozenset
implement `PySetMethods` and `PyFrozenSetMethods`
2023-12-26 13:33:23 +00:00
David Hewitt 442d13dab3 introduce `Bound::unbind` 2023-12-26 13:17:12 +00:00
David Hewitt e4fd557d2a remove `IntoIterator` for `&Bound` 2023-12-26 13:10:36 +00:00
David Hewitt 8a28a69c3d implement `PyFrozenSetMethods` 2023-12-26 13:10:36 +00:00
David Hewitt 271cbf9edb implement `PySetMethods` 2023-12-26 13:10:36 +00:00
Adam Reichold c44d2f53d5
Merge pull request #3692 from davidhewitt/as-bound
Add `as_borrowed` conversion from gil-refs to `Bound<T>`
2023-12-26 10:45:43 +00:00
David Hewitt d36ad8f61f introduce `Bound::as_borrowed` 2023-12-26 09:49:03 +00:00
David Hewitt 1b61cb015a Add `.as_borrowed()` conversion from gil-refs to `Bound<T>` 2023-12-26 09:49:03 +00:00
Adam Reichold f37c682f8c
Merge pull request #3700 from davidhewitt/super-bound
introduce `PySuper::new_bound`
2023-12-25 09:48:39 +00:00
Adam Reichold ff373eb1c6
Merge pull request #3697 from davidhewitt/as-gil-ref
expose `Bound::as_gil_ref` and `Bound::into_gil_ref`
2023-12-25 09:48:23 +00:00
Adam Reichold bd660537d8
Merge pull request #3695 from davidhewitt/bound-iterator
implement iterator for `Bound` iterator
2023-12-25 09:46:01 +00:00
David Hewitt 38abfd2eed expose `Bound::as_gil_ref` and `Bound::into_gil_ref` 2023-12-24 22:10:09 +00:00
David Hewitt 382e9b9fb5
Merge pull request #3701 from alex/gevent-py37
Don't try to run the gevent tests on py37
2023-12-24 21:40:51 +00:00
Alex Gaynor 91fdfaab45 Use a version of gevent that supports py37
The current version of gevent we require is 3.8+ only
2023-12-24 16:24:47 -05:00
David Hewitt d9cc0c0f24 introduce `PySuper::new_bound` 2023-12-24 20:04:15 +00:00
David Hewitt 6ca63b5772
Merge pull request #3698 from davidhewitt/unraisable-bound
implement `PyErr::write_unraisable_bound`
2023-12-24 19:54:27 +00:00
David Hewitt d669a943f7 implement iterator for `Bound` iterator 2023-12-24 19:52:36 +00:00
David Hewitt 1004ffa7d6 export `Bound` and `Borrowed` from lib.rs 2023-12-24 19:35:50 +00:00
David Hewitt 877e34ac86 implement `PyErr::write_unraisable_bound` 2023-12-24 19:35:29 +00:00
David Hewitt 214ed29bbb
Merge pull request #3699 from davidhewitt/tzinfo
partially revert changes to `PyTzInfoAccess` trait
2023-12-24 15:25:45 +00:00
David Hewitt f5b18468bc partially revert changes to `PyTzInfoAccess` trait 2023-12-24 15:07:42 +00:00
Adam Reichold 7d245842d4
Merge pull request #3669 from davidhewitt/gevent
add test which is broken with gevent
2023-12-24 09:25:29 +00:00
David Hewitt 49d7718823 demonstrate switching to `Bound` API resolves `gevent` crash 2023-12-23 22:10:01 +00:00
David Hewitt 46fa1b2b80 add test which is broken with gevent 2023-12-23 22:07:48 +00:00
Adam Reichold e99058a442
Merge pull request #3679 from davidhewitt/datetime2
implement datetime traits for `Bound`
2023-12-23 14:54:30 +00:00
David Hewitt 6832bf88f2 implement datetime traits for Py2 2023-12-23 15:34:07 +01:00
Adam Reichold 8bef6e3398
Merge pull request #3689 from PyO3/unsendable-threadsafe-traverse
Turn calls of __traverse__ into no-ops for unsendable pyclass if on the wrong thread
2023-12-23 14:13:46 +00:00
Adam Reichold 4dc6c1643e Turn calls of __traverse__ into no-ops for unsendable pyclass if on the wrong thread
Adds a "threadsafe" variant of `PyCell::try_borrow` which will fail instead of
panicking if called on the wrong thread and use it in `call_traverse` to turn GC
traversals of unsendable pyclasses into no-ops if on the wrong thread.

This can imply leaking the underlying resource if the originator thread has
already exited so that the GC will never run there again, but it does avoid hard
aborts as we cannot raise an exception from within `call_traverse`.
2023-12-23 15:01:08 +01:00
David Hewitt 65f25d4133
Merge pull request #3690 from PyO3/check-signal-docs
Copy note on using check_signals on non-main thread/interpreter from Python docs.
2023-12-22 22:50:22 +00:00
Adam Reichold e58b251fef Copy note on using check_signals on non-main thread/interpreter from Python docs. 2023-12-22 12:08:37 +01:00
Adam Reichold a115877bba
Merge pull request #3686 from davidhewitt/bound
make Bound and Borrowed types public API
2023-12-21 16:25:01 +00:00
Adam Reichold 1fa47b0409
Merge pull request #3687 from alex/abi3-py312
added `abi3-py312` feature
2023-12-21 16:05:34 +00:00
David Hewitt 4ac6a6bf15 add safety note to `downcast_into_unchecked` 2023-12-21 15:51:56 +00:00
Alex Gaynor d92792f8ad Fixes #3645 -- added `abi3-py312` feature 2023-12-21 10:40:14 -05:00
David Hewitt 3092289020 expose `BoundDictIterator` and `BoundListIterator` 2023-12-21 13:09:22 +00:00
David Hewitt e8e6fb93d7 Add `Py::bind`, `Py::into_bound`, and `Py::bind_borrowed` 2023-12-21 13:03:59 +00:00
David Hewitt 0f242c399d make `DowncastError` and `DowncastIntoError` public 2023-12-21 13:03:59 +00:00
David Hewitt c08c6c0a41 make new downcast errors public API 2023-12-21 12:28:12 +00:00
David Hewitt a09b9f8834 make `Bound` and `Borrowed` types public API 2023-12-21 12:20:33 +00:00
David Hewitt 704e9fc7b5 `Py2` -> `Bound` 2023-12-21 12:04:45 +00:00
David Hewitt 2f080f4075 `Py2Borrowed` -> `Borrowed` 2023-12-21 12:02:56 +00:00
David Hewitt 2788f4a110
Merge pull request #3680 from davidhewitt/list2
implement `PyListMethods`
2023-12-21 11:13:04 +00:00
Adam Reichold 5b12cf1b8a
Merge pull request #3683 from PyO3/use-type-ref-helper
Some boy scouting w.r.t. usage of our internal helper functions when handling collection ABC
2023-12-21 10:46:56 +00:00
David Hewitt ee1272ed76 implement `Copy` for `Py2Borrowed` 2023-12-21 10:44:39 +00:00
David Hewitt de82e2d6e2 add `Py2Borrowed::to_owned` 2023-12-21 10:44:39 +00:00
David Hewitt 337e48328f implement `PyListMethods` 2023-12-21 10:44:37 +00:00
Adam Reichold 3c97167fd1 Use write_unraisable to report errors loading type objects for ABC checks. 2023-12-21 11:33:26 +01:00
Adam Reichold c1f4db0a9b Increase use of common get_or_try_init_type_ref helper when caching type objects. 2023-12-21 11:33:25 +01:00
Adam Reichold 7f626b26d4
Merge pull request #3678 from davidhewitt/mapping2
implement `PyMappingMethods`
2023-12-21 09:47:35 +00:00
David Hewitt 43827e39ee
Merge pull request #3677 from davidhewitt/string2
implement `PyStringMethods`
2023-12-20 22:04:23 +00:00
David Hewitt f4f3169cad implement `PyStringMethods` 2023-12-20 16:19:18 +00:00
David Hewitt 8bd2972201
Merge pull request #3675 from davidhewitt/dict2-try2
implement `PyDictMethods`
2023-12-20 14:59:15 +00:00
David Hewitt 8e7c90733d implement `PyMappingMethods` 2023-12-20 14:39:40 +00:00
David Hewitt 15d309eb1f implement `PyDictMethods` 2023-12-20 14:18:45 +00:00
Adam Reichold fd2fc983b1
Merge pull request #3676 from davidhewitt/any-iter
tidy up some Py2 <-> gil-ref conversions
2023-12-20 13:31:15 +00:00
Adam Reichold 1b3dc6d7ac
Merge pull request #3661 from PyO3/iter-output-type
Replace (A)IterNextOutput by autoref-based specialization to allow returning arbitrary value
2023-12-20 12:53:42 +00:00
David Hewitt 5181e35a61 tidy up some Py2 <-> gil-ref conversions 2023-12-20 12:50:15 +00:00
Adam Reichold 5528895f3e Relax the error type in the Result<Option<T>, E>> specializations for __(a)next__. 2023-12-20 13:12:16 +01:00
Adam Reichold a605308cee Add change log and migration guide entries. 2023-12-20 13:07:14 +01:00
David Hewitt a3c92fa319
Merge pull request #3601 from davidhewitt/deprecate-pytryfrom
deprecate `PyTryFrom` and `PyTryInto`
2023-12-20 11:55:35 +00:00
David Hewitt bc87b7bac6 deprecate `PyTryFrom` and `PyTryInto` 2023-12-20 11:28:24 +00:00
Adam Reichold 83697f0c62 Also replace IterANextOutput by autoref-based specialization to allow returning arbitrary values 2023-12-20 09:56:16 +01:00
Adam Reichold ca7d90dcf3 Replace IterNextOutput by autoref-based specialization to allow returning arbitrary values 2023-12-20 09:56:16 +01:00
Adam Reichold d75d4bdf81 Fix some holdouts from using argument holders for lifetime extensions. 2023-12-20 09:56:16 +01:00
Adam Reichold 3583b9ac67
Merge pull request #3670 from Tpt/duration
Adds std::duration::Duration from/to Python conversions
2023-12-20 08:10:55 +00:00
Tpt 0752942c3f Duration: drops truncation warning 2023-12-20 08:57:26 +01:00
Tpt 8b614745cf Adds std::duration::Duration from/to Python conversions 2023-12-20 08:57:26 +01:00
Adam Reichold ee54132ff6
Merge pull request #3671 from davidhewitt/dh/downcast-error-split
Add `Py2` variants of `PyDowncastError`
2023-12-19 21:31:38 +00:00
David Hewitt 1451418ee4 Add `Py2` variants of `PyDowncastError` 2023-12-19 20:59:57 +00:00
David Hewitt 54ba6e82ca
Merge pull request #3651 from davidhewitt/bytes2
implement `PyBytesMethods` and `PyByteArrayMethods`
2023-12-19 19:56:16 +00:00
David Hewitt 35f7f1a78c use Py2Borrowed to make PyBytesMethods slightly nicer 2023-12-19 19:02:23 +00:00
Adam Reichold 8bb64377b8
Merge pull request #3638 from PyO3/call-op-bool
Try harder by looking for a __bool__ magic method when extracing bool values from Python objects.
2023-12-19 18:52:55 +00:00
Adam Reichold 4177dfcc81 Apply __bool__ conversion only to numpy.bool_ to avoid false positives. 2023-12-19 18:55:28 +01:00
Adam Reichold 57002d2389 Align error message when no method __bool__ is defined with CPython's general style. 2023-12-19 18:55:28 +01:00
Adam Reichold 3e10d64fa2 Avoid attribute lookup overhead for __bool__ if the unlimited API is available. 2023-12-19 18:55:28 +01:00
Adam Reichold 8133aaa5d8 Try harder by looking for a __bool__ magic method when extracing bool values from Python objects.
I decided to not implement the full protocol for truth value testing [1] as it
seems confusing in the context of function arguments if basically any instance
of custom class or non-empty collection turns into `true`.

[1] https://docs.python.org/3/library/stdtypes.html#truth
2023-12-19 18:55:28 +01:00
David Hewitt e727640ef3
Merge pull request #3664 from Tpt/chrono-abi3
Chrono: compatibility with abi3
2023-12-19 17:53:30 +00:00
David Hewitt c5dce0172b implement `PyBytesMethods` and `PyByteArrayMethods` 2023-12-19 17:26:52 +00:00
Adam Reichold 87e42c96be
Merge pull request #3660 from PyO3/type-name-fast-path
Rename name to qualname and add name to better match Python and ensure abi3 fallbacks.
2023-12-19 16:36:44 +00:00
Adam Reichold 68f417fb1c Defend against mutable type objects when extracting their full name. 2023-12-19 16:51:24 +01:00
Adam Reichold 416d3c488f Rename name to qualname and full_name to name to better match Python 2023-12-19 16:51:24 +01:00
Adam Reichold 86989a7329
Merge pull request #3667 from PyO3/extend-holder-lifetime-redux
Bring back holder bindings as prerequiste to specialization on return value types
2023-12-19 15:46:15 +00:00
Adam Reichold f03ccf204c Also apply holder lifetime extension to slot implementations. 2023-12-19 16:29:43 +01:00
Adam Reichold 27019b5523 Use local variables to extend lifetime of holder references. 2023-12-19 16:21:18 +01:00
Adam Reichold 2fdd52003e Add PyType::full_name which is tp_name and has an abi3 fallback. 2023-12-19 15:47:21 +01:00
Tpt d7eac6527b Chrono: compatibility with abi3 2023-12-19 14:53:14 +01:00
David Hewitt 1dca87972a
Merge pull request #3662 from Tpt/chrono-test
Chrono: makes test independent of datetime C-API
2023-12-19 08:26:21 +00:00
David Hewitt 12b44ea16d
Merge pull request #3663 from Tpt/chrono-generic-datetime
Chrono: makes FromPyObject impl on DateTime generic
2023-12-19 08:22:47 +00:00
Tpt 0d2387e858 Chrono: makes FromPyObject impl on DateTime generic
ToPyObject was already generic
2023-12-18 20:12:30 +01:00
Tpt 7705181049 Chrono: makes test independent of datetime C-API 2023-12-18 17:12:39 +01:00
Adam Reichold ff50285d1f
Merge pull request #3657 from PyO3/is-truthy
Transition is_true to is_truthy to clarify that the test is not based on identity with or equality to the True singleton.
2023-12-16 13:16:49 +00:00
Adam Reichold ced97f80d1 Transition is_true to is_truthy to clarify that the test is not based on identity with or equality to the True singleton. 2023-12-16 14:04:37 +01:00
David Hewitt 867a273afc
Merge pull request #3648 from Tpt/timezone-constructor
Adds internal timezone_from_offset function
2023-12-15 16:12:32 +00:00
Tpt dcaed199c7 Adds internal timezone_from_offset function
It allows to build conversions from chrono without direct access to the C API
2023-12-15 17:00:25 +01:00
Adam Reichold 118d578ac1
Merge pull request #3652 from davidhewitt/bool2
implement `PyBoolMethods`
2023-12-15 14:55:56 +00:00
David Hewitt d7adc74ba5 implement `PyBoolMethods` 2023-12-15 14:39:04 +00:00
David Hewitt 97cf9b834c
Merge pull request #3653 from davidhewitt/native-type-source
Add `AsRefSource` to `PyNativeType`.
2023-12-14 22:33:49 +00:00
David Hewitt ef8532b175 Add `AsRefSource` to `PyNativeType`. 2023-12-14 18:35:06 +00:00
Adam Reichold d1b4b9e7d4
Merge pull request #3650 from davidhewitt/float2
implement `PyFloatMethods`
2023-12-14 18:07:32 +00:00
David Hewitt 8a7c5002bd rename `floatob.rs` to `float.rs` 2023-12-14 17:14:17 +00:00
David Hewitt 8bdae345f3 implement `PyFloatMethods` 2023-12-14 17:12:06 +00:00
Adam Reichold 763ecb381b
Merge pull request #3649 from davidhewitt/module-name-lifetime
hold onto module name properly in `PyCFunction::internal_new`
2023-12-14 16:54:23 +00:00
David Hewitt 015f028589 hold onto module name properly in `PyCFunction::internal_new` 2023-12-14 16:23:53 +00:00
David Hewitt 79a54cfc05
Merge pull request #3572 from davidhewitt/sequence2-try2
implement `PySequenceMethods`, try 2
2023-12-14 13:27:32 +00:00
David Hewitt 82ac801be4 introduce traits to make ffi ptr handling cleaner 2023-12-14 13:00:45 +00:00
David Hewitt ac4ee2841b implement `PySequenceMethods` 2023-12-14 13:00:45 +00:00
Adam Reichold fc82c9f870
Merge pull request #3636 from PyO3/more-async-self-hints
Expand guide on async methods borrowing self.
2023-12-14 10:58:48 +00:00
David Hewitt a010da2c58
Merge pull request #3647 from PyO3/fix-msrv
Fix the Crossbeam ecosystem to point releases before it required Rust 1.61.
2023-12-14 10:58:30 +00:00
Adam Reichold 97e0998ac2 Fix the Crossbeam ecosystem to point releases before it required Rust 1.61. 2023-12-14 11:35:20 +01:00
messense 42601f3af9
Merge pull request #3642 from PyO3/dependabot/github_actions/actions/setup-python-5
build(deps): bump actions/setup-python from 4 to 5
2023-12-12 02:55:22 +00:00
dependabot[bot] 20e9680a81
build(deps): bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 20:04:18 +00:00
Adam Reichold b0d4ef3525 Expand guide on async methods borrowing self. 2023-12-10 16:37:39 +01:00
David Hewitt 24d9113974
Merge pull request #3632 from messense/extract-frozen-set
Add support for extracting Rust set types from `frozenset`
2023-12-07 16:17:51 +00:00
messense 601d9573c8
Merge pull request #3631 from wyfo/doc_typo
docs: fix typos
2023-12-07 12:29:00 +00:00
messense c21a84d999
Add support for extracting Rust set types from `frozenset` 2023-12-07 20:25:31 +08:00
Joseph Perez e73c795967
docs: fix typos 2023-12-07 10:10:15 +01:00
David Hewitt 07726aefc4
Merge pull request #3609 from wyfo/async_receiver
feat: allow async methods to accept `&self`/`&mut self`
2023-12-07 07:38:25 +00:00
Joseph Perez f34c70c2da
feat: allow async methods to accept `&self`/`&mut self` 2023-12-07 07:42:10 +01:00
Adam Reichold 4baf0235c3
Merge pull request #3616 from neachdainn/3615-gilprotected-pyvisit
Enable `GILProtected` access via `PyVisit`
2023-12-05 20:43:38 +00:00
Nathan Kent 3249feb85c
Enable `GILProtected` access via `PyVisit`
Closes #3615

This simply adds a new method which uses the existence of a `PyVisit`
object as proof that the GIL is held instead of a `Python` object. This
allows `GILProtected` to be used in instances where contained Python
objects need to participate in garbage collection. Usage in this
situation should be valid since no Python calls are made and this does
not provide any additional mechanism for accessing a `Python` object.
2023-12-05 11:10:00 -08:00
David Hewitt 856c573231
Merge pull request #3600 from davidhewitt/pytypecheck
replace `PyTryFrom` by splitting `PyTypeInfo`
2023-12-05 09:52:35 +00:00
David Hewitt de6d8b7d30
Merge pull request #3629 from davidhewitt/pytests-debug
ci: fixup pytests to compile in debug
2023-12-05 05:30:57 +00:00
David Hewitt ed87637ebb replace `PyTryFrom` by splitting `PyTypeInfo` 2023-12-05 08:01:30 +03:00
David Hewitt 5326bce57f ci: fixup pytests to compile in debug 2023-12-05 08:01:02 +03:00
David Hewitt 93370d9f6c
Merge pull request #3627 from davidhewitt/codspeed-fixup
try to debug codspeed issue
2023-12-04 22:51:55 +00:00
David Hewitt 2a5dedcbb5 ci: refactor pytests dev dependencies 2023-12-05 00:54:32 +03:00
David Hewitt 16ae0e2efe
Merge pull request #3608 from wyfo/remove_futures
refactor: drop futures_util dependency
2023-12-04 19:17:43 +00:00
Joseph Perez 2ca9f59d6f
refactor: drop futures_util dependency 2023-12-04 18:56:34 +01:00
David Hewitt e8f852bce7
Merge pull request #3599 from wyfo/coroutine_cancel
feat: add `coroutine::CancelHandle`
2023-12-04 12:44:36 +00:00
Joseph Perez 8a674c2bd3
feat: add `coroutine::CancelHandle` 2023-12-04 07:46:51 +01:00
David Hewitt 38beac4fb2
Merge pull request #3622 from messense/pyimport-frozen-ffi
Add additional definitions for `_PyImport_Frozen*`
2023-12-04 05:09:56 +00:00
messense 28dda997f9
Add additional definitions for `_PyImport_Frozen*` 2023-12-03 21:47:00 +08:00
David Hewitt 1556845e78
Merge pull request #3619 from PyO3/portable-atomic
Use portable-atomic for targets which lack 64-bit atomics used to check interpreter ID.
2023-12-03 07:16:33 +00:00
Adam Reichold e6457c5e99 Use portable-atomic for targets which lack 64-bit atomics used to check interpreter ID.
I chose to make the dependency mandatory instead of optional as portable-atomic
itself just forwards to the native atomics when they are available so making
that choice part of our build system is not really necessary. Personally, I was
unable to perceive any noticeable compile-time hit from adding it.
2023-12-02 07:57:00 +01:00
David Hewitt 81ad2e8bab
Merge pull request #3598 from davidhewitt/clippy-beta
ci: run beta clippy as an allowed-to-fail job
2023-11-29 06:47:21 +00:00
David Hewitt 016b11069e
Merge pull request #3604 from alex/monomorphize-create_type_object
Refactor create_type_object so that most of the code is monomorphic
2023-11-29 06:47:03 +00:00
Alex Gaynor dd6e0339d3 Refactor create_type_object so that most of the code is monomorphic
In pyca/cryptography this function is the #1 source of lines of generated LLVM IR, because it is duplicated 42x (and growing!). By rewriting it so most of the logic is monomorphic, we reduce the generated LLVM IR for this function by 4x.
2023-11-28 20:09:34 -05:00
David Hewitt cf67c2ce46 fix test-serde beta clippy warning 2023-11-28 07:29:45 +00:00
David Hewitt 41842f9e4b fix pyo3-ffi beta clippy warnings 2023-11-28 07:29:45 +00:00
David Hewitt 8e5ef9058b ci: run beta clippy as an allowed-to-fail job 2023-11-28 07:29:45 +00:00
David Hewitt e62e6cad5d
Merge pull request #3603 from davidhewitt/0.19-deprecations
remove all functionality deprecated in 0.19
2023-11-28 06:47:30 +00:00
David Hewitt 53311a90eb
Merge pull request #3602 from davidhewitt/dead-kws
remove some unused keyword declarations
2023-11-28 06:45:43 +00:00
Alex Gaynor 5080deedbf
Merge pull request #3564 from PyO3/alex-patch-1
fixes #3561 -- silence new clippy warning
2023-11-27 22:41:50 +00:00
Alex Gaynor fae209419c fixes #3561 -- silence new clippy warning 2023-11-27 17:06:59 -05:00
David Hewitt 5c6d49084f remove all functionality deprecated in 0.19 2023-11-27 22:02:19 +00:00
David Hewitt e76797a1d6 remove some unused keyword declarations 2023-11-27 21:32:01 +00:00
David Hewitt 0f34fcd4b7
Merge pull request #3508 from mejrs/sub2
Create subinterpreter example
2023-11-26 10:42:31 +00:00
mejrs 597a184b4f Create subinterpreter example 2023-11-26 09:49:56 +00:00
David Hewitt d8002c4b2b
Merge pull request #3588 from wyfo/coroutine_name
feat: add coroutine `__name__`/`__qualname__`
2023-11-26 09:23:29 +00:00
Joseph Perez 781b9e3983
feat: add coroutine `__name__`/`__qualname__` and not-awaited warning 2023-11-25 21:51:20 +01:00
Adam Reichold 1203921d5c
Merge pull request #3456 from aldanor/feature/either
Add conversion support for `either::Either`
2023-11-25 09:37:49 +00:00
Adam Reichold 81bc838acd
Merge pull request #3577 from davidhewitt/none-typeinfo
Implement `PyTypeInfo` for `PyEllipsis`, `PyNone`, and `PyNotImplemented`
2023-11-25 09:36:43 +00:00
David Hewitt 9f66846238
Merge pull request #3595 from davidhewitt/ok-wrap
refactor `OkWrap` to not call `.into_py(py)`
2023-11-25 05:43:54 +00:00
messense cbd06309ff
Merge pull request #3596 from davidhewitt/smallvec-docs
docs: fixup docs for smallvec feature
2023-11-25 01:36:51 +00:00
David Hewitt cd8526ecc6 Implement `PyTypeInfo` for `PyEllipsis`, `PyNone`, and `PyNotImplemented` 2023-11-24 22:31:39 +00:00
David Hewitt bead83f4b0 docs: fixup docs for smallvec feature 2023-11-24 22:11:40 +00:00
Ivan Smirnov a75464ee26 add conversion support for `either::Either` 2023-11-24 22:09:34 +00:00
David Hewitt c814078866 refactor `OkWrap` to not call `.into_py(py)` 2023-11-24 10:41:08 +00:00
David Hewitt 31b871a9c0
Merge pull request #3594 from davidhewitt/remove_type_is_pymodule
remove `type_is_pymodule`
2023-11-24 10:09:50 +00:00
David Hewitt 5ac56b8eb0 improve error for invalid `#[classmethod]` receivers 2023-11-24 03:24:46 +00:00
David Hewitt aba3a3552d remove type_is_pymodule 2023-11-22 21:30:10 +00:00
David Hewitt 81df150823
Merge pull request #3593 from wyfo/fix_nox
fix: replace removed `fmt` session by `rustfmt` and `ruff`
2023-11-22 20:51:31 +00:00
Joseph Perez 226a2a3f7a
fix: replace removed `fmt` session by `rustfmt` and `ruff` 2023-11-22 21:00:51 +01:00
David Hewitt 69870d2298
Merge pull request #3540 from wyfo/coroutine
feat: support `async fn` in macros with coroutine implementation
2023-11-22 19:52:15 +00:00
David Hewitt 3f0dfa9698
Merge pull request #3587 from wyfo/classmethod_into
feat: allow classmethods to receive `Py<PyType>`
2023-11-22 19:34:19 +00:00
Joseph Perez 627841f1e2
feat: support `async fn` in macros with coroutine implementation 2023-11-22 20:25:36 +01:00
Joseph Perez 744de3a142
feat: allow `classmethod`/`pass_module` to receive owned types
This is necessary for async functions
2023-11-22 07:45:59 +01:00
David Hewitt abe518d164
Merge pull request #3586 from davidhewitt/semver-checks-v2
enable cargo-semver-checks, try 2
2023-11-20 07:07:44 +00:00
David Hewitt 7b07d6d21b
Merge pull request #3578 from davidhewitt/typed-helpers
Change return types of `py.None()`, `py.NotImplemented()` and `py.Ellipsis()` to typed singletons
2023-11-20 07:07:12 +00:00
David Hewitt 4a43b2f454 bump version to 0.21.0-dev 2023-11-19 06:41:10 +00:00
David Hewitt 24f3d0da62 enable cargo-semver-checks, try 2 2023-11-18 13:25:05 +00:00
Adam Reichold 99858236bd
Merge pull request #3583 from davidhewitt/1.74-lints
ci: move lints to new 1.74 cargo.toml tables
2023-11-18 09:56:11 +00:00
David Hewitt bd0174aa5d Change return types of `py.None()`, `py.NotImplemented()` and `py.Ellipsis()` to typed singletons 2023-11-17 16:16:19 +00:00
David Hewitt a9305ab389 ci: move lints to new 1.74 cargo.toml tables 2023-11-17 15:41:52 +00:00
David Hewitt d89c38822e
Merge pull request #3579 from davidhewitt/rust-1.74
ci: updates for rust 1.74
2023-11-16 17:02:59 +00:00
David Hewitt 29ad73b6d5 ci: updates for rust 1.74 2023-11-16 16:06:01 +00:00
David Hewitt 1e28ba7fd0
Merge pull request #3570 from suriya-ganesh/patch-1
docs: fix missing char conversion
2023-11-15 00:54:44 +00:00
David Hewitt 78ae491557
Merge pull request #3575 from davidhewitt/no-rust-toolchain
ci: try to run without rust-toolchain.toml
2023-11-15 00:41:28 +00:00
David Hewitt ef9741850f ci: try to run without rust-toolchain.toml 2023-11-14 19:54:39 +00:00
Surya 0ff84d250e
fix missing char conversion 2023-11-10 09:27:01 -05:00
Bruno Kolenbrander c8fdb80630
Merge pull request #3559 from davidhewitt/cross-compile-link
add link to user guide to cross compile error message
2023-10-30 23:19:08 +00:00
David Hewitt 852e4fea04 add link to user guide to cross compile error message 2023-10-30 22:21:56 +00:00
David Hewitt 7a2c63da76
Merge pull request #3536 from davidhewitt/maturin-build-args
examples: remove requirements-dev.txt files
2023-10-29 13:27:15 +00:00
Adam Reichold bb808dad5c
Merge pull request #3556 from wyfo/giloncecell_take
feat: add `take` and `into_inner` methods to `GILOnceCell`
2023-10-29 13:15:45 +00:00
Joseph Perez f9107191f5 docs: add newsfragment 2023-10-29 13:54:10 +01:00
Joseph Perez 1fd4090bd4 refactor: remove useless `unsafe` in `get_mut` 2023-10-29 13:50:36 +01:00
Joseph Perez bb1cc93797 feat: add `take` and `into_inner` methods to `GILOnceCell` 2023-10-29 08:33:04 +01:00
Adam Reichold dc251d0c9f
Merge pull request #3552 from daemontus/main
docs: Example of dynamic return type in the "Python classes" guide
2023-10-27 14:32:18 +00:00
Adam Reichold 579af5e4cb
Merge pull request #3551 from davidhewitt/ruff
ci: switch from black to ruff
2023-10-27 06:09:21 +00:00
Samuel Pastva 2fbc02d06c
Finish rename 2023-10-26 19:02:06 -05:00
Samuel Pastva fc787eabd2
Update guide/src/class.md
Co-authored-by: Bruno Kolenbrander <59372212+mejrs@users.noreply.github.com>
2023-10-26 18:58:43 -05:00
Samuel Pastva 48c90d9586 Add example of dynamic return type in the "Python classes" section of the guide. 2023-10-26 17:13:32 -05:00
David Hewitt 3042ab1621 ci: switch from black to ruff 2023-10-26 21:04:49 +01:00
Adam Reichold aa6622bb1a
Merge pull request #3550 from davidhewitt/pyenv-install
docs: improve detail around pyenv install
2023-10-26 08:45:00 +00:00
David Hewitt 391687dab6 examples: remove requirements-dev.txt files 2023-10-26 08:24:30 +01:00
David Hewitt 7534c31a6d
Note about `pyenv activate` and `pyenv virtualenv` commands
Co-authored-by: Niko Matsakis <niko@alum.mit.edu>
2023-10-26 08:21:31 +01:00
Adam Reichold 2b91fea94f
Merge pull request #3548 from davidhewitt/nightly-code
ci: fix nightly unused import warning
2023-10-26 06:01:18 +00:00
Adam Reichold 719b87cc41
Merge pull request #3539 from davidhewitt/emit-cfgs-coverage
add coverage for `emit_pyo3_cfgs`
2023-10-25 22:50:47 +00:00
David Hewitt 572a27d2d3 ci: fix nightly unused import warnings 2023-10-25 23:30:14 +01:00
David Hewitt 98346dd137 docs: improve detail around pyenv install 2023-10-25 23:29:20 +01:00
David Hewitt e284f3669f add coverage for `emit_pyo3_cfgs` 2023-10-25 22:59:43 +01:00
David Hewitt 33ab4e5295
Merge pull request #3543 from davidhewitt/dev-deps-clean
ci: tidy up some dev deps
2023-10-25 20:17:43 +00:00
Adam Reichold 95c8ee9afc
Merge pull request #3542 from PyO3/dependabot/github_actions/actions/setup-node-4
Bump actions/setup-node from 3 to 4
2023-10-25 19:07:24 +00:00
David Hewitt f302b1c6bc ci: tidy up some dev deps 2023-10-25 20:33:41 +02:00
David Hewitt de6162f533
Merge pull request #3546 from davidhewitt/older-hashbrown
ci: use older hashbrown and indexmap for MSRV
2023-10-25 08:01:35 +00:00
David Hewitt d835a4c86c allow CI to continue when nightly Rust is broken 2023-10-25 08:33:00 +01:00
David Hewitt b281a6cc08 ci: use older hashbrown and indexmap for MSRV 2023-10-24 21:50:10 +01:00
David Hewitt 8c8f00e69d
Merge pull request #3541 from PyO3/dependabot/github_actions/actions/checkout-4
Bump actions/checkout from 3 to 4
2023-10-23 20:59:10 +00:00
dependabot[bot] 0f42c688f8
Bump actions/setup-node from 3 to 4
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-23 20:32:21 +00:00
dependabot[bot] 64b639b945
Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-23 20:32:16 +00:00
Adam Reichold 9d75325d0c
Merge pull request #3532 from davidhewitt/dict-from-seq
change `PyDict::from_sequence` to take just `&PyAny`
2023-10-20 15:06:08 +00:00
David Hewitt d895734499 change `PyDict::from_sequence` to take just `&PyAny` 2023-10-20 11:59:26 +01:00
David Hewitt d8a6f37fec
Merge pull request #3421 from davidhewitt/codspeed
try adding codspeed benchmarks
2023-10-20 09:14:41 +00:00
Adam Reichold 63e24df968
Merge pull request #3529 from jessekrubin/utiles2examples
docs: utiles added to examples of readme!
2023-10-20 06:02:19 +00:00
David Hewitt bf2c5a896b add codspeed benchmarks 2023-10-19 21:36:05 +01:00
jessekrubin de30e3b1a5 utiles added to examples of readme! 2023-10-19 12:13:00 -07:00
messense 3aaa7e5615
Merge pull request #3526 from davidhewitt/ci-beta
ci: include beta as an allowed-to-fail job
2023-10-19 03:34:48 +00:00
David Hewitt e502c133a4 ci: include beta as an allowed-to-fail job 2023-10-18 22:21:29 +01:00
Adam Reichold 638a9ad84c
Merge pull request #3525 from davidhewitt/architecture-corrections
small tidy ups to Architecture.md
2023-10-17 19:31:57 +00:00
David Hewitt 708c644ecd small tidy ups to Architecture.md 2023-10-17 20:06:22 +01:00
messense fe60f313f9
Merge pull request #3518 from davidhewitt/ci-actions
ci: install prebuilt cargo-careful
2023-10-17 02:46:44 +00:00
David Hewitt 0c58648059 ci: install prebuilt cargo-careful 2023-10-16 20:16:01 +01:00
David Hewitt 674dac8bfc
Merge pull request #3517 from davidhewitt/remove-comparison
remove comparison to rust-cpython
2023-10-16 19:11:29 +00:00
David Hewitt 2ca8e573a4 remove comparison to rust-cpython 2023-10-15 22:03:24 +01:00
David Hewitt f12f928bd5
Merge pull request #3514 from messense/memoryview
Add `PyMemoryView` type
2023-10-15 15:08:50 +00:00
David Hewitt aa28cec893
Merge pull request #3507 from orhun/feat/support_smallvec_conversion
Add support for `SmallVec` in conversion traits (#3440)
2023-10-15 14:43:55 +00:00
messense dca18b3e41
Fix `PyMemoryView_FromBuffer` cfg 2023-10-15 22:24:58 +08:00
messense f4e64aadef
Add test cases for new `TryFrom` impls 2023-10-15 21:53:31 +08:00
David Hewitt cabbca4003
Merge pull request #3515 from davidhewitt/drop-psutil
ci: drop psutil dependency
2023-10-15 13:43:11 +00:00
David Hewitt 410fef5d79 ci: drop psutil dependency 2023-10-15 14:15:38 +01:00
messense d4ed66fff0
Add `TryFrom` impls for `PyByteArray` and `PyMemoryView` 2023-10-15 17:39:01 +08:00
messense 5b94241bd7
Add `PyMemoryView` type 2023-10-15 17:32:09 +08:00
messense b8cf9e8358
Merge pull request #3512 from PyO3/fix-chrono-dep
Align chrono dev and runtime dependency version specifications.
2023-10-14 08:23:35 +00:00
Adam Reichold 7ad122e7d9 Align chrono dev and runtime dependency version specifications. 2023-10-14 09:18:45 +02:00
Orhun Parmaksız 87cbd38229
Set version of smallvec to 1.0 2023-10-13 15:37:25 +03:00
David Hewitt bed3ebeb37
Merge pull request #3489 from davidhewitt/bump-ci-version
bump "latest" CI jobs to 3.12
2023-10-13 12:20:55 +00:00
Orhun Parmaksız 31acf0dc6f
Add an entry to features table in lib 2023-10-13 13:35:30 +03:00
David Hewitt db13a97790
Merge pull request #3445 from davidhewitt/py2-internals
add `Py2` as an internal API for optimization and dogfooding
2023-10-13 06:49:08 +00:00
David Hewitt 6801c508a8 keep emscripten back on 3.11 for now 2023-10-13 08:25:36 +02:00
David Hewitt cac95f31c7 add Py2 as an internal API for optimization and dogfooding 2023-10-13 08:10:37 +02:00
David Hewitt 2b7eb3a05e also test emscripten with CI-build-full 2023-10-13 00:57:39 +02:00
David Hewitt b2df27f0a1 bump "latest" CI jobs to 3.12 2023-10-13 00:57:39 +02:00
David Hewitt 642b335ce3
Merge pull request #3493 from PyO3/release-0.20
release: 0.20.0
2023-10-11 21:23:08 +00:00
Orhun Parmaksız dfeae473e5
Add support for `SmallVec` in conversion traits (#3440) 2023-10-11 17:14:23 +03:00
487 changed files with 36552 additions and 10848 deletions

View File

@ -1,20 +0,0 @@
[target.'cfg(feature = "cargo-clippy")']
rustflags = [
# Lints to enforce in CI
"-Dclippy::checked_conversions",
"-Dclippy::dbg_macro",
"-Dclippy::explicit_into_iter_loop",
"-Dclippy::explicit_iter_loop",
"-Dclippy::filter_map_next",
"-Dclippy::flat_map_option",
"-Dclippy::let_unit_value",
"-Dclippy::manual_assert",
"-Dclippy::manual_ok_or",
"-Dclippy::todo",
"-Dclippy::unnecessary_wraps",
"-Dclippy::useless_transmute",
"-Dclippy::used_underscore_binding",
"-Delided_lifetimes_in_paths",
"-Dunused_lifetimes",
"-Drust_2021_prelude_collisions"
]

View File

@ -7,5 +7,5 @@ contact_links:
url: https://github.com/PyO3/pyo3/discussions
about: For troubleshooting help, see the Discussions
- name: 👋 Chat
url: https://gitter.im/PyO3/Lobby
about: Engage with PyO3's users and developers on Gitter
url: https://discord.gg/33kcChzH7f
about: Engage with PyO3's users and developers on Discord

View File

@ -8,6 +8,6 @@ Please consider adding the following to your pull request:
- 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. To run its tests
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.

43
.github/workflows/benches.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: benches
on:
push:
branches:
- "main"
pull_request:
# `workflow_dispatch` allows CodSpeed to trigger backtest
# performance analysis in order to generate initial data.
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}-benches
cancel-in-progress: true
jobs:
benchmarks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: dtolnay/rust-toolchain@stable
with:
components: rust-src
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
.
pyo3-benches
continue-on-error: true
- name: Install cargo-codspeed
run: cargo install cargo-codspeed
- name: Install nox
run: pip install nox
- name: Run the benchmarks
uses: CodSpeedHQ/action@v2
with:
run: nox -s codspeed
token: ${{ secrets.CODSPEED_TOKEN }}

View File

@ -16,25 +16,24 @@ on:
rust-target:
required: true
type: string
msrv:
required: false
type: string
extra-features:
MSRV:
required: true
type: string
jobs:
build:
continue-on-error: ${{ endsWith(inputs.python-version, '-dev') || contains(fromJSON('["3.7", "pypy3.7"]'), inputs.python-version) }}
continue-on-error: ${{ endsWith(inputs.python-version, '-dev') || contains(fromJSON('["3.7", "pypy3.7"]'), inputs.python-version) || inputs.rust == 'beta' || inputs.rust == 'nightly' }}
runs-on: ${{ inputs.os }}
if: ${{ !(startsWith(inputs.python-version, 'graalpy') && startsWith(inputs.os, 'windows')) }}
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
architecture: ${{ inputs.python-architecture }}
check-latest: ${{ startsWith(inputs.python-version, 'pypy') }} # PyPy can have FFI changes within Python versions, which creates pain in CI
- name: Install nox
run: python -m pip install --upgrade pip && pip install nox
@ -49,25 +48,29 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
key: cargo-${{ inputs.python-architecture }}-${{ inputs.os }}-${{ inputs.msrv }}
continue-on-error: true
save-if: ${{ github.event_name != 'merge_group' }}
- if: inputs.os == 'ubuntu-latest'
name: Prepare LD_LIBRARY_PATH (Ubuntu only)
run: echo LD_LIBRARY_PATH=${pythonLocation}/lib >> $GITHUB_ENV
- if: inputs.msrv == 'MSRV'
name: Prepare minimal package versions (MSRV only)
run: nox -s set-minimal-package-versions
- if: inputs.rust == inputs.MSRV
name: Prepare MSRV package versions
run: nox -s set-msrv-package-versions
- if: inputs.rust == 'nightly' || inputs.msrv == 'MSRV'
- if: inputs.rust != 'stable'
name: Ignore changed error messages when using trybuild
run: echo "TRYBUILD=overwrite" >> "$GITHUB_ENV"
- if: inputs.rust == 'nightly'
name: Prepare to test on nightly rust
run: echo "MAYBE_NIGHTLY=nightly" >> "$GITHUB_ENV"
- name: Build docs
run: cargo doc --no-deps --no-default-features --features "full ${{ inputs.extra-features }}"
run: nox -s docs
- name: Build (no features)
if: ${{ !startsWith(inputs.python-version, 'graalpy') }}
run: cargo build --lib --tests --no-default-features
# --no-default-features when used with `cargo build/test -p` doesn't seem to work!
@ -77,7 +80,7 @@ jobs:
cargo build --no-default-features
# Run tests (except on PyPy, because no embedding API).
- if: ${{ !startsWith(inputs.python-version, 'pypy') }}
- if: ${{ !startsWith(inputs.python-version, 'pypy') && !startsWith(inputs.python-version, 'graalpy') }}
name: Test (no features)
run: cargo test --no-default-features --lib --tests
@ -88,26 +91,32 @@ jobs:
cargo test --no-default-features
- name: Build (all additive features)
run: cargo build --lib --tests --no-default-features --features "full ${{ inputs.extra-features }}"
if: ${{ !startsWith(inputs.python-version, 'graalpy') }}
run: cargo build --lib --tests --no-default-features --features "multiple-pymethods full $MAYBE_NIGHTLY"
- if: ${{ startsWith(inputs.python-version, 'pypy') }}
name: Build PyPy (abi3-py37)
run: cargo build --lib --tests --no-default-features --features "abi3-py37 full ${{ inputs.extra-features }}"
run: cargo build --lib --tests --no-default-features --features "multiple-pymethods abi3-py37 full $MAYBE_NIGHTLY"
# Run tests (except on PyPy, because no embedding API).
- if: ${{ !startsWith(inputs.python-version, 'pypy') }}
- if: ${{ !startsWith(inputs.python-version, 'pypy') && !startsWith(inputs.python-version, 'graalpy') }}
name: Test
run: cargo test --no-default-features --features "full ${{ inputs.extra-features }}"
run: cargo test --no-default-features --features "full $MAYBE_NIGHTLY"
# Repeat, with multiple-pymethods feature enabled (it's not entirely additive)
- if: ${{ !startsWith(inputs.python-version, 'pypy') && !startsWith(inputs.python-version, 'graalpy') }}
name: Test
run: cargo test --no-default-features --features "multiple-pymethods full $MAYBE_NIGHTLY"
# Run tests again, but in abi3 mode
- if: ${{ !startsWith(inputs.python-version, 'pypy') }}
- if: ${{ !startsWith(inputs.python-version, 'pypy') && !startsWith(inputs.python-version, 'graalpy') }}
name: Test (abi3)
run: cargo test --no-default-features --features "abi3 full ${{ inputs.extra-features }}"
run: cargo test --no-default-features --features "multiple-pymethods abi3 full $MAYBE_NIGHTLY"
# Run tests again, for abi3-py37 (the minimal Python version)
- if: ${{ (!startsWith(inputs.python-version, 'pypy')) && (inputs.python-version != '3.7') }}
- if: ${{ (!startsWith(inputs.python-version, 'pypy') && !startsWith(inputs.python-version, 'graalpy')) && (inputs.python-version != '3.7') }}
name: Test (abi3-py37)
run: cargo test --no-default-features --features "abi3-py37 full ${{ inputs.extra-features }}"
run: cargo test --no-default-features --features "multiple-pymethods abi3-py37 full $MAYBE_NIGHTLY"
- name: Test proc-macro code
run: cargo test --manifest-path=pyo3-macros-backend/Cargo.toml
@ -121,9 +130,9 @@ jobs:
env:
CARGO_TARGET_DIR: ${{ github.workspace }}/target
- uses: dorny/paths-filter@v2
- uses: dorny/paths-filter@v3
# pypy 3.7 and 3.8 are not PEP 3123 compliant so fail checks here
if: ${{ inputs.rust == 'stable' && inputs.python-version != 'pypy3.7' && inputs.python-version != 'pypy3.8' }}
if: ${{ inputs.rust == 'stable' && inputs.python-version != 'pypy3.7' && inputs.python-version != 'pypy3.8' && !startsWith(inputs.python-version, 'graalpy') }}
id: ffi-changes
with:
base: ${{ github.event.pull_request.base.ref || github.event.merge_group.base_ref }}
@ -138,63 +147,9 @@ jobs:
- name: Run pyo3-ffi-check
# pypy 3.7 and 3.8 are not PEP 3123 compliant so fail checks here, nor
# is pypy 3.9 on windows
if: ${{ endsWith(inputs.python-version, '-dev') || (steps.ffi-changes.outputs.changed == 'true' && inputs.rust == 'stable' && inputs.python-version != 'pypy3.7' && inputs.python-version != 'pypy3.8' && !(inputs.python-version == 'pypy3.9' && contains(inputs.os, 'windows'))) }}
if: ${{ endsWith(inputs.python-version, '-dev') || (steps.ffi-changes.outputs.changed == 'true' && inputs.rust == 'stable' && inputs.python-version != 'pypy3.7' && inputs.python-version != 'pypy3.8' && !startsWith(inputs.python-version, 'graalpy') && !(inputs.python-version == 'pypy3.9' && contains(inputs.os, 'windows'))) }}
run: nox -s ffi-check
- name: Test cross compilation
if: ${{ inputs.os == 'ubuntu-latest' && inputs.python-version == '3.9' }}
uses: PyO3/maturin-action@v1
env:
PYO3_CROSS_LIB_DIR: /opt/python/cp39-cp39/lib
with:
target: aarch64-unknown-linux-gnu
manylinux: auto
args: --release -i python3.9 -m examples/maturin-starter/Cargo.toml
- run: sudo rm -rf examples/maturin-starter/target
if: ${{ inputs.os == 'ubuntu-latest' && inputs.python-version == '3.9' }}
- name: Test cross compile to same architecture
if: ${{ inputs.os == 'ubuntu-latest' && inputs.python-version == '3.9' }}
uses: PyO3/maturin-action@v1
env:
PYO3_CROSS_LIB_DIR: /opt/python/cp39-cp39/lib
with:
target: x86_64-unknown-linux-gnu
manylinux: auto
args: --release -i python3.9 -m examples/maturin-starter/Cargo.toml
- name: Test cross compilation
if: ${{ inputs.os == 'macos-latest' && inputs.python-version == '3.9' }}
uses: PyO3/maturin-action@v1
with:
target: aarch64-apple-darwin
args: --release -i python3.9 -m examples/maturin-starter/Cargo.toml
- name: Test cross compile to Windows
if: ${{ inputs.os == 'ubuntu-latest' && inputs.python-version == '3.8' }}
env:
XWIN_ARCH: x86_64
run: |
set -ex
sudo apt-get install -y mingw-w64 llvm
rustup target add x86_64-pc-windows-gnu x86_64-pc-windows-msvc
pip install cargo-xwin
# abi3
cargo build --manifest-path examples/maturin-starter/Cargo.toml --features abi3 --target x86_64-pc-windows-gnu
cargo xwin build --manifest-path examples/maturin-starter/Cargo.toml --features abi3 --target x86_64-pc-windows-msvc
# non-abi3
export PYO3_CROSS_PYTHON_VERSION=3.9
cargo build --manifest-path examples/maturin-starter/Cargo.toml --features generate-import-lib --target x86_64-pc-windows-gnu
cargo xwin build --manifest-path examples/maturin-starter/Cargo.toml --features generate-import-lib --target x86_64-pc-windows-msvc
- name: Test cross compile to Windows with maturin
if: ${{ inputs.os == 'ubuntu-latest' && inputs.python-version == '3.8' }}
uses: PyO3/maturin-action@v1
with:
target: x86_64-pc-windows-gnu
args: -i python3.8 -m examples/maturin-starter/Cargo.toml --features abi3
env:
CARGO_TERM_VERBOSE: true
CARGO_BUILD_TARGET: ${{ inputs.rust-target }}

31
.github/workflows/cache-cleanup.yml vendored Normal file
View File

@ -0,0 +1,31 @@
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

View File

@ -10,6 +10,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
- run: python -m pip install --upgrade pip && pip install nox
- run: nox -s check-changelog

View File

@ -18,42 +18,62 @@ env:
jobs:
fmt:
if: github.ref != 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
- run: python -m pip install --upgrade pip && pip install nox
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Check python formatting (black)
run: nox -s fmt-py
- name: Check python formatting and lints (ruff)
run: nox -s ruff
- name: Check rust formatting (rustfmt)
run: nox -s fmt-rust
run: nox -s rustfmt
check-msrv:
resolve:
runs-on: ubuntu-latest
outputs:
MSRV: ${{ steps.resolve-msrv.outputs.MSRV }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: resolve MSRV
id: resolve-msrv
run:
echo MSRV=`python -c 'import tomllib; print(tomllib.load(open("Cargo.toml", "rb"))["package"]["rust-version"])'` >> $GITHUB_OUTPUT
semver-checks:
if: github.ref != 'refs/heads/main'
needs: [fmt]
runs-on: ubuntu-latest
if: github.ref != 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: obi1kenobi/cargo-semver-checks-action@v2
check-msrv:
needs: [fmt, resolve]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.56.0
toolchain: ${{ needs.resolve.outputs.MSRV }}
targets: x86_64-unknown-linux-gnu
components: rust-src
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
architecture: "x64"
- uses: Swatinem/rust-cache@v2
with:
key: check-msrv-1.56.0
continue-on-error: true
save-if: ${{ github.event_name != 'merge_group' }}
- run: python -m pip install --upgrade pip && pip install nox
- name: Prepare minimal package versions
run: nox -s set-minimal-package-versions
- run: nox -s check-all
# 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
@ -61,7 +81,6 @@ jobs:
clippy:
needs: [fmt]
runs-on: ${{ matrix.platform.os }}
if: github.ref != 'refs/heads/main'
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') }}
@ -69,9 +88,9 @@ jobs:
rust: [stable]
platform: [
{
os: "macos-latest",
python-architecture: "x64",
rust-target: "x86_64-apple-darwin",
os: "macos-14", # first available arm macos runner
python-architecture: "arm64",
rust-target: "aarch64-apple-darwin",
},
{
os: "ubuntu-latest",
@ -104,7 +123,17 @@ jobs:
rust-target: "i686-pc-windows-msvc",
},
]
include:
# Run beta clippy as a way to detect any incoming lints which may affect downstream users
- rust: beta
platform:
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}
name: clippy/${{ matrix.platform.rust-target }}/${{ matrix.rust }}
continue-on-error: ${{ matrix.rust != 'stable' }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
@ -112,13 +141,12 @@ jobs:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.platform.rust-target }}
components: clippy,rust-src
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
architecture: ${{ matrix.platform.python-architecture }}
- uses: Swatinem/rust-cache@v2
with:
key: clippy-${{ matrix.platform.rust-target }}-${{ matrix.platform.os }}-${{ matrix.rust }}
continue-on-error: true
save-if: ${{ github.event_name != 'merge_group' }}
- run: python -m pip install --upgrade pip && pip install nox
- run: nox -s clippy-all
env:
@ -127,7 +155,7 @@ jobs:
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]
needs: [fmt, resolve]
uses: ./.github/workflows/build.yml
with:
os: ${{ matrix.platform.os }}
@ -135,20 +163,23 @@ jobs:
python-architecture: ${{ matrix.platform.python-architecture }}
rust: ${{ matrix.rust }}
rust-target: ${{ matrix.platform.rust-target }}
msrv: ${{ matrix.msrv }}
extra-features: ${{ matrix.platform.extra-features }}
MSRV: ${{ needs.resolve.outputs.MSRV }}
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:
extra-features: ["multiple-pymethods"]
rust: [stable]
python-version: ["3.11"]
python-version: ["3.12"]
platform:
[
{
os: "macos-latest",
os: "macos-14", # first available arm macos runner
python-architecture: "arm64",
rust-target: "aarch64-apple-darwin",
},
{
os: "macos-13", # last available x86_64 macos runner
python-architecture: "x64",
rust-target: "x86_64-apple-darwin",
},
@ -166,12 +197,23 @@ jobs:
os: "windows-latest",
python-architecture: "x86",
rust-target: "i686-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.12"
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' && github.ref != 'refs/heads/main') }}
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]
needs: [fmt, resolve]
uses: ./.github/workflows/build.yml
with:
os: ${{ matrix.platform.os }}
@ -179,14 +221,12 @@ jobs:
python-architecture: ${{ matrix.platform.python-architecture }}
rust: ${{ matrix.rust }}
rust-target: ${{ matrix.platform.rust-target }}
msrv: ${{ matrix.msrv }}
extra-features: ${{ matrix.platform.extra-features }}
MSRV: ${{ needs.resolve.outputs.MSRV }}
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:
extra-features: ["multiple-pymethods"] # Because MSRV doesn't support this
rust: [stable]
python-version: [
"3.7",
@ -195,15 +235,22 @@ jobs:
"3.10",
"3.11",
"3.12",
"3.13-dev",
"pypy3.7",
"pypy3.8",
"pypy3.9",
"pypy3.10",
"graalpy24.0",
]
platform:
[
# for the full matrix, use x86_64 macos runners because not all Python versions
# PyO3 supports are available for arm on GitHub Actions. (Availability starts
# around Python 3.10, can switch the full matrix to arm once earlier versions
# are dropped.)
# NB: if this switches to arm, switch the arm job below in the `include` to x86_64
{
os: "macos-latest",
os: "macos-13",
python-architecture: "x64",
rust-target: "x86_64-apple-darwin",
},
@ -220,50 +267,66 @@ jobs:
]
include:
# Test minimal supported Rust version
- rust: 1.56.0
python-version: "3.11"
- rust: ${{ needs.resolve.outputs.MSRV }}
python-version: "3.12"
platform:
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}
msrv: "MSRV"
extra-features: ""
# Test the `nightly` feature
- rust: nightly
python-version: "3.11"
python-version: "3.12"
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.12"
platform:
{
os: "ubuntu-latest",
python-architecture: "x64",
rust-target: "x86_64-unknown-linux-gnu",
}
extra-features: "nightly multiple-pymethods"
# Test 32-bit Windows only with the latest Python version
- rust: stable
python-version: "3.11"
python-version: "3.12"
platform:
{
os: "windows-latest",
python-architecture: "x86",
rust-target: "i686-pc-windows-msvc",
}
extra-features: "multiple-pymethods"
# test arm macos runner with the latest Python version
# NB: if the full matrix switchess to arm, switch to x86_64 here
- rust: stable
python-version: "3.12"
platform:
{
os: "macos-14",
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' && github.ref != 'refs/heads/main') }}
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@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
- uses: Swatinem/rust-cache@v2
with:
key: cargo-valgrind
continue-on-error: true
save-if: ${{ github.event_name != 'merge_group' }}
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@valgrind
- run: python -m pip install --upgrade pip && pip install nox
@ -274,51 +337,64 @@ jobs:
TRYBUILD: overwrite
careful:
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || (github.event_name != 'pull_request' && github.ref != 'refs/heads/main') }}
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@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
- uses: Swatinem/rust-cache@v2
with:
key: cargo-careful
continue-on-error: true
save-if: ${{ github.event_name != 'merge_group' }}
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- run: cargo install cargo-careful
- uses: taiki-e/install-action@cargo-careful
- run: python -m pip install --upgrade pip && pip install nox
- 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@v4
- uses: actions/setup-python@v5
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.event_name != 'merge_group' }}
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- run: cargo rustdoc --lib --no-default-features --features full -Zunstable-options --config "build.rustdocflags=[\"--cfg\", \"docsrs\"]"
coverage:
needs: [fmt]
name: coverage-${{ matrix.os }}
name: coverage ${{ matrix.os }}
strategy:
matrix:
os: ["windows", "macos", "ubuntu"]
runs-on: ${{ matrix.os }}-latest
os: ["windows-latest", "macos-14", "ubuntu-latest"] # first available arm macos runner
runs-on: ${{ matrix.os }}
steps:
- if: ${{ github.event_name == 'pull_request' && matrix.os != 'ubuntu' }}
- if: ${{ github.event_name == 'pull_request' && matrix.os != 'ubuntu-latest' }}
id: should-skip
shell: bash
run: echo 'skip=true' >> $GITHUB_OUTPUT
- uses: actions/checkout@v4
if: steps.should-skip.outputs.skip != 'true'
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
if: steps.should-skip.outputs.skip != 'true'
- uses: Swatinem/rust-cache@v2
if: steps.should-skip.outputs.skip != 'true'
with:
key: coverage-cargo-${{ matrix.os }}
continue-on-error: true
save-if: ${{ github.event_name != 'merge_group' }}
- uses: dtolnay/rust-toolchain@stable
if: steps.should-skip.outputs.skip != 'true'
with:
components: llvm-tools-preview
components: llvm-tools-preview,rust-src
- name: Install cargo-llvm-cov
if: steps.should-skip.outputs.skip != 'true'
uses: taiki-e/install-action@cargo-llvm-cov
@ -326,29 +402,35 @@ jobs:
if: steps.should-skip.outputs.skip != 'true'
- run: nox -s coverage
if: steps.should-skip.outputs.skip != 'true'
- uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v4
if: steps.should-skip.outputs.skip != 'true'
with:
file: coverage.json
name: ${{ matrix.os }}
token: ${{ secrets.CODECOV_TOKEN }}
emscripten:
name: emscripten
if: ${{ github.event_name != 'pull_request' && github.ref != 'refs/heads/main' }}
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@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
# TODO bump emscripten builds to test on 3.12
python-version: 3.11
id: setup-python
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-emscripten
- uses: actions/setup-node@v3
components: rust-src
- uses: actions/setup-node@v4
with:
node-version: 14
- run: python -m pip install --upgrade pip && pip install nox
- uses: actions/cache@v3
- uses: actions/cache@v4
id: cache
with:
path: |
@ -356,7 +438,7 @@ jobs:
key: ${{ hashFiles('emscripten/*') }} - ${{ hashFiles('noxfile.py') }} - ${{ steps.setup-python.outputs.python-path }}
- uses: Swatinem/rust-cache@v2
with:
key: cargo-emscripten-wasm32
save-if: ${{ github.event_name != 'merge_group' }}
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
run: nox -s build-emscripten
@ -364,13 +446,14 @@ jobs:
run: nox -s test-emscripten
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@v4
- uses: Swatinem/rust-cache@v2
with:
key: cargo-test-debug
continue-on-error: true
save-if: ${{ github.event_name != 'merge_group' }}
- uses: dtolnay/rust-toolchain@stable
with:
components: rust-src
@ -406,6 +489,131 @@ jobs:
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@v4
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.event_name != 'merge_group' }}
- uses: dtolnay/rust-toolchain@stable
- run: python3 -m pip install --upgrade pip && pip install nox
- 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@v4
- uses: actions/setup-python@v5
- uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.event_name != 'merge_group' }}
- 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
- 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.12"
manylinux: auto
# ubuntu x86_64 -> aarch64
- os: "ubuntu-latest"
target: "aarch64-unknown-linux-gnu"
flags: "-i python3.12"
manylinux: auto
# ubuntu x86_64 -> windows x86_64
- os: "ubuntu-latest"
target: "x86_64-pc-windows-gnu"
flags: "-i python3.12 --features abi3 --features generate-import-lib"
manylinux: off
# macos x86_64 -> aarch64
- os: "macos-13" # last x86_64 macos runners
target: "aarch64-apple-darwin"
# macos aarch64 -> x86_64
- os: "macos-14" # aarch64 macos runners
target: "x86_64-apple-darwin"
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
with:
workspaces:
examples/maturin-starter
save-if: ${{ github.event_name != 'merge_group' }}
key: ${{ matrix.target }}
- name: Setup cross-compiler
if: ${{ matrix.target == 'x86_64-pc-windows-gnu' }}
run: sudo apt-get install -y mingw-w64 llvm
- uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: ${{ matrix.manylinux }}
args: --release -m examples/maturin-starter/Cargo.toml ${{ 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@v4
- uses: actions/setup-python@v5
- uses: Swatinem/rust-cache@v2
with:
workspaces:
examples/maturin-starter
save-if: ${{ github.event_name != 'merge_group' }}
- uses: actions/cache/restore@v4
with:
# https://github.com/PyO3/maturin/discussions/1953
path: ~/.cache/cargo-xwin
key: cargo-xwin-cache
- name: Test cross compile to Windows
env:
XWIN_ARCH: x86_64
run: |
set -ex
sudo apt-get install -y mingw-w64 llvm
rustup target add x86_64-pc-windows-gnu x86_64-pc-windows-msvc
pip install cargo-xwin
# abi3
cargo build --manifest-path examples/maturin-starter/Cargo.toml --features abi3 --target x86_64-pc-windows-gnu
cargo xwin build --manifest-path examples/maturin-starter/Cargo.toml --features abi3 --target x86_64-pc-windows-msvc
# non-abi3
export PYO3_CROSS_PYTHON_VERSION=3.12
cargo build --manifest-path examples/maturin-starter/Cargo.toml --features generate-import-lib --target x86_64-pc-windows-gnu
cargo xwin build --manifest-path examples/maturin-starter/Cargo.toml --features generate-import-lib --target x86_64-pc-windows-msvc
- if: ${{ github.ref == 'refs/heads/main' }}
uses: actions/cache/save@v4
with:
path: ~/.cache/cargo-xwin
key: cargo-xwin-cache
conclusion:
needs:
- fmt
@ -415,8 +623,14 @@ jobs:
- build-full
- valgrind
- careful
- docsrs
- coverage
- emscripten
- test-debug
- test-version-limits
- check-feature-powerset
- test-cross-compilation
- test-cross-compilation-windows
if: always()
runs-on: ubuntu-latest
steps:

View File

@ -22,13 +22,13 @@ jobs:
tag_name: ${{ steps.prepare_tag.outputs.tag_name }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: dtolnay/rust-toolchain@nightly
- name: Setup mdBook
uses: peaceiris/actions-mdbook@v1
uses: taiki-e/install-action@v2
with:
mdbook-version: "0.4.19"
tool: mdbook,lychee
- name: Prepare tag
id: prepare_tag
@ -36,104 +36,19 @@ jobs:
TAG_NAME="${GITHUB_REF##*/}"
echo "::set-output name=tag_name::${TAG_NAME}"
# This builds the book in target/guide.
# This builds the book in target/guide/.
- name: Build the guide
run: |
python -m pip install --upgrade pip && pip install nox
nox -s build-guide
nox -s check-guide
env:
PYO3_VERSION_TAG: ${{ steps.prepare_tag.outputs.tag_name }}
- name: Deploy docs and the guide
if: ${{ github.event_name == 'release' }}
uses: peaceiris/actions-gh-pages@v3
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 }}"
cargo-benchmark:
if: ${{ github.ref_name == 'main' }}
name: Cargo benchmark
needs: guide-build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: dtolnay/rust-toolchain@stable
- uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-${{ runner.os }}-bench-${{ hashFiles('**/Cargo.toml') }}
continue-on-error: true
- name: Run benchmarks
run: |
python -m pip install --upgrade pip && pip install nox
for bench in pyo3-benches/benches/*.rs; do
bench_name=$(basename "$bench" .rs)
nox -s bench -- --bench "$bench_name" -- --output-format bencher | tee -a output.txt
done
# Download previous benchmark result from cache (if exists)
- name: Download previous benchmark data
uses: actions/cache@v3
with:
path: ./cache
key: ${{ runner.os }}-benchmark
# Run `github-action-benchmark` action
- name: Store benchmark result
uses: benchmark-action/github-action-benchmark@v1
with:
name: pyo3-bench
# What benchmark tool the output.txt came from
tool: "cargo"
# Where the output from the benchmark tool is stored
output-file-path: output.txt
# GitHub API token to make a commit comment
github-token: ${{ secrets.GITHUB_TOKEN }}
auto-push: ${{ github.event_name != 'pull_request' }}
pytest-benchmark:
if: ${{ github.ref_name == 'main' }}
name: Pytest benchmark
needs: cargo-benchmark
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: dtolnay/rust-toolchain@stable
- uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-${{ runner.os }}-pytest-bench-${{ hashFiles('**/Cargo.toml') }}
continue-on-error: true
- name: Download previous benchmark data
uses: actions/cache@v3
with:
path: ./cache
key: ${{ runner.os }}-pytest-benchmark
- name: Run benchmarks
run: |
python -m pip install --upgrade pip && pip install nox
nox -f pytests/noxfile.py -s bench -- --benchmark-json $(pwd)/output.json
- name: Store benchmark result
uses: benchmark-action/github-action-benchmark@v1
with:
name: pytest-bench
tool: "pytest"
output-file-path: output.json
github-token: ${{ secrets.GITHUB_TOKEN }}
auto-push: ${{ github.event_name != 'pull_request' }}

4
.gitignore vendored
View File

@ -14,6 +14,8 @@ dist/
.eggs/
venv*
guide/book/
guide/src/LICENSE-APACHE
guide/src/LICENSE-MIT
*.so
*.out
*.egg-info
@ -22,4 +24,6 @@ pip-wheel-metadata
valgrind-python.supp
*.pyd
lcov.info
coverage.json
netlify_build/
.nox/

View File

@ -2,6 +2,7 @@
set -uex
rustup update nightly
rustup default nightly
PYO3_VERSION=$(cargo search pyo3 --limit 1 | head -1 | tr -s ' ' | cut -d ' ' -f 3 | tr -d '"')
@ -48,6 +49,15 @@ 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
@ -71,9 +81,17 @@ if [ "${INSTALLED_MDBOOK_VERSION}" != "mdbook v${MDBOOK_VERSION}" ]; then
cargo install mdbook@${MDBOOK_VERSION} --force
fi
# Install latest mdbook-linkcheck. Netlify will cache the cargo bin dir, so this will
# only build mdbook-linkcheck if needed.
MDBOOK_LINKCHECK_VERSION=$(cargo search mdbook-linkcheck --limit 1 | head -1 | tr -s ' ' | cut -d ' ' -f 3 | tr -d '"')
INSTALLED_MDBOOK_LINKCHECK_VERSION=$(mdbook-linkcheck --version || echo "none")
if [ "${INSTALLED_MDBOOK_LINKCHECK_VERSION}" != "mdbook v${MDBOOK_LINKCHECK_VERSION}" ]; then
cargo install mdbook-linkcheck@${MDBOOK_LINKCHECK_VERSION} --force
fi
pip install nox
nox -s build-guide
mv target/guide netlify_build/main/
mv target/guide/ netlify_build/main/
## Build public docs

View File

@ -1 +1 @@
3.11
3.12

View File

@ -24,17 +24,18 @@ To summarize, there are six main parts to the PyO3 codebase.
- [`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. [Protocol methods like `__getitem__`.](#4-protocol-methods)
- [`src/class`]
5. [Procedural macros to simplify usage for users.](#5-procedural-macros-to-simplify-usage-for-users)
- [`src/derive_utils.rs`], [`pyo3-macros`] and [`pyo3-macros-backend`]
6. [`build.rs` and `pyo3-build-config`](#6-buildrs-and-pyo3-build-config)
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 [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/v3.9.2/Include).
@ -44,12 +45,12 @@ the file contents upstream in CPython.
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_37)]`, and `#[cfg(PyPy)]`.
`#[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_37` means that the API is available from Python >= 3.7.
There are also `Py_38`, `Py_39`, and so on.
[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).
@ -58,6 +59,7 @@ Those flags are set in [`build.rs`](#6-buildrs-and-pyo3-build-config).
[`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
@ -65,38 +67,16 @@ Currently, `PyAny` is a straightforward wrapper of `ffi::PyObject`, defined as:
pub struct PyAny(UnsafeCell<ffi::PyObject>);
```
All built-in types are defined as a C struct.
For example, `dict` is defined as:
```c
typedef struct {
/* Base object */
PyObject ob_base;
/* Number of items in the dictionary */
Py_ssize_t ma_used;
/* Dictionary version */
uint64_t ma_version_tag;
PyDictKeysObject *ma_keys;
PyObject **ma_values;
} PyDictObject;
```
However, we cannot access such a specific data structure with `#[cfg(Py_LIMITED_API)]` set.
Thus, all builtin objects are implemented as opaque types by wrapping `PyAny`, e.g.,:
Concrete Python objects are implemented by wrapping `PyAny`, e.g.,:
```rust
#[repr(transparent)]
pub struct PyDict(PyAny);
```
Note that `PyAny` is not a pointer, and it is usually used as a pointer to the object in the
Python heap, as `&PyAny`.
This design choice can be changed
(see the discussion in [#1056](https://github.com/PyO3/pyo3/issues/1056)).
These types are not intended to be accessed directly, and instead are used through the `Py<T>` and `Bound<T>` smart pointers.
Since we need lots of boilerplate for implementing common traits for these types
(e.g., `AsPyPointer`, `AsRef<PyAny>`, and `Debug`), we have some macros in
[`src/types/mod.rs`].
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
@ -104,26 +84,16 @@ Since we need lots of boilerplate for implementing common traits for these types
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 must have the following two fields
at the beginning.
```rust
#[repr(C)]
pub struct PyObject {
pub ob_refcnt: usize,
pub ob_type: *mut PyTypeObject,
...
}
```
Thanks to this guarantee, casting `*mut A` to `*mut PyObject` is valid if `A` is a Python object.
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 `PyCell<T>` in [`src/pycell.rs`] which is roughly:
```rust
#[repr(C)]
pub struct PyCell<T: PyClass> {
object: crate::ffi::PyObject,
ob_base: crate::ffi::PyObject,
inner: T,
}
```
@ -142,7 +112,7 @@ 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.
## 4. Protocol methods
### 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
@ -151,15 +121,15 @@ 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.
## 5. Procedural macros to simplify usage for users.
## 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/derive_utils.rs`] contains some utilities used in code generated by these proc-macros,
[`src/impl_`] contains `#[doc(hidden)]` functionality used in code generated by these proc-macros,
such as parsing function arguments.
## 6. `build.rs` and `pyo3-build-config`
## 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
@ -218,7 +188,7 @@ Some of the functionality of `pyo3-build-config`:
<!-- Files -->
[`src/derive_utils.rs`]: https://github.com/PyO3/pyo3/blob/main/src/derive_utils.rs
[`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

View File

@ -10,6 +10,231 @@ To see unreleased changes, please see the [CHANGELOG on the main branch guide](h
<!-- towncrier release notes start -->
## [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<bool>` 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<PyObject>` 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<Bound<'py, T>>` for `PyClassInitializer<T>`. [#4214](https://github.com/PyO3/pyo3/pull/4214)
- Add `as_super` methods to `PyRef` and `PyRefMut` for accesing the base class by reference. [#4219](https://github.com/PyO3/pyo3/pull/4219)
- Implement `PartialEq<str>` 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<T>`, `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<Bound<'py, PyList>>` (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 comparision 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<PyObject>` and `ToPyObject` are implemented on `std::duration::Duration` [#3670](https://github.com/PyO3/pyo3/pull/3670)
- Add `PyString::to_cow`. Add `Py<PyString>::to_str`, `Py<PyString>::to_cow`, and `Py<PyString>::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<T>` and `Borrowed<T>` 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<str>`. [#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<PyAny>` 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<Tz>` 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::<i64>` (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<T>` 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<str>`, `&[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<L, R>` 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<PyType>` as their first argument. [#3587](https://github.com/PyO3/pyo3/pull/3587)
- `#[pyfunction(pass_module)]` can now also receive `Py<PyModule>` 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>` `self` receiver. [#3564](https://github.com/PyO3/pyo3/pull/3564)
## [0.20.0] - 2023-10-11
### Packaging
@ -571,7 +796,7 @@ To see unreleased changes, please see the [CHANGELOG on the main branch guide](h
- 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 are now unsafe functions instead of statics. [#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`
@ -1599,7 +1824,16 @@ Yanked
- Initial release
[Unreleased]: https://github.com/pyo3/pyo3/compare/v0.20.0...HEAD
[Unreleased]: https://github.com/pyo3/pyo3/compare/v0.22.1...HEAD
[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
@ -1643,7 +1877,7 @@ Yanked
[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.4]: https://github.com/pyo3/pyo3/compare/v0.8.4...v0.8.5
[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
@ -1652,7 +1886,8 @@ Yanked
[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.0...v0.5.2
[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

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3"
version = "0.20.0"
version = "0.23.0-dev"
description = "Bindings to Python interpreter"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
readme = "README.md"
@ -12,19 +12,19 @@ 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 = "1.56"
rust-version = "1.63"
[dependencies]
cfg-if = "1.0"
libc = "0.2.62"
parking_lot = ">= 0.11, < 0.13"
memoffset = "0.9"
once_cell = "1.13"
# ffi bindings to the python interpreter, split into a separate crate so they can be used independently
pyo3-ffi = { path = "pyo3-ffi", version = "=0.20.0" }
pyo3-ffi = { path = "pyo3-ffi", version = "=0.23.0-dev" }
# support crates for macros feature
pyo3-macros = { path = "pyo3-macros", version = "=0.20.0", optional = true }
pyo3-macros = { path = "pyo3-macros", version = "=0.23.0-dev", optional = true }
indoc = { version = "2.0.1", optional = true }
unindent = { version = "0.2.1", optional = true }
@ -32,35 +32,45 @@ unindent = { version = "0.2.1", optional = true }
inventory = { version = "0.3.0", optional = true }
# crate integrations that can be added using the eponymous features
anyhow = { version = "1.0", optional = true }
chrono = { version = "0.4", default-features = false, optional = true }
anyhow = { version = "1.0.1", optional = true }
chrono = { version = "0.4.25", default-features = false, optional = true }
chrono-tz = { version = ">= 0.6, < 0.10", default-features = false, optional = true }
either = { version = "1.9", optional = true }
eyre = { version = ">= 0.4, < 0.7", optional = true }
hashbrown = { version = ">= 0.9, < 0.15", optional = true }
indexmap = { version = ">= 1.6, < 3", optional = true }
num-bigint = { version = "0.4", optional = true }
num-bigint = { version = "0.4.2", optional = true }
num-complex = { version = ">= 0.2, < 0.5", optional = true }
rust_decimal = { version = "1.0.0", default-features = false, optional = true }
num-rational = {version = "0.4.1", optional = true }
rust_decimal = { version = "1.15", default-features = false, optional = true }
serde = { version = "1.0", optional = true }
smallvec = { version = "1.0", optional = true }
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
portable-atomic = "1.0"
[dev-dependencies]
assert_approx_eq = "1.1.0"
chrono = { version = "0.4.25" }
chrono = "0.4.25"
chrono-tz = ">= 0.6, < 0.10"
# Required for "and $N others" normalization
trybuild = ">=1.0.70"
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.0.2"
rust_decimal = { version = "1.8.0", features = ["std"] }
widestring = "0.5.1"
rayon = "1.6.1"
futures = "0.3.28"
[build-dependencies]
pyo3-build-config = { path = "pyo3-build-config", version = "0.20.0", features = ["resolve-config"] }
pyo3-build-config = { path = "pyo3-build-config", version = "=0.23.0-dev", 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 = []
@ -77,14 +87,15 @@ multiple-pymethods = ["inventory", "pyo3-macros/multiple-pymethods"]
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", "pyo3-macros/abi3"]
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", "pyo3-build-config/abi3-py311", "pyo3-ffi/abi3-py311"]
abi3-py311 = ["abi3-py312", "pyo3-build-config/abi3-py311", "pyo3-ffi/abi3-py311"]
abi3-py312 = ["abi3", "pyo3-build-config/abi3-py312", "pyo3-ffi/abi3-py312"]
# Automatically generates `python3.dll` import libraries for Windows targets.
generate-import-lib = ["pyo3-ffi/generate-import-lib"]
@ -92,6 +103,12 @@ generate-import-lib = ["pyo3-ffi/generate-import-lib"]
# Changes `Python::with_gil` to automatically initialize the Python interpreter if needed.
auto-initialize = []
# Allows use of the deprecated "GIL Refs" APIs.
gil-refs = ["pyo3-macros/gil-refs"]
# Enables `Clone`ing references to Python objects `Py<T>` which panics if the GIL is not held.
py-clone = []
# Optimizes PyObject to Vec conversion and so on.
nightly = []
@ -99,17 +116,23 @@ nightly = []
# This is mostly intended for testing purposes - activating *all* of these isn't particularly useful.
full = [
"macros",
# "multiple-pymethods", # TODO re-add this when MSRV is greater than 1.62
# "multiple-pymethods", # Not supported by wasm
"anyhow",
"chrono",
"chrono-tz",
"either",
"experimental-async",
"experimental-inspect",
"eyre",
"hashbrown",
"indexmap",
"num-bigint",
"num-complex",
"hashbrown",
"serde",
"indexmap",
"eyre",
"anyhow",
"experimental-inspect",
"num-rational",
"py-clone",
"rust_decimal",
"serde",
"smallvec",
]
[workspace]
@ -124,5 +147,34 @@ members = [
[package.metadata.docs.rs]
no-default-features = true
features = ["macros", "num-bigint", "num-complex", "hashbrown", "serde", "multiple-pymethods", "indexmap", "eyre", "chrono", "rust_decimal"]
features = ["full", "gil-refs"]
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"
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"
[workspace.lints.rustdoc]
broken_intra_doc_links = "warn"
bare_urls = "warn"
[lints]
workspace = true

View File

@ -23,19 +23,15 @@ To work and develop PyO3, you need Python & Rust installed on your system.
* [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.
### Caveats
* When using pyenv on macOS, installing a Python version using `--enable-shared` is required to make it work. i.e `env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install 3.7.12`
### Help users identify bugs
The [PyO3 Gitter channel](https://gitter.im/PyO3/Lobby) 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.
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-implemeter) label.
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.
@ -48,7 +44,7 @@ There are some specific areas of focus where help is currently needed for the do
- 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!
You can build the docs (including all features) with
To build the docs (including all features), install [`nox`][nox] and then run
```shell
nox -s docs -- open
@ -70,6 +66,12 @@ First, install [`mdbook`][mdbook] and [`nox`][nox]. Then, run
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.
@ -86,19 +88,42 @@ Here are a few things to note when you are writing PRs.
### 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. In addition, all warnings in Rust code are disallowed (using `RUSTFLAGS="-D warnings"`).
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. 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 these tests yourself with
```nox```
and
```nox -l```
lists further commands you can run.
`nox`. Use `nox -l` to list the full set of subcommands you can run.
#### Linting Python code
`nox -s ruff`
#### Linting Rust code
`nox -s rustfmt`
#### Semver checks
`cargo semver-checks check-release`
#### Clippy
`nox -s clippy-all`
#### Tests
`cargo test --features full`
#### Check all conditional compilation
`nox -s check-feature-powerset`
#### UI Tests
PyO3 uses [`trybuild`](https://github.com/dtolnay/trybuild) to develop UI tests to capture error messages from the Rust compiler for some of the macro functionality.
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
```
### Documenting changes
@ -171,15 +196,20 @@ First, there are Rust-based benchmarks located in the `pyo3-benches` subdirector
nox -s bench
Second, there is a Python-based benchmark contained in the `pytests` subdirectory. You can read more about it [here](pytests).
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, generate a `lcov.info` file with
- 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
nox -s coverage
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.
@ -198,7 +228,7 @@ You can install an IDE plugin to view the coverage. For example, if you use VSCo
## 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 [Gitter](https://gitter.im/PyO3/Lobby) and we can discuss.
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:
@ -206,4 +236,5 @@ In the meanwhile, some of our maintainers have personal GitHub sponsorship pages
- [messense](https://github.com/sponsors/messense)
[mdbook]: https://rust-lang.github.io/mdBook/cli/index.html
[lychee]: https://github.com/lycheeverse/lychee
[nox]: https://github.com/theacodes/nox

View File

@ -1,11 +1,11 @@
# PyO3
[![actions status](https://img.shields.io/github/actions/workflow/status/PyO3/pyo3/ci.yml?branch=main&logo=github&style=)](https://github.com/PyO3/pyo3/actions)
[![benchmark](https://img.shields.io/badge/benchmark-✓-Green?logo=github)](https://pyo3.rs/dev/bench/)
[![benchmark](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/PyO3/pyo3)
[![codecov](https://img.shields.io/codecov/c/gh/PyO3/pyo3?logo=codecov)](https://codecov.io/gh/PyO3/pyo3)
[![crates.io](https://img.shields.io/crates/v/pyo3?logo=rust)](https://crates.io/crates/pyo3)
[![minimum rustc 1.56](https://img.shields.io/badge/rustc-1.56+-blue?logo=rust)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
[![dev chat](https://img.shields.io/gitter/room/PyO3/Lobby?logo=gitter)](https://gitter.im/PyO3/Lobby)
[![minimum rustc 1.63](https://img.shields.io/badge/rustc-1.63+-blue?logo=rust)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
[![discord server](https://img.shields.io/discord/1209263839632424990?logo=discord)](https://discord.gg/33kcChzH7f)
[![contributing notes](https://img.shields.io/badge/contribute-on%20github-Green?logo=github)](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.
@ -17,8 +17,8 @@
## Usage
PyO3 supports the following software versions:
- Python 3.7 and up (CPython and PyPy)
- Rust 1.56 and up
- Python 3.7 and up (CPython, PyPy, and GraalPy)
- Rust 1.63 and up
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.
@ -68,7 +68,7 @@ name = "string_sum"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.20.0", features = ["extension-module"] }
pyo3 = { version = "0.22.1", features = ["extension-module"] }
```
**`src/lib.rs`**
@ -86,7 +86,7 @@ fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
/// import the module.
#[pymodule]
fn string_sum(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn string_sum(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
Ok(())
}
@ -118,7 +118,7 @@ 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.
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
@ -137,7 +137,7 @@ Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like th
```toml
[dependencies.pyo3]
version = "0.20.0"
version = "0.22.1"
features = ["auto-initialize"]
```
@ -149,12 +149,12 @@ use pyo3::types::IntoPyDict;
fn main() -> PyResult<()> {
Python::with_gil(|py| {
let sys = py.import("sys")?;
let sys = py.import_bound("sys")?;
let version: String = sys.getattr("version")?.extract()?;
let locals = [("os", py.import("os")?)].into_py_dict(py);
let locals = [("os", py.import_bound("os")?)].into_py_dict_bound(py);
let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
let user: String = py.eval(code, None, Some(&locals))?.extract()?;
let user: String = py.eval_bound(code, None, Some(&locals))?.extract()?;
println!("Hello {}, I'm Python {}", user, version);
Ok(())
@ -162,7 +162,7 @@ fn main() -> PyResult<()> {
}
```
The guide has [a section](https://pyo3.rs/latest/python_from_rust.html) with lots of examples
The guide has [a section](https://pyo3.rs/latest/python-from-rust.html) with lots of examples
about this topic.
## Tools and libraries
@ -176,6 +176,7 @@ about this topic.
- [pythonize](https://github.com/davidhewitt/pythonize) _Serde serializer for converting Rust objects to JSON-compatible Python objects_
- [pyo3-asyncio](https://github.com/awestlake87/pyo3-asyncio) _Utilities for working with Python's Asyncio library and async functions_
- [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._
## Examples
@ -192,13 +193,16 @@ about this topic.
- [fastuuid](https://github.com/thedrow/fastuuid/) _Python bindings to Rust's UUID library._
- [feos](https://github.com/feos-org/feos) _Lightning fast thermodynamic modeling in Rust with fully developed Python interface._
- [forust](https://github.com/jinlow/forust) _A lightweight gradient boosted decision tree library written in Rust._
- [granian](https://github.com/emmett-framework/granian) _A Rust HTTP server for Python applications._
- [greptimedb](https://github.com/GreptimeTeam/greptimedb/tree/main/src/script) _Support [Python scripting](https://docs.greptime.com/user-guide/python-scripts/overview) in the database_
- [haem](https://github.com/BooleanCat/haem) _A Python library for working on Bioinformatics problems._
- [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._
- [hyperjson](https://github.com/mre/hyperjson) _A hyper-fast Python module for reading/writing JSON data using Rust's serde-json._
- [inline-python](https://github.com/fusion-engineering/inline-python) _Inline Python code directly in your Rust code._
- [johnnycanencrypt](https://github.com/kushaldas/johnnycanencrypt) OpenPGP library with Yubikey support.
- [jsonschema-rs](https://github.com/Stranger6667/jsonschema-rs/tree/master/bindings/python) _Fast JSON Schema validation library._
- [mocpy](https://github.com/cds-astro/mocpy) _Astronomical Python library offering data structures for describing any arbitrary coverage regions on the unit sphere._
- [opendal](https://github.com/apache/incubator-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._
- [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._
- [point-process](https://github.com/ManifoldFR/point-process-rust/tree/master/pylib) _High level API for pointprocesses as a Python library._
@ -208,16 +212,19 @@ about this topic.
- [pyheck](https://github.com/kevinheavey/pyheck) _Fast case conversion library, built by wrapping [heck](https://github.com/withoutboats/heck)._
- Quite easy to follow as there's not much code.
- [pyre](https://github.com/Project-Dream-Weaver/pyre-http) _Fast Python HTTP server written in Rust._
- [pyreqwest_impersonate](https://github.com/deedy5/pyreqwest_impersonate) _The fastest python HTTP client that can impersonate web browsers by mimicking their headers and TLS/JA3/JA4/HTTP2 fingerprints._
- [ril-py](https://github.com/Cryptex-github/ril-py) _A performant and high-level image processing library for Python written in Rust._
- [river](https://github.com/online-ml/river) _Online machine learning in python, the computationally heavy statistics algorithms are implemented in Rust._
- [rust-python-coverage](https://github.com/cjermain/rust-python-coverage) _Example PyO3 project with automated test coverage for Rust and Python._
- [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._
- [utiles](https://github.com/jessekrubin/utiles) _Fast Python web-map tile utilities_
- [wasmer-python](https://github.com/wasmerio/wasmer-python) _Python library to run WebAssembly binaries._
## Articles and other media
- [(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
@ -234,7 +241,7 @@ about this topic.
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 Gitter
- 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

View File

@ -6,14 +6,16 @@ This is notes for the current process of releasing a new PyO3 version. Replace `
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 with the new version to be released. Places to check:
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`.
Make sure not to modify the CHANGELOG during this step!
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.

View File

@ -16,7 +16,7 @@ fn ensure_auto_initialize_ok(interpreter_config: &InterpreterConfig) -> Result<(
\n\
For more information, see \
https://pyo3.rs/v{pyo3_version}/\
building_and_distribution.html#embedding-python-in-rust",
building-and-distribution.html#embedding-python-in-rust",
pyo3_version = env::var("CARGO_PKG_VERSION").unwrap()
);
}
@ -33,17 +33,20 @@ fn ensure_auto_initialize_ok(interpreter_config: &InterpreterConfig) -> Result<(
fn configure_pyo3() -> Result<()> {
let interpreter_config = pyo3_build_config::get();
interpreter_config.emit_pyo3_cfgs();
ensure_auto_initialize_ok(interpreter_config)?;
// Emit cfgs like `thread_local_const_init`
for cfg in interpreter_config.build_script_outputs() {
println!("{}", cfg)
}
// Emit cfgs like `invalid_from_utf8_lint`
print_feature_cfgs();
Ok(())
}
fn main() {
pyo3_build_config::print_expected_cfgs();
if let Err(e) = configure_pyo3() {
eprintln!("error: {}", e.report());
std::process::exit(1)

1
emscripten/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
pybuilddir.txt

View File

@ -5,10 +5,10 @@ publish = false
edition = "2021"
[dev-dependencies]
pyo3 = { version = "0.20.0", path = "..", features = ["auto-initialize", "extension-module"] }
pyo3 = { path = "..", features = ["auto-initialize", "extension-module"] }
[[example]]
name = "decorator"
path = "decorator/src/lib.rs"
crate_type = ["cdylib"]
crate-type = ["cdylib"]
doc-scrape-examples = true

View File

@ -11,6 +11,7 @@ Below is a brief description of each of these:
| `setuptools-rust-starter` | A template project which is configured to use [`setuptools_rust`](https://github.com/PyO3/setuptools-rust/) for development. |
| `word-count` | A quick performance comparison between word counter implementations written in each of Rust and Python. |
| `plugin` | Illustrates how to use Python as a scripting language within a Rust application |
| `sequential` | Illustrates how to use pyo3-ffi to write subinterpreter-safe modules |
## Creating new projects from these examples

View File

@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.20.0");
variable::set("PYO3_VERSION", "0.23.0-dev");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");

View File

@ -5,3 +5,6 @@ build-backend = "maturin"
[project]
name = "{{project-name}}"
version = "0.1.0"
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -2,8 +2,7 @@ import nox
@nox.session
def python(session):
session.install("-rrequirements-dev.txt")
session.install("maturin")
session.run_always("maturin", "develop")
def python(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")

View File

@ -14,3 +14,6 @@ classifiers = [
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -1,3 +0,0 @@
pytest>=3.5.0
pip>=21.3
maturin>=0.12,<0.13

View File

@ -40,8 +40,8 @@ impl PyCounter {
fn __call__(
&self,
py: Python<'_>,
args: &PyTuple,
kwargs: Option<&PyDict>,
args: &Bound<'_, PyTuple>,
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult<Py<PyAny>> {
let old_count = self.count.get();
let new_count = old_count + 1;
@ -51,7 +51,7 @@ impl PyCounter {
println!("{} has been called {} time(s).", name, new_count);
// After doing something, we finally forward the call to the wrapped function
let ret = self.wraps.call(py, args, kwargs)?;
let ret = self.wraps.call_bound(py, args, kwargs)?;
// We could do something with the return value of
// the function before returning it
@ -60,7 +60,7 @@ impl PyCounter {
}
#[pymodule]
pub fn decorator(_py: Python<'_>, module: &PyModule) -> PyResult<()> {
pub fn decorator(module: &Bound<'_, PyModule>) -> PyResult<()> {
module.add_class::<PyCounter>()?;
Ok(())
}

View File

@ -1,3 +1,6 @@
from decorator import Counter
@Counter
def say_hello():
print("hello")

View File

@ -45,7 +45,7 @@ def test_discussion_2598():
@Counter
def say_hello():
if say_hello.count < 2:
print(f"hello from decorator")
print("hello from decorator")
say_hello()
say_hello()

View File

@ -5,3 +5,6 @@ build-backend = "maturin"
[project]
name = "{{project-name}}"
version = "0.1.0"
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -2,8 +2,7 @@ import nox
@nox.session
def python(session):
session.install("-rrequirements-dev.txt")
session.install("maturin")
session.run_always("maturin", "develop")
def python(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")

View File

@ -14,3 +14,6 @@ classifiers = [
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -1,3 +0,0 @@
pytest>=3.5.0
pip>=21.3
maturin>=1,<2

View File

@ -2,12 +2,11 @@
use pyo3::exceptions::PyTypeError;
use pyo3::prelude::*;
use pyo3::types::PySlice;
use std::os::raw::c_long;
#[derive(FromPyObject)]
enum IntOrSlice<'py> {
Int(i32),
Slice(&'py PySlice),
Slice(Bound<'py, PySlice>),
}
#[pyclass]
@ -23,13 +22,13 @@ impl ExampleContainer {
ExampleContainer { max_length: 100 }
}
fn __getitem__(&self, key: &PyAny) -> PyResult<i32> {
fn __getitem__(&self, key: &Bound<'_, PyAny>) -> PyResult<i32> {
if let Ok(position) = key.extract::<i32>() {
return Ok(position);
} else if let Ok(slice) = key.downcast::<PySlice>() {
// 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 c_long).unwrap();
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
@ -62,8 +61,11 @@ impl ExampleContainer {
fn __setitem__(&self, idx: IntOrSlice, value: u32) -> PyResult<()> {
match idx {
IntOrSlice::Slice(slice) => {
let index = slice.indices(self.max_length as c_long).unwrap();
println!("Got a slice! {}-{}, step: {}, value: {}", index.start, index.stop, index.step, value);
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);
@ -73,9 +75,8 @@ impl ExampleContainer {
}
}
#[pymodule]
#[pyo3(name = "getitem")]
fn example(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
#[pymodule(name = "getitem")]
fn example(m: &Bound<'_, PyModule>) -> PyResult<()> {
// ? -https://github.com/PyO3/maturin/issues/475
m.add_class::<ExampleContainer>()?;
Ok(())

View File

@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.20.0");
variable::set("PYO3_VERSION", "0.23.0-dev");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");

View File

@ -5,3 +5,6 @@ build-backend = "maturin"
[project]
name = "{{project-name}}"
version = "0.1.0"
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -1,7 +1,9 @@
# import the contents of the Rust library into the Python extension
# optional: include the documentation from the Rust module
from .maturin_starter import *
from .maturin_starter import __all__, __doc__
from .maturin_starter import __all__
# optional: include the documentation from the Rust module
from .maturin_starter import __doc__ # noqa: F401
__all__ = __all__ + ["PythonClass"]

View File

@ -3,7 +3,6 @@ import nox
@nox.session
def python(session):
session.install("-rrequirements-dev.txt")
session.install("maturin")
session.run_always("maturin", "develop")
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")

View File

@ -14,3 +14,6 @@ classifiers = [
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -1,3 +0,0 @@
pytest>=3.5.0
pip>=21.3
maturin>=0.12,<0.13

View File

@ -20,15 +20,15 @@ impl ExampleClass {
/// An example module implemented in Rust using PyO3.
#[pymodule]
fn maturin_starter(py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn maturin_starter(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<ExampleClass>()?;
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: &PyDict = sys.getattr("modules")?.downcast()?;
let sys = PyModule::import_bound(py, "sys")?;
let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.downcast_into()?;
sys_modules.set_item("maturin_starter.submodule", m.getattr("submodule")?)?;
Ok(())

View File

@ -16,7 +16,7 @@ impl SubmoduleClass {
}
#[pymodule]
pub fn submodule(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
pub fn submodule(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<SubmoduleClass>()?;
Ok(())
}

View File

@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.20.0");
variable::set("PYO3_VERSION", "0.23.0-dev");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/plugin_api/Cargo.toml", "plugin_api/Cargo.toml");
file::delete(".template");

View File

@ -3,7 +3,6 @@ import nox
@nox.session
def python(session):
session.install("-rrequirements-dev.txt")
session.install("maturin")
session.run_always("maturin", "develop", "--features", "extension-module")
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")

View File

@ -10,3 +10,6 @@ classifiers = [
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -1,3 +0,0 @@
pytest>=3.5.0
pip>=21.3
maturin>=0.14

View File

@ -26,7 +26,7 @@ impl Gadget {
/// A Python module for plugin interface types
#[pymodule]
pub fn plugin_api(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
pub fn plugin_api(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Gadget>()?;
Ok(())
}

View File

@ -1,2 +1,2 @@
def test_import():
import plugin_api
import plugin_api # noqa: F401

View File

@ -19,7 +19,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// 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")?;
let plugin = PyModule::import_bound(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()?;

View File

@ -0,0 +1,12 @@
[package]
authors = ["{{authors}}"]
name = "{{project-name}}"
version = "0.1.0"
edition = "2021"
[lib]
name = "sequential"
crate-type = ["cdylib", "lib"]
[dependencies]
pyo3-ffi = { version = "{{PYO3_VERSION}}", features = ["extension-module"] }

View File

@ -0,0 +1,4 @@
variable::set("PYO3_VERSION", "0.19.2");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");

View File

@ -0,0 +1,7 @@
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "{{project-name}}"
version = "0.1.0"

View File

@ -0,0 +1,13 @@
[package]
name = "sequential"
version = "0.1.0"
edition = "2021"
[lib]
name = "sequential"
crate-type = ["cdylib", "lib"]
[dependencies]
pyo3-ffi = { path = "../../pyo3-ffi", features = ["extension-module"] }
[workspace]

View File

@ -0,0 +1,2 @@
include pyproject.toml Cargo.toml
recursive-include src *

View File

@ -0,0 +1,36 @@
# sequential
A project built using only `pyo3_ffi`, without any of PyO3's safe api. It can be executed by subinterpreters that have their own GIL.
## 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/sequential
```
(`cargo generate` will take a little while to clone the PyO3 repo first; be patient when waiting for the command to run.)

View File

@ -0,0 +1,5 @@
[template]
ignore = [".nox"]
[hooks]
pre = [".template/pre-script.rhai"]

View File

@ -0,0 +1,11 @@
import sys
import nox
@nox.session
def python(session):
if sys.version_info < (3, 12):
session.skip("Python 3.12+ is required")
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")

View File

@ -0,0 +1,20 @@
[build-system]
requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "sequential"
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",
]
requires-python = ">=3.12"
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -0,0 +1,136 @@
use core::sync::atomic::{AtomicU64, Ordering};
use core::{mem, ptr};
use std::ffi::CString;
use std::os::raw::{c_char, c_int, c_uint, c_ulonglong, c_void};
use pyo3_ffi::*;
#[repr(C)]
pub struct PyId {
_ob_base: PyObject,
id: Id,
}
static COUNT: AtomicU64 = AtomicU64::new(0);
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct Id(u64);
impl Id {
fn new() -> Self {
Id(COUNT.fetch_add(1, Ordering::Relaxed))
}
}
unsafe extern "C" fn id_new(
subtype: *mut PyTypeObject,
args: *mut PyObject,
kwds: *mut PyObject,
) -> *mut PyObject {
if PyTuple_Size(args) != 0 || !kwds.is_null() {
// We use pyo3-ffi's `c_str!` macro to create null-terminated literals because
// Rust's string literals are not null-terminated
// On Rust 1.77 or newer you can use `c"text"` instead.
PyErr_SetString(PyExc_TypeError, c_str!("Id() takes no arguments").as_ptr());
return ptr::null_mut();
}
let f: allocfunc = (*subtype).tp_alloc.unwrap_or(PyType_GenericAlloc);
let slf = f(subtype, 0);
if slf.is_null() {
return ptr::null_mut();
} else {
let id = Id::new();
let slf = slf.cast::<PyId>();
ptr::addr_of_mut!((*slf).id).write(id);
}
slf
}
unsafe extern "C" fn id_repr(slf: *mut PyObject) -> *mut PyObject {
let slf = slf.cast::<PyId>();
let id = (*slf).id.0;
let string = format!("Id({})", id);
PyUnicode_FromStringAndSize(string.as_ptr().cast::<c_char>(), string.len() as Py_ssize_t)
}
unsafe extern "C" fn id_int(slf: *mut PyObject) -> *mut PyObject {
let slf = slf.cast::<PyId>();
let id = (*slf).id.0;
PyLong_FromUnsignedLongLong(id as c_ulonglong)
}
unsafe extern "C" fn id_richcompare(
slf: *mut PyObject,
other: *mut PyObject,
op: c_int,
) -> *mut PyObject {
let pytype = Py_TYPE(slf); // guaranteed to be `sequential.Id`
if Py_TYPE(other) != pytype {
return Py_NewRef(Py_NotImplemented());
}
let slf = (*slf.cast::<PyId>()).id;
let other = (*other.cast::<PyId>()).id;
let cmp = match op {
pyo3_ffi::Py_LT => slf < other,
pyo3_ffi::Py_LE => slf <= other,
pyo3_ffi::Py_EQ => slf == other,
pyo3_ffi::Py_NE => slf != other,
pyo3_ffi::Py_GT => slf > other,
pyo3_ffi::Py_GE => slf >= other,
unrecognized => {
let msg = CString::new(&*format!(
"unrecognized richcompare opcode {}",
unrecognized
))
.unwrap();
PyErr_SetString(PyExc_SystemError, msg.as_ptr());
return ptr::null_mut();
}
};
if cmp {
Py_NewRef(Py_True())
} else {
Py_NewRef(Py_False())
}
}
static mut SLOTS: &[PyType_Slot] = &[
PyType_Slot {
slot: Py_tp_new,
pfunc: id_new as *mut c_void,
},
PyType_Slot {
slot: Py_tp_doc,
pfunc: c_str!("An id that is increased every time an instance is created").as_ptr()
as *mut c_void,
},
PyType_Slot {
slot: Py_tp_repr,
pfunc: id_repr as *mut c_void,
},
PyType_Slot {
slot: Py_nb_int,
pfunc: id_int as *mut c_void,
},
PyType_Slot {
slot: Py_tp_richcompare,
pfunc: id_richcompare as *mut c_void,
},
PyType_Slot {
slot: 0,
pfunc: ptr::null_mut(),
},
];
pub static mut ID_SPEC: PyType_Spec = PyType_Spec {
name: c_str!("sequential.Id").as_ptr(),
basicsize: mem::size_of::<PyId>() as c_int,
itemsize: 0,
flags: (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE) as c_uint,
slots: unsafe { SLOTS as *const [PyType_Slot] as *mut PyType_Slot },
};

View File

@ -0,0 +1,14 @@
use std::ptr;
use pyo3_ffi::*;
mod id;
mod module;
use crate::module::MODULE_DEF;
// The module initialization function, which must be named `PyInit_<your_module>`.
#[allow(non_snake_case)]
#[no_mangle]
pub unsafe extern "C" fn PyInit_sequential() -> *mut PyObject {
PyModuleDef_Init(ptr::addr_of_mut!(MODULE_DEF))
}

View File

@ -0,0 +1,80 @@
use core::{mem, ptr};
use pyo3_ffi::*;
use std::os::raw::{c_int, c_void};
pub static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_base: PyModuleDef_HEAD_INIT,
m_name: c_str!("sequential").as_ptr(),
m_doc: c_str!("A library for generating sequential ids, written in Rust.").as_ptr(),
m_size: mem::size_of::<sequential_state>() as Py_ssize_t,
m_methods: std::ptr::null_mut(),
m_slots: unsafe { SEQUENTIAL_SLOTS as *const [PyModuleDef_Slot] as *mut PyModuleDef_Slot },
m_traverse: Some(sequential_traverse),
m_clear: Some(sequential_clear),
m_free: Some(sequential_free),
};
static mut SEQUENTIAL_SLOTS: &[PyModuleDef_Slot] = &[
PyModuleDef_Slot {
slot: Py_mod_exec,
value: sequential_exec as *mut c_void,
},
PyModuleDef_Slot {
slot: Py_mod_multiple_interpreters,
value: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED,
},
PyModuleDef_Slot {
slot: 0,
value: ptr::null_mut(),
},
];
unsafe extern "C" fn sequential_exec(module: *mut PyObject) -> c_int {
let state: *mut sequential_state = PyModule_GetState(module).cast();
let id_type = PyType_FromModuleAndSpec(
module,
ptr::addr_of_mut!(crate::id::ID_SPEC),
ptr::null_mut(),
);
if id_type.is_null() {
PyErr_SetString(
PyExc_SystemError,
c_str!("cannot locate type object").as_ptr(),
);
return -1;
}
(*state).id_type = id_type.cast::<PyTypeObject>();
PyModule_AddObjectRef(module, c_str!("Id").as_ptr(), id_type)
}
unsafe extern "C" fn sequential_traverse(
module: *mut PyObject,
visit: visitproc,
arg: *mut c_void,
) -> c_int {
let state: *mut sequential_state = PyModule_GetState(module.cast()).cast();
let id_type: *mut PyObject = (*state).id_type.cast();
if id_type.is_null() {
0
} else {
(visit)(id_type, arg)
}
}
unsafe extern "C" fn sequential_clear(module: *mut PyObject) -> c_int {
let state: *mut sequential_state = PyModule_GetState(module.cast()).cast();
Py_CLEAR(ptr::addr_of_mut!((*state).id_type).cast());
0
}
unsafe extern "C" fn sequential_free(module: *mut c_void) {
sequential_clear(module.cast());
}
#[repr(C)]
struct sequential_state {
id_type: *mut PyTypeObject,
}

View File

@ -0,0 +1,147 @@
use core::ffi::{c_char, CStr};
use core::ptr;
use std::thread;
use pyo3_ffi::*;
use sequential::PyInit_sequential;
static COMMAND: &'static str = c_str!(
"
from sequential import Id
s = sum(int(Id()) for _ in range(12))
"
);
// Newtype to be able to pass it to another thread.
struct State(*mut PyThreadState);
unsafe impl Sync for State {}
unsafe impl Send for State {}
#[test]
fn lets_go_fast() -> Result<(), String> {
unsafe {
let ret = PyImport_AppendInittab(c_str!("sequential").as_ptr(), Some(PyInit_sequential));
if ret == -1 {
return Err("could not add module to inittab".into());
}
Py_Initialize();
let main_state = PyThreadState_Swap(ptr::null_mut());
const NULL: State = State(ptr::null_mut());
let mut subs = [NULL; 12];
let config = PyInterpreterConfig {
use_main_obmalloc: 0,
allow_fork: 0,
allow_exec: 0,
allow_threads: 1,
allow_daemon_threads: 0,
check_multi_interp_extensions: 1,
gil: PyInterpreterConfig_OWN_GIL,
};
for State(state) in &mut subs {
let status = Py_NewInterpreterFromConfig(state, &config);
if PyStatus_IsError(status) == 1 {
let msg = if status.err_msg.is_null() {
"no error message".into()
} else {
CStr::from_ptr(status.err_msg).to_string_lossy()
};
PyThreadState_Swap(main_state);
Py_FinalizeEx();
return Err(format!("could not create new subinterpreter: {msg}"));
}
}
PyThreadState_Swap(main_state);
let main_state = PyEval_SaveThread(); // a PyInterpreterConfig with shared gil would deadlock otherwise
let ints: Vec<_> = thread::scope(move |s| {
let mut handles = vec![];
for state in subs {
let handle = s.spawn(move || {
let state = state;
PyEval_RestoreThread(state.0);
let ret = run_code();
Py_EndInterpreter(state.0);
ret
});
handles.push(handle);
}
handles.into_iter().map(|h| h.join().unwrap()).collect()
});
PyEval_RestoreThread(main_state);
let ret = Py_FinalizeEx();
if ret == -1 {
return Err("could not finalize interpreter".into());
}
let mut sum: u64 = 0;
for i in ints {
let i = i?;
sum += i;
}
assert_eq!(sum, (0..).take(12 * 12).sum());
}
Ok(())
}
unsafe fn fetch() -> String {
let err = PyErr_GetRaisedException();
let err_repr = PyObject_Str(err);
if !err_repr.is_null() {
let mut size = 0;
let p = PyUnicode_AsUTF8AndSize(err_repr, &mut size);
if !p.is_null() {
let s = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
p.cast::<u8>(),
size as usize,
));
let s = String::from(s);
Py_DECREF(err_repr);
return s;
}
}
String::from("could not get error")
}
fn run_code() -> Result<u64, String> {
unsafe {
let code_obj =
Py_CompileString(COMMAND.as_ptr(), c_str!("program").as_ptr(), Py_file_input);
if code_obj.is_null() {
return Err(fetch());
}
let globals = PyDict_New();
let res_ptr = PyEval_EvalCode(code_obj, globals, ptr::null_mut());
Py_DECREF(code_obj);
if res_ptr.is_null() {
return Err(fetch());
} else {
Py_DECREF(res_ptr);
}
let sum = PyDict_GetItemString(globals, c_str!("s").as_ptr()); /* borrowed reference */
if sum.is_null() {
Py_DECREF(globals);
return Err("globals did not have `s`".into());
}
let int = PyLong_AsUnsignedLongLong(sum) as u64;
Py_DECREF(globals);
Ok(int)
}
}

View File

@ -0,0 +1,21 @@
import pytest
from sequential import Id
def test_make_some():
for x in range(12):
i = Id()
assert x == int(i)
def test_args():
with pytest.raises(TypeError, match="Id\\(\\) takes no arguments"):
Id(3, 4)
def test_cmp():
a = Id()
b = Id()
assert a <= b
assert a < b
assert a == a

View File

@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.20.0");
variable::set("PYO3_VERSION", "0.23.0-dev");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/setup.cfg", "setup.cfg");
file::delete(".template");

View File

@ -2,7 +2,7 @@ import nox
@nox.session
def python(session):
def python(session: nox.Session):
session.install("-rrequirements-dev.txt")
session.run_always(
"pip", "install", "-e", ".", "--no-build-isolation", env={"BUILD_DEBUG": "1"}

View File

@ -1,7 +1,9 @@
# import the contents of the Rust library into the Python extension
# optional: include the documentation from the Rust module
from ._setuptools_rust_starter import *
from ._setuptools_rust_starter import __all__, __doc__
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"]

View File

@ -20,15 +20,15 @@ impl ExampleClass {
/// An example module implemented in Rust using PyO3.
#[pymodule]
fn _setuptools_rust_starter(py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn _setuptools_rust_starter(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<ExampleClass>()?;
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: &PyDict = sys.getattr("modules")?.downcast()?;
let sys = PyModule::import_bound(py, "sys")?;
let sys_modules: Bound<'_, PyDict> = sys.getattr("modules")?.downcast_into()?;
sys_modules.set_item("setuptools_rust_starter.submodule", m.getattr("submodule")?)?;
Ok(())

View File

@ -16,7 +16,7 @@ impl SubmoduleClass {
}
#[pymodule]
pub fn submodule(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
pub fn submodule(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<SubmoduleClass>()?;
Ok(())
}

View File

@ -5,3 +5,6 @@ build-backend = "maturin"
[project]
name = "{{project-name}}"
version = "0.1.0"
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -2,8 +2,7 @@ import nox
@nox.session
def python(session):
session.install("-rrequirements-dev.txt")
session.install("maturin")
session.run_always("maturin", "develop")
def python(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")

View File

@ -3,7 +3,7 @@ requires = ["maturin>=1,<2"]
build-backend = "maturin"
[project]
name = "string sum"
name = "string_sum"
version = "0.1.0"
classifiers = [
"License :: OSI Approved :: MIT License",
@ -14,3 +14,6 @@ classifiers = [
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest"]

View File

@ -1,3 +0,0 @@
pytest>=3.5.0
pip>=21.3
maturin>=0.12,<0.13

View File

@ -5,10 +5,8 @@ use pyo3_ffi::*;
static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_base: PyModuleDef_HEAD_INIT,
m_name: "string_sum\0".as_ptr().cast::<c_char>(),
m_doc: "A Python module written in Rust.\0"
.as_ptr()
.cast::<c_char>(),
m_name: c_str!("string_sum").as_ptr(),
m_doc: c_str!("A Python module written in Rust.").as_ptr(),
m_size: 0,
m_methods: unsafe { METHODS as *const [PyMethodDef] as *mut PyMethodDef },
m_slots: std::ptr::null_mut(),
@ -19,14 +17,12 @@ static mut MODULE_DEF: PyModuleDef = PyModuleDef {
static mut METHODS: &[PyMethodDef] = &[
PyMethodDef {
ml_name: "sum_as_string\0".as_ptr().cast::<c_char>(),
ml_name: c_str!("sum_as_string").as_ptr(),
ml_meth: PyMethodDefPointer {
_PyCFunctionFast: sum_as_string,
},
ml_flags: METH_FASTCALL,
ml_doc: "returns the sum of two integers as a string\0"
.as_ptr()
.cast::<c_char>(),
ml_doc: c_str!("returns the sum of two integers as a string").as_ptr(),
},
// A zeroed PyMethodDef to mark the end of the array.
PyMethodDef::zeroed(),
@ -93,9 +89,7 @@ pub unsafe extern "C" fn sum_as_string(
if nargs != 2 {
PyErr_SetString(
PyExc_TypeError,
"sum_as_string expected 2 positional arguments\0"
.as_ptr()
.cast::<c_char>(),
c_str!("sum_as_string expected 2 positional arguments").as_ptr(),
);
return std::ptr::null_mut();
}
@ -119,7 +113,7 @@ pub unsafe extern "C" fn sum_as_string(
None => {
PyErr_SetString(
PyExc_OverflowError,
"arguments too large to add\0".as_ptr().cast::<c_char>(),
c_str!("arguments too large to add").as_ptr(),
);
std::ptr::null_mut()
}

View File

@ -14,7 +14,7 @@ def test_err1():
with pytest.raises(
TypeError, match="sum_as_string expected an int for positional argument 1"
) as e:
):
sum_as_string(a, b)
@ -23,19 +23,19 @@ def test_err2():
with pytest.raises(
TypeError, match="sum_as_string expected an int for positional argument 2"
) as e:
):
sum_as_string(a, b)
def test_overflow1():
a, b = 0, 1 << 43
with pytest.raises(OverflowError, match="cannot fit 8796093022208 in 32 bits") as e:
with pytest.raises(OverflowError, match="cannot fit 8796093022208 in 32 bits"):
sum_as_string(a, b)
def test_overflow2():
a, b = 1 << 30, 1 << 30
with pytest.raises(OverflowError, match="arguments too large to add") as e:
with pytest.raises(OverflowError, match="arguments too large to add"):
sum_as_string(a, b)

View File

@ -1,3 +1,4 @@
variable::set("PYO3_VERSION", "0.20.0");
variable::set("PYO3_VERSION", "0.23.0-dev");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");

View File

@ -1,9 +1,13 @@
[build-system]
requires = ["setuptools>=41.0.0", "wheel", "setuptools_rust>=1.0.0"]
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"

View File

@ -1,9 +0,0 @@
[metadata]
name = {{project-name}}
version = 0.1.0
packages =
word_count
[options]
include_package_data = True
zip_safe = False

View File

@ -4,15 +4,14 @@ nox.options.sessions = ["test"]
@nox.session
def test(session):
session.install("-rrequirements-dev.txt")
session.install("maturin")
session.run_always("maturin", "develop")
def test(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest")
@nox.session
def bench(session):
session.install("-rrequirements-dev.txt")
session.install(".")
def bench(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.install(".[dev]")
session.run("pytest", "--benchmark-enable")

View File

@ -15,6 +15,8 @@ classifiers = [
"Operating System :: MacOS :: MacOS X",
]
[project.optional-dependencies]
dev = ["pytest", "pytest-benchmark"]
[tool.pytest.ini_options]
addopts = "--benchmark-disable"

View File

@ -1,2 +0,0 @@
pytest>=3.5.0
pytest-benchmark>=3.1.1

View File

@ -33,7 +33,7 @@ fn count_line(line: &str, needle: &str) -> usize {
}
#[pymodule]
fn word_count(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
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_allow_threads, m)?)?;

View File

@ -2,21 +2,26 @@
| 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. |
| <span style="white-space: pre">`crate = "some::path"`</span> | 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. |
| <span style="white-space: pre">`extends = BaseType`</span> | Use a custom baseclass. Defaults to [`PyAny`][params-1] |
| <span style="white-space: pre">`freelist = N`</span> | 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. |
| <span style="white-space: pre">`frozen`</span> | 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. |
| `get_all` | Generates getters for all fields of the pyclass. |
| `hash` | Implements `__hash__` using the `Hash` implementation of the underlying Rust datatype. |
| `mapping` | Inform PyO3 that this class is a [`Mapping`][params-mapping], and so leave its implementation of sequence C-API slots empty. |
| <span style="white-space: pre">`module = "module_name"`</span> | Python code will see the class as being defined in this module. Defaults to `builtins`. |
| <span style="white-space: pre">`name = "python_name"`</span> | 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. |
| `subclass` | Allows other Python classes and `#[pyclass]` to inherit from this class. Enums cannot be subclassed. |
| <span style="white-space: pre">`text_signature = "(arg1, arg2, ...)"`</span> | Sets the text signature for the Python class' `__new__` method. |
| `unsendable` | Required if your struct is not [`Send`][params-3]. Rather than using `unsendable`, consider implementing your struct in a threadsafe way by e.g. substituting [`Rc`][params-4] with [`Arc`][params-5]. By using `unsendable`, your class will panic when accessed by another thread.|
| `unsendable` | Required if your struct is not [`Send`][params-3]. Rather than using `unsendable`, consider implementing your struct in a threadsafe 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
@ -33,11 +38,12 @@ struct MyClass {}
struct MyClass {}
```
[params-1]: https://docs.rs/pyo3/latest/pyo3/struct.PyAny.html
[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

View File

@ -4,30 +4,34 @@
---
- [Getting started](getting_started.md)
- [Python modules](module.md)
- [Python functions](function.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)
- [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)
- [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)]
- [Python exceptions](exception.md)
- [Calling Python from Rust](python_from_rust.md)
- [GIL, mutability and object types](types.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)
- [Debugging](debugging.md)
- [Features reference](features.md)
- [Memory management](memory.md)
- [Performance](performance.md)
- [Advanced topics](advanced.md)
- [Building and distribution](building_and_distribution.md)
- [Supporting multiple Python versions](building_and_distribution/multiple_python_versions.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)
- [Using `async` and `await`](ecosystem/async-await.md)
@ -36,9 +40,8 @@
---
[Appendix A: Migration guide](migration.md)
[Appendix B: PyO3 and rust-cpython](rust_cpython.md)
[Appendix C: Trait bounds](trait_bounds.md)
[Appendix D: Python typing hints](python_typing_hints.md)
[Appendix B: Trait bounds](trait-bounds.md)
[Appendix C: Python typing hints](python-typing-hints.md)
[CHANGELOG](changelog.md)
---

View File

@ -5,10 +5,3 @@
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.
## Memory management
PyO3's `&PyAny` "owned references" and `Py<PyAny>` smart pointers are used to
access memory stored in Python's heap. This memory sometimes lives for longer
than expected because of differences in Rust and Python's memory models. See
the chapter on [memory management](./memory.md) for more information.

103
guide/src/async-await.md Normal file
View File

@ -0,0 +1,103 @@
# 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
# #![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<PyObject>) -> Option<PyObject> {
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.
## Implicit GIL holding
Even if it is not possible to pass a `py: Python<'py>` parameter to `async fn`, the GIL is still held during the execution of the future it's also the case for regular `fn` without `Python<'py>`/`Bound<'py, PyAny>` parameter, yet the GIL is held.
It is still possible to get a `Python` marker using [`Python::with_gil`]({{#PYO3_DOCS_URL}}/pyo3/marker/struct.Python.html#method.with_gil); because `with_gil` is reentrant and optimized, the cost will be negligible.
## Release the GIL across `.await`
There is currently no simple way to release the GIL 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>(F);
impl<F> Future for AllowThreads<F>
where
F: Future + Unpin + Send,
F::Output: Send,
{
type Output = F::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let waker = cx.waker();
Python::with_gil(|gil| {
gil.allow_threads(|| 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
# #![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.*

View File

@ -2,9 +2,9 @@
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.
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.html).
There is an additional sub-chapter dedicated to [supporting multiple Python versions](./building-and-distribution/multiple-python-versions.md).
## Configuring the Python version
@ -151,7 +151,7 @@ rustflags = [
]
```
Alternatively, on rust >= 1.56, one can include in `build.rs`:
Alternatively, one can include in `build.rs`:
```rust
fn main() {
@ -163,7 +163,7 @@ fn main() {
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-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror)).
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 `extension-module` feature
@ -177,7 +177,7 @@ The downside of not linking to `libpython` is that binaries, tests, and examples
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.html) section of this guide, in particular the `#[cfg(Py_LIMITED_API)]` flag.
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:
@ -198,7 +198,7 @@ See the [corresponding](https://github.com/PyO3/maturin/pull/353) [PRs](https://
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.html#checking-the-python-version-at-runtime) on supporting multiple Python versions at runtime.
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.
@ -232,7 +232,7 @@ not work when compiling for `abi3`. These are:
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](#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. For example, this is the default for `pyenv` on macOS.
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
@ -242,7 +242,7 @@ This mode of embedding works well for Rust tests which need access to the Python
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://foss.heptapod.net/pypy/pypy/-/issues/3286).
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
@ -285,7 +285,7 @@ Thanks to Rust's great cross-compilation support, cross-compiling using PyO3 is
* 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`](#python-version) variable (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.

View File

@ -85,7 +85,7 @@ 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.html#minimum-python-version-for-abi3) of Python, but may be running on newer Python versions.
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.
@ -104,5 +104,5 @@ Python::with_gil(|py| {
});
```
[`Python::version()`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.version
[`Python::version_info()`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.version_info
[`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

View File

@ -2,7 +2,7 @@
PyO3 exposes a group of attributes powered by Rust's proc macro system for defining Python classes as Rust structs.
The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or a fieldless `enum` (a.k.a. C-like enum) to generate a Python type for it. They will usually also have *one* `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled, each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`.
The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or `enum` to generate a Python type for it. They will usually also have *one* `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled, each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`.
This chapter will discuss the functionality and configuration these attributes offer. Below is a list of links to the relevant section of this chapter for each:
@ -16,18 +16,18 @@ This chapter will discuss the functionality and configuration these attributes o
- [`#[classmethod]`](#class-methods)
- [`#[classattr]`](#class-attributes)
- [`#[args]`](#method-arguments)
- [Magic methods and slots](class/protocols.html)
- [Magic methods and slots](class/protocols.md)
- [Classes as function arguments](#classes-as-function-arguments)
## Defining a new class
To define a custom Python class, add the `#[pyclass]` attribute to a Rust struct or a fieldless enum.
To define a custom Python class, add the `#[pyclass]` attribute to a Rust struct or enum.
```rust
# #![allow(dead_code)]
use pyo3::prelude::*;
#[pyclass]
struct Integer {
struct MyClass {
inner: i32,
}
@ -35,8 +35,18 @@ struct Integer {
#[pyclass]
struct Number(i32);
// PyO3 supports custom discriminants in enums
#[pyclass]
// PyO3 supports unit-only enums (which contain only unit variants)
// These simple enums behave similarly to Python's enumerations (enum.Enum)
#[pyclass(eq, eq_int)]
#[derive(PartialEq)]
enum MyEnum {
Variant,
OtherVariant = 30, // PyO3 supports custom discriminants.
}
// PyO3 supports custom discriminants in unit-only enums
#[pyclass(eq, eq_int)]
#[derive(PartialEq)]
enum HttpResponse {
Ok = 200,
NotFound = 404,
@ -44,14 +54,22 @@ enum HttpResponse {
// ...
}
// PyO3 also supports enums with Struct and Tuple variants
// These complex enums have sligtly different behavior from the simple enums above
// They are meant to work with instance checks and match statement patterns
// The variants can be mixed and matched
// Struct variants have named fields while tuple enums generate generic names for fields in order _0, _1, _2, ...
// Apart from this both types are functionally identical
#[pyclass]
enum MyEnum {
Variant,
OtherVariant = 30, // PyO3 supports custom discriminants.
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
RegularPolygon(u32, f64),
Nothing(),
}
```
The above example generates implementations for [`PyTypeInfo`] and [`PyClass`] for `MyClass` and `MyEnum`. To see these generated implementations, refer to the [implementation details](#implementation-details) at the end of this chapter.
The above example generates implementations for [`PyTypeInfo`] and [`PyClass`] for `MyClass`, `Number`, `MyEnum`, `HttpResponse`, and `Shape`. To see these generated implementations, refer to the [implementation details](#implementation-details) at the end of this chapter.
### Restrictions
@ -69,6 +87,38 @@ When you need to share ownership of data between Python and Rust, instead of usi
A Rust `struct Foo<T>` with a generic parameter `T` generates new compiled implementations each time it is used with a different concrete type for `T`. These new implementations are generated by the compiler at each usage site. This is incompatible with wrapping `Foo` in Python, where there needs to be a single compiled implementation of `Foo` which is integrated with the Python interpreter.
Currently, the best alternative is to write a macro which expands to a new `#[pyclass]` for each instantiation you want:
```rust
# #![allow(dead_code)]
use pyo3::prelude::*;
struct GenericClass<T> {
data: T,
}
macro_rules! create_interface {
($name: ident, $type: ident) => {
#[pyclass]
pub struct $name {
inner: GenericClass<$type>,
}
#[pymethods]
impl $name {
#[new]
pub fn new(data: $type) -> Self {
Self {
inner: GenericClass { data: data },
}
}
}
};
}
create_interface!(IntClass, i64);
create_interface!(FloatClass, String);
```
#### Must be Send
Because Python objects are freely shared between threads by the Python interpreter, there is no guarantee which thread will eventually drop the object. Therefore all types annotated with `#[pyclass]` must implement `Send` (unless annotated with [`#[pyclass(unsendable)]`](#customizing-the-class)).
@ -80,6 +130,7 @@ To declare a constructor, you need to define a method and annotate it with the `
attribute. Only Python's `__new__` method can be specified, `__init__` is not available.
```rust
# #![allow(dead_code)]
# use pyo3::prelude::*;
# #[pyclass]
# struct Number(i32);
@ -96,6 +147,7 @@ impl Number {
Alternatively, if your `new` method may fail you can return `PyResult<Self>`.
```rust
# #![allow(dead_code)]
# use pyo3::prelude::*;
# use pyo3::exceptions::PyValueError;
# #[pyclass]
@ -130,37 +182,33 @@ For arguments, see the [`Method arguments`](#method-arguments) section below.
The next step is to create the module initializer and add our class to it:
```rust
# #![allow(dead_code)]
# use pyo3::prelude::*;
# #[pyclass]
# struct Number(i32);
#
#[pymodule]
fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Number>()?;
Ok(())
}
```
## PyCell and interior mutability
## Bound<T> and interior mutability
You sometimes need to convert your `pyclass` into a Python object and access it
from Rust code (e.g., for testing it).
[`PyCell`] is the primary interface for that.
Often is useful to turn a `#[pyclass]` type `T` into a Python object and access it from Rust code. The [`Py<T>`] and [`Bound<'py, T>`] smart pointers are the ways to represent a Python object in PyO3's API. More detail can be found about them [in the Python objects](./types.md#pyo3s-smart-pointers) section of the guide.
`PyCell<T: PyClass>` is always allocated in the Python heap, so Rust doesn't have ownership of it.
In other words, Rust code can only extract a `&PyCell<T>`, not a `PyCell<T>`.
Most Python objects do not offer exclusive (`&mut`) access (see the [section on Python's memory model](./python-from-rust.md#pythons-memory-model)). However, Rust structs wrapped as Python objects (called `pyclass` types) often *do* need `&mut` access. Due to the GIL, PyO3 *can* guarantee exclusive access to them.
Thus, to mutate data behind `&PyCell` safely, PyO3 employs the
[Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html)
like [`RefCell`].
The Rust borrow checker cannot reason about `&mut` references once an object's ownership has been passed to the Python interpreter. This means that borrow checking is done at runtime using with a scheme very similar to `std::cell::RefCell<T>`. This is known as [interior mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html).
Users who are familiar with `RefCell` can use `PyCell` just like `RefCell`.
Users who are familiar with `RefCell<T>` can use `Py<T>` and `Bound<'py, T>` just like `RefCell<T>`.
For users who are not very familiar with `RefCell`, here is a reminder of Rust's rules of borrowing:
For users who are not very familiar with `RefCell<T>`, here is a reminder of Rust's rules of borrowing:
- At any given time, you can have either (but not both of) one mutable reference or any number of immutable references.
- References must always be valid.
- References can never outlast the data they refer to.
`PyCell`, like `RefCell`, ensures these borrowing rules by tracking references at runtime.
`Py<T>` and `Bound<'py, T>`, like `RefCell<T>`, ensure these borrowing rules by tracking references at runtime.
```rust
# use pyo3::prelude::*;
@ -170,7 +218,7 @@ struct MyClass {
num: i32,
}
Python::with_gil(|py| {
let obj = PyCell::new(py, MyClass { num: 3 }).unwrap();
let obj = Bound::new(py, MyClass { num: 3 }).unwrap();
{
let obj_ref = obj.borrow(); // Get PyRef
assert_eq!(obj_ref.num, 3);
@ -185,15 +233,13 @@ Python::with_gil(|py| {
assert!(obj.try_borrow_mut().is_err());
}
// You can convert `&PyCell` to a Python object
// You can convert `Bound` to a Python object
pyo3::py_run!(py, obj, "assert obj.num == 5");
});
```
`&PyCell<T>` is bounded by the same lifetime as a [`GILGuard`].
To make the object longer lived (for example, to store it in a struct on the
Rust side), you can use `Py<T>`, which stores an object longer than the GIL
lifetime, and therefore needs a `Python<'_>` token to access.
A `Bound<'py, T>` is restricted to the GIL lifetime `'py`. To make the object longer lived (for example, to store it in a struct on the
Rust side), use `Py<T>`. `Py<T>` needs a `Python<'_>` token to allow access:
```rust
# use pyo3::prelude::*;
@ -208,9 +254,9 @@ fn return_myclass() -> Py<MyClass> {
let obj = return_myclass();
Python::with_gil(|py| {
let cell = obj.as_ref(py); // Py<MyClass>::as_ref returns &PyCell<MyClass>
let obj_ref = cell.borrow(); // Get PyRef<T>
Python::with_gil(move |py| {
let bound = obj.bind(py); // Py<MyClass>::bind returns &Bound<'py, MyClass>
let obj_ref = bound.borrow(); // Get PyRef<T>
assert_eq!(obj_ref.num, 1);
});
```
@ -219,7 +265,7 @@ Python::with_gil(|py| {
As detailed above, runtime borrow checking is currently enabled by default. But a class can opt of out it by declaring itself `frozen`. It can still use interior mutability via standard Rust types like `RefCell` or `Mutex`, but it is not bound to the implementation provided by PyO3 and can choose the most appropriate strategy on field-by-field basis.
Classes which are `frozen` and also `Sync`, e.g. they do use `Mutex` but not `RefCell`, can be accessed without needing the Python GIL via the `PyCell::get` and `Py::get` methods:
Classes which are `frozen` and also `Sync`, e.g. they do use `Mutex` but not `RefCell`, can be accessed without needing the Python GIL via the `Bound::get` and `Py::get` methods:
```rust
use std::sync::atomic::{AtomicUsize, Ordering};
@ -239,13 +285,15 @@ let py_counter: Py<FrozenCounter> = Python::with_gil(|py| {
});
py_counter.get().value.fetch_add(1, Ordering::Relaxed);
Python::with_gil(move |_py| drop(py_counter));
```
Frozen classes are likely to become the default thereby guiding the PyO3 ecosystem towards a more deliberate application of interior mutability. Eventually, this should enable further optimizations of PyO3's internals and avoid downstream code paying the cost of interior mutability when it is not actually required.
## Customizing the class
{{#include ../pyclass_parameters.md}}
{{#include ../pyclass-parameters.md}}
These parameters are covered in various sections of this guide.
@ -279,8 +327,12 @@ explicitly.
To get a parent class from a child, use [`PyRef`] instead of `&self` for methods,
or [`PyRefMut`] instead of `&mut self`.
Then you can access a parent class by `self_.as_ref()` as `&Self::BaseClass`,
or by `self_.into_super()` as `PyRef<Self::BaseClass>`.
Then you can access a parent class by `self_.as_super()` as `&PyRef<Self::BaseClass>`,
or by `self_.into_super()` as `PyRef<Self::BaseClass>` (and similar for the `PyRefMut`
case). For convenience, `self_.as_ref()` can also be used to get `&Self::BaseClass`
directly; however, this approach does not let you access base clases higher in the
inheritance hierarchy, for which you would need to chain multiple `as_super` or
`into_super` calls.
```rust
# use pyo3::prelude::*;
@ -297,7 +349,7 @@ impl BaseClass {
BaseClass { val1: 10 }
}
pub fn method(&self) -> PyResult<usize> {
pub fn method1(&self) -> PyResult<usize> {
Ok(self.val1)
}
}
@ -315,8 +367,8 @@ impl SubClass {
}
fn method2(self_: PyRef<'_, Self>) -> PyResult<usize> {
let super_ = self_.as_ref(); // Get &BaseClass
super_.method().map(|x| x * self_.val2)
let super_ = self_.as_super(); // Get &PyRef<BaseClass>
super_.method1().map(|x| x * self_.val2)
}
}
@ -333,14 +385,54 @@ impl SubSubClass {
}
fn method3(self_: PyRef<'_, Self>) -> PyResult<usize> {
let base = self_.as_super().as_super(); // Get &PyRef<'_, BaseClass>
base.method1().map(|x| x * self_.val3)
}
fn method4(self_: PyRef<'_, Self>) -> PyResult<usize> {
let v = self_.val3;
let super_ = self_.into_super(); // Get PyRef<'_, SubClass>
SubClass::method2(super_).map(|x| x * v)
}
fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) {
let val1 = self_.as_super().as_super().val1;
let val2 = self_.as_super().val2;
(val1, val2, self_.val3)
}
fn double_values(mut self_: PyRefMut<'_, Self>) {
self_.as_super().as_super().val1 *= 2;
self_.as_super().val2 *= 2;
self_.val3 *= 2;
}
#[staticmethod]
fn factory_method(py: Python<'_>, val: usize) -> PyResult<PyObject> {
let base = PyClassInitializer::from(BaseClass::new());
let sub = base.add_subclass(SubClass { val2: val });
if val % 2 == 0 {
Ok(Py::new(py, sub)?.to_object(py))
} else {
let sub_sub = sub.add_subclass(SubSubClass { val3: val });
Ok(Py::new(py, sub_sub)?.to_object(py))
}
}
}
# Python::with_gil(|py| {
# let subsub = pyo3::PyCell::new(py, SubSubClass::new()).unwrap();
# pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000")
# let subsub = pyo3::Py::new(py, SubSubClass::new()).unwrap();
# pyo3::py_run!(py, subsub, "assert subsub.method1() == 10");
# pyo3::py_run!(py, subsub, "assert subsub.method2() == 150");
# pyo3::py_run!(py, subsub, "assert subsub.method3() == 200");
# pyo3::py_run!(py, subsub, "assert subsub.method4() == 3000");
# pyo3::py_run!(py, subsub, "assert subsub.get_values() == (10, 15, 20)");
# pyo3::py_run!(py, subsub, "assert subsub.double_values() == None");
# pyo3::py_run!(py, subsub, "assert subsub.get_values() == (20, 30, 40)");
# let subsub = SubSubClass::factory_method(py, 2).unwrap();
# let subsubsub = SubSubClass::factory_method(py, 3).unwrap();
# let cls = py.get_type_bound::<SubSubClass>();
# pyo3::py_run!(py, subsub cls, "assert not isinstance(subsub, cls)");
# pyo3::py_run!(py, subsubsub cls, "assert isinstance(subsubsub, cls)");
# });
```
@ -348,8 +440,9 @@ You can inherit native types such as `PyDict`, if they implement
[`PySizedLayout`]({{#PYO3_DOCS_URL}}/pyo3/type_object/trait.PySizedLayout.html).
This is not supported when building for the Python limited API (aka the `abi3` feature of PyO3).
However, because of some technical problems, we don't currently provide safe upcasting methods for types
that inherit native types. Even in such cases, you can unsafely get a base class by raw pointer conversion.
To convert between the Rust type and its native base class, you can take
`slf` as a Python object. To access the Rust fields use `slf.borrow()` or
`slf.borrow_mut()`, and to access the base class use `slf.downcast::<BaseClass>()`.
```rust
# #[cfg(not(Py_LIMITED_API))] {
@ -370,15 +463,14 @@ impl DictWithCounter {
Self::default()
}
fn set(mut self_: PyRefMut<'_, Self>, key: String, value: &PyAny) -> PyResult<()> {
self_.counter.entry(key.clone()).or_insert(0);
let py = self_.py();
let dict: &PyDict = unsafe { py.from_borrowed_ptr_or_err(self_.as_ptr())? };
fn set(slf: &Bound<'_, Self>, key: String, value: Bound<'_, PyAny>) -> PyResult<()> {
slf.borrow_mut().counter.entry(key.clone()).or_insert(0);
let dict = slf.downcast::<PyDict>()?;
dict.set_item(key, value)
}
}
# Python::with_gil(|py| {
# let cnt = pyo3::PyCell::new(py, DictWithCounter::new()).unwrap();
# let cnt = pyo3::Py::new(py, DictWithCounter::new()).unwrap();
# pyo3::py_run!(py, cnt, "cnt.set('abc', 10); assert cnt['abc'] == 10")
# });
# }
@ -427,14 +519,14 @@ struct MyDict {
impl MyDict {
#[new]
#[pyo3(signature = (*args, **kwargs))]
fn new(args: &PyAny, kwargs: Option<&PyAny>) -> Self {
fn new(args: &Bound<'_, PyAny>, kwargs: Option<&Bound<'_, PyAny>>) -> Self {
Self { private: 0 }
}
// some custom methods that use `private` here...
}
# Python::with_gil(|py| {
# let cls = py.get_type::<MyDict>();
# let cls = py.get_type_bound::<MyDict>();
# pyo3::py_run!(py, cls, "cls(a=1, b=2)")
# });
# }
@ -628,7 +720,7 @@ This is the equivalent of the Python decorator `@classmethod`.
#[pymethods]
impl MyClass {
#[classmethod]
fn cls_method(cls: &PyType) -> PyResult<i32> {
fn cls_method(cls: &Bound<'_, PyType>) -> PyResult<i32> {
Ok(10)
}
}
@ -638,7 +730,7 @@ Declares a class method callable from Python.
* The first parameter is the type object of the class on which the method is called.
This may be the type object of a derived class.
* The first parameter implicitly has type `&PyType`.
* The first parameter implicitly has type `&Bound<'_, PyType>`.
* For details on `parameter-list`, see the documentation of `Method arguments` section.
* The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPy<PyObject>`.
@ -646,6 +738,7 @@ Declares a class method callable from Python.
To create a constructor which takes a positional class argument, you can combine the `#[classmethod]` and `#[new]` modifiers:
```rust
# #![allow(dead_code)]
# use pyo3::prelude::*;
# use pyo3::types::PyType;
# #[pyclass]
@ -655,10 +748,10 @@ To create a constructor which takes a positional class argument, you can combine
impl BaseClass {
#[new]
#[classmethod]
fn py_new<'p>(cls: &'p PyType, py: Python<'p>) -> PyResult<Self> {
fn py_new(cls: &Bound<'_, PyType>) -> PyResult<Self> {
// Get an abstract attribute (presumably) declared on a subclass of this class.
let subclass_attr = cls.getattr("a_class_attr")?;
Ok(Self(subclass_attr.to_object(py)))
let subclass_attr: Bound<'_, PyAny> = cls.getattr("a_class_attr")?;
Ok(Self(subclass_attr.unbind()))
}
}
```
@ -703,7 +796,7 @@ impl MyClass {
}
Python::with_gil(|py| {
let my_class = py.get_type::<MyClass>();
let my_class = py.get_type_bound::<MyClass>();
pyo3::py_run!(py, my_class, "assert my_class.my_attribute == 'hello'")
});
```
@ -737,23 +830,23 @@ struct MyClass {
my_field: i32,
}
// Take a GIL-bound reference when the underlying `PyCell` is irrelevant.
// Take a reference when the underlying `Bound` is irrelevant.
#[pyfunction]
fn increment_field(my_class: &mut MyClass) {
my_class.my_field += 1;
}
// Take a GIL-bound reference wrapper when borrowing should be automatic,
// but interaction with the underlying `PyCell` is desired.
// Take a reference wrapper when borrowing should be automatic,
// but interaction with the underlying `Bound` is desired.
#[pyfunction]
fn print_field(my_class: PyRef<'_, MyClass>) {
println!("{}", my_class.my_field);
}
// Take a GIL-bound reference to the underlying cell
// Take a reference to the underlying Bound
// when borrowing needs to be managed manually.
#[pyfunction]
fn increment_then_print_field(my_class: &PyCell<MyClass>) {
fn increment_then_print_field(my_class: &Bound<'_, MyClass>) {
my_class.borrow_mut().my_field += 1;
println!("{}", my_class.borrow().my_field);
@ -812,9 +905,9 @@ impl MyClass {
fn method(
&mut self,
num: i32,
py_args: &PyTuple,
py_args: &Bound<'_, PyTuple>,
name: &str,
py_kwargs: Option<&PyDict>,
py_kwargs: Option<&Bound<'_, PyDict>>,
) -> String {
let num_before = self.num;
self.num = num;
@ -837,9 +930,7 @@ py_args=('World', 666), py_kwargs=Some({'x': 44, 'y': 55}), name=Hello, num=44,
py_args=(), py_kwargs=None, name=World, num=-1, num_before=44
```
## Making class method signatures available to Python
The [`text_signature = "..."`](./function.md#text_signature) option for `#[pyfunction]` also works for `#[pymethods]`:
The [`#[pyo3(text_signature = "...")`](./function/signature.md#overriding-the-generated-signature) option for `#[pyfunction]` also works for `#[pymethods]`.
```rust
# #![allow(dead_code)]
@ -864,7 +955,7 @@ impl MyClass {
// similarly for classmethod arguments, use $cls
#[classmethod]
#[pyo3(text_signature = "($cls, e, f)")]
fn my_class_method(cls: &PyType, e: i32, f: i32) -> i32 {
fn my_class_method(cls: &Bound<'_, PyType>, e: i32, f: i32) -> i32 {
e + f
}
#[staticmethod]
@ -876,8 +967,8 @@ impl MyClass {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
# let module = PyModule::new(py, "my_module")?;
# let inspect = PyModule::import_bound(py, "inspect")?.getattr("signature")?;
# let module = PyModule::new_bound(py, "my_module")?;
# module.add_class::<MyClass>()?;
# let class = module.getattr("MyClass")?;
#
@ -886,7 +977,7 @@ impl MyClass {
# assert_eq!(doc, "");
#
# let sig: String = inspect
# .call1((class,))?
# .call1((&class,))?
# .call_method0("__str__")?
# .extract()?;
# assert_eq!(sig, "(c, d)");
@ -894,7 +985,7 @@ impl MyClass {
# let doc: String = class.getattr("__doc__")?.extract()?;
# assert_eq!(doc, "");
#
# inspect.call1((class,)).expect_err("`text_signature` on classes is not compatible with compilation in `abi3` mode until Python 3.10 or greater");
# inspect.call1((&class,)).expect_err("`text_signature` on classes is not compatible with compilation in `abi3` mode until Python 3.10 or greater");
# }
#
# {
@ -941,13 +1032,58 @@ impl MyClass {
Note that `text_signature` on `#[new]` is not compatible with compilation in
`abi3` mode until Python 3.10 or greater.
## #[pyclass] enums
### Method receivers and lifetime elision
Currently PyO3 only supports fieldless enums. PyO3 adds a class attribute for each variant, so you can access them in Python without defining `#[new]`. PyO3 also provides default implementations of `__richcmp__` and `__int__`, so they can be compared using `==`:
PyO3 supports writing instance methods using the normal method receivers for shared `&self` and unique `&mut self` references. This interacts with [lifetime elision][lifetime-elision] insofar as the lifetime of a such a receiver is assigned to all elided output lifetime parameters.
This is a good default for general Rust code where return values are more likely to borrow from the receiver than from the other arguments, if they contain any lifetimes at all. However, when returning bound references `Bound<'py, T>` in PyO3-based code, the GIL lifetime `'py` should usually be derived from a GIL token `py: Python<'py>` passed as an argument instead of the receiver.
Specifically, signatures like
```rust,ignore
fn frobnicate(&self, py: Python) -> Bound<Foo>;
```
will not work as they are inferred as
```rust,ignore
fn frobnicate<'a, 'py>(&'a self, py: Python<'py>) -> Bound<'a, Foo>;
```
instead of the intended
```rust,ignore
fn frobnicate<'a, 'py>(&'a self, py: Python<'py>) -> Bound<'py, Foo>;
```
and should usually be written as
```rust,ignore
fn frobnicate<'py>(&self, py: Python<'py>) -> Bound<'py, Foo>;
```
The same problem does not exist for `#[pyfunction]`s as the special case for receiver lifetimes does not apply and indeed a signature like
```rust,ignore
fn frobnicate(bar: &Bar, py: Python) -> Bound<Foo>;
```
will yield compiler error [E0106 "missing lifetime specifier"][compiler-error-e0106].
## `#[pyclass]` enums
Enum support in PyO3 comes in two flavors, depending on what kind of variants the enum has: simple and complex.
### Simple enums
A simple enum (a.k.a. C-like enum) has only unit variants.
PyO3 adds a class attribute for each variant, so you can access them in Python without defining `#[new]`. PyO3 also provides default implementations of `__richcmp__` and `__int__`, so they can be compared using `==`:
```rust
# use pyo3::prelude::*;
#[pyclass]
#[pyclass(eq, eq_int)]
#[derive(PartialEq)]
enum MyEnum {
Variant,
OtherVariant,
@ -956,7 +1092,7 @@ enum MyEnum {
Python::with_gil(|py| {
let x = Py::new(py, MyEnum::Variant).unwrap();
let y = Py::new(py, MyEnum::OtherVariant).unwrap();
let cls = py.get_type::<MyEnum>();
let cls = py.get_type_bound::<MyEnum>();
pyo3::py_run!(py, x y cls, r#"
assert x == cls.Variant
assert y == cls.OtherVariant
@ -965,24 +1101,23 @@ Python::with_gil(|py| {
})
```
You can also convert your enums into `int`:
You can also convert your simple enums into `int`:
```rust
# use pyo3::prelude::*;
#[pyclass]
#[pyclass(eq, eq_int)]
#[derive(PartialEq)]
enum MyEnum {
Variant,
OtherVariant = 10,
}
Python::with_gil(|py| {
let cls = py.get_type::<MyEnum>();
let cls = py.get_type_bound::<MyEnum>();
let x = MyEnum::Variant as i32; // The exact value is assigned by the compiler.
pyo3::py_run!(py, cls x, r#"
assert int(cls.Variant) == x
assert int(cls.OtherVariant) == 10
assert cls.OtherVariant == 10 # You can also compare against int.
assert 10 == cls.OtherVariant
"#)
})
```
@ -991,14 +1126,15 @@ PyO3 also provides `__repr__` for enums:
```rust
# use pyo3::prelude::*;
#[pyclass]
#[pyclass(eq, eq_int)]
#[derive(PartialEq)]
enum MyEnum{
Variant,
OtherVariant,
}
Python::with_gil(|py| {
let cls = py.get_type::<MyEnum>();
let cls = py.get_type_bound::<MyEnum>();
let x = Py::new(py, MyEnum::Variant).unwrap();
pyo3::py_run!(py, cls x, r#"
assert repr(x) == 'MyEnum.Variant'
@ -1011,7 +1147,8 @@ All methods defined by PyO3 can be overridden. For example here's how you overri
```rust
# use pyo3::prelude::*;
#[pyclass]
#[pyclass(eq, eq_int)]
#[derive(PartialEq)]
enum MyEnum {
Answer = 42,
}
@ -1024,7 +1161,7 @@ impl MyEnum {
}
Python::with_gil(|py| {
let cls = py.get_type::<MyEnum>();
let cls = py.get_type_bound::<MyEnum>();
pyo3::py_run!(py, cls, "assert repr(cls.Answer) == '42'")
})
```
@ -1033,7 +1170,8 @@ Enums and their variants can also be renamed using `#[pyo3(name)]`.
```rust
# use pyo3::prelude::*;
#[pyclass(name = "RenamedEnum")]
#[pyclass(eq, eq_int, name = "RenamedEnum")]
#[derive(PartialEq)]
enum MyEnum {
#[pyo3(name = "UPPERCASE")]
Variant,
@ -1041,7 +1179,7 @@ enum MyEnum {
Python::with_gil(|py| {
let x = Py::new(py, MyEnum::Variant).unwrap();
let cls = py.get_type::<MyEnum>();
let cls = py.get_type_bound::<MyEnum>();
pyo3::py_run!(py, x cls, r#"
assert repr(x) == 'RenamedEnum.UPPERCASE'
assert x == cls.UPPERCASE
@ -1049,6 +1187,32 @@ Python::with_gil(|py| {
})
```
Ordering of enum variants is optionally added using `#[pyo3(ord)]`.
*Note: Implementation of the `PartialOrd` trait is required when passing the `ord` argument. If not implemented, a compile time error is raised.*
```rust
# use pyo3::prelude::*;
#[pyclass(eq, ord)]
#[derive(PartialEq, PartialOrd)]
enum MyEnum{
A,
B,
C,
}
Python::with_gil(|py| {
let cls = py.get_type_bound::<MyEnum>();
let a = Py::new(py, MyEnum::A).unwrap();
let b = Py::new(py, MyEnum::B).unwrap();
let c = Py::new(py, MyEnum::C).unwrap();
pyo3::py_run!(py, cls a b c, r#"
assert (a < b) == True
assert (c <= b) == False
assert (c > a) == True
"#)
})
```
You may not use enums as a base class or let enums inherit from other classes.
```rust,compile_fail
@ -1073,6 +1237,115 @@ enum BadSubclass {
`#[pyclass]` enums are currently not interoperable with `IntEnum` in Python.
### Complex enums
An enum is complex if it has any non-unit (struct or tuple) variants.
PyO3 supports only struct and tuple variants in a complex enum. Unit variants aren't supported at present (the recommendation is to use an empty tuple enum instead).
PyO3 adds a class attribute for each variant, which may be used to construct values and in match patterns. PyO3 also provides getter methods for all fields of each variant.
```rust
# use pyo3::prelude::*;
#[pyclass]
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
RegularPolygon(u32, f64),
Nothing { },
}
# #[cfg(Py_3_10)]
Python::with_gil(|py| {
let circle = Shape::Circle { radius: 10.0 }.into_py(py);
let square = Shape::RegularPolygon(4, 10.0).into_py(py);
let cls = py.get_type_bound::<Shape>();
pyo3::py_run!(py, circle square cls, r#"
assert isinstance(circle, cls)
assert isinstance(circle, cls.Circle)
assert circle.radius == 10.0
assert isinstance(square, cls)
assert isinstance(square, cls.RegularPolygon)
assert square[0] == 4 # Gets _0 field
assert square[1] == 10.0 # Gets _1 field
def count_vertices(cls, shape):
match shape:
case cls.Circle():
return 0
case cls.Rectangle():
return 4
case cls.RegularPolygon(n):
return n
case cls.Nothing():
return 0
assert count_vertices(cls, circle) == 0
assert count_vertices(cls, square) == 4
"#)
})
```
WARNING: `Py::new` and `.into_py` are currently inconsistent. Note how the constructed value is _not_ an instance of the specific variant. For this reason, constructing values is only recommended using `.into_py`.
```rust
# use pyo3::prelude::*;
#[pyclass]
enum MyEnum {
Variant { i: i32 },
}
Python::with_gil(|py| {
let x = Py::new(py, MyEnum::Variant { i: 42 }).unwrap();
let cls = py.get_type_bound::<MyEnum>();
pyo3::py_run!(py, x cls, r#"
assert isinstance(x, cls)
assert not isinstance(x, cls.Variant)
"#)
})
```
The constructor of each generated class can be customized using the `#[pyo3(constructor = (...))]` attribute. This uses the same syntax as the [`#[pyo3(signature = (...))]`](function/signature.md)
attribute on function and methods and supports the same options. To apply this attribute simply place it on top of a variant in a `#[pyclass]` complex enum as shown below:
```rust
# use pyo3::prelude::*;
#[pyclass]
enum Shape {
#[pyo3(constructor = (radius=1.0))]
Circle { radius: f64 },
#[pyo3(constructor = (*, width, height))]
Rectangle { width: f64, height: f64 },
#[pyo3(constructor = (side_count, radius=1.0))]
RegularPolygon { side_count: u32, radius: f64 },
Nothing { },
}
# #[cfg(Py_3_10)]
Python::with_gil(|py| {
let cls = py.get_type_bound::<Shape>();
pyo3::py_run!(py, cls, r#"
circle = cls.Circle()
assert isinstance(circle, cls)
assert isinstance(circle, cls.Circle)
assert circle.radius == 1.0
square = cls.Rectangle(width = 1, height = 1)
assert isinstance(square, cls)
assert isinstance(square, cls.Rectangle)
assert square.width == 1
assert square.height == 1
hexagon = cls.RegularPolygon(6)
assert isinstance(hexagon, cls)
assert isinstance(hexagon, cls.RegularPolygon)
assert hexagon.side_count == 6
assert hexagon.radius == 1
"#)
})
```
## Implementation details
The `#[pyclass]` macros rely on a lot of conditional code generation: each `#[pyclass]` can optionally have a `#[pymethods]` block.
@ -1091,8 +1364,15 @@ struct MyClass {
# #[allow(dead_code)]
num: i32,
}
unsafe impl pyo3::type_object::PyTypeInfo for MyClass {
impl pyo3::types::DerefToPyAny for MyClass {}
# #[allow(deprecated)]
# #[cfg(feature = "gil-refs")]
unsafe impl pyo3::type_object::HasPyGilRef for MyClass {
type AsRefTarget = pyo3::PyCell<Self>;
}
unsafe impl pyo3::type_object::PyTypeInfo for MyClass {
const NAME: &'static str = "MyClass";
const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::None;
#[inline]
@ -1112,7 +1392,7 @@ impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a
type Holder = ::std::option::Option<pyo3::PyRef<'py, MyClass>>;
#[inline]
fn extract(obj: &'py pyo3::PyAny, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder)
}
}
@ -1122,7 +1402,7 @@ impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a
type Holder = ::std::option::Option<pyo3::PyRefMut<'py, MyClass>>;
#[inline]
fn extract(obj: &'py pyo3::PyAny, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder)
}
}
@ -1136,6 +1416,8 @@ impl pyo3::IntoPy<PyObject> for MyClass {
impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
const IS_BASETYPE: bool = false;
const IS_SUBCLASS: bool = false;
const IS_MAPPING: bool = false;
const IS_SEQUENCE: bool = false;
type BaseType = PyAny;
type ThreadChecker = pyo3::impl_::pyclass::SendablePyClass<MyClass>;
type PyClassMutability = <<pyo3::PyAny as pyo3::impl_::pyclass::PyClassBaseType>::PyClassMutability as pyo3::impl_::pycell::PyClassMutability>::MutableChild;
@ -1158,27 +1440,26 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
fn doc(py: Python<'_>) -> pyo3::PyResult<&'static ::std::ffi::CStr> {
use pyo3::impl_::pyclass::*;
static DOC: pyo3::once_cell::GILOnceCell<::std::borrow::Cow<'static, ::std::ffi::CStr>> = pyo3::once_cell::GILOnceCell::new();
static DOC: pyo3::sync::GILOnceCell<::std::borrow::Cow<'static, ::std::ffi::CStr>> = pyo3::sync::GILOnceCell::new();
DOC.get_or_try_init(py, || {
let collector = PyClassImplCollector::<Self>::new();
build_pyclass_doc(<MyClass as pyo3::PyTypeInfo>::NAME, "", None.or_else(|| collector.new_text_signature()))
build_pyclass_doc(<MyClass as pyo3::PyTypeInfo>::NAME, pyo3::ffi::c_str!(""), collector.new_text_signature())
}).map(::std::ops::Deref::deref)
}
}
# Python::with_gil(|py| {
# let cls = py.get_type::<MyClass>();
# let cls = py.get_type_bound::<MyClass>();
# pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'")
# });
# }
```
[`GILGuard`]: {{#PYO3_DOCS_URL}}/pyo3/struct.GILGuard.html
[`PyTypeInfo`]: {{#PYO3_DOCS_URL}}/pyo3/type_object/trait.PyTypeInfo.html
[`Py`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Py.html
[`PyCell`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyCell.html
[`Bound<'_, T>`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Bound.html
[`PyClass`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/trait.PyClass.html
[`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html
[`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRefMut.html
@ -1190,3 +1471,6 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
[classattr]: https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables
[`multiple-pymethods`]: features.md#multiple-pymethods
[lifetime-elision]: https://doc.rust-lang.org/reference/lifetime-elision.html
[compiler-error-e0106]: https://doc.rust-lang.org/error_codes/E0106.html

View File

@ -75,8 +75,8 @@ A [previous implementation] used a normal `u64`, which meant it required a `&mut
fn __call__(
&mut self,
py: Python<'_>,
args: &PyTuple,
kwargs: Option<&PyDict>,
args: &Bound<'_, PyTuple>,
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult<Py<PyAny>> {
self.count += 1;
let name = self.wraps.getattr(py, "__name__")?;

View File

@ -35,7 +35,7 @@ and cast it to an `i32`.
# #![allow(dead_code)]
use pyo3::prelude::*;
fn wrap(obj: &PyAny) -> Result<i32, PyErr> {
fn wrap(obj: &Bound<'_, PyAny>) -> PyResult<i32> {
let val = obj.call_method1("__and__", (0xFFFFFFFF_u32,))?;
let val: u32 = val.extract()?;
// 👇 This intentionally overflows!
@ -48,7 +48,7 @@ We also add documentation, via `///` comments, which are visible to Python users
# #![allow(dead_code)]
use pyo3::prelude::*;
fn wrap(obj: &PyAny) -> Result<i32, PyErr> {
fn wrap(obj: &Bound<'_, PyAny>) -> PyResult<i32> {
let val = obj.call_method1("__and__", (0xFFFFFFFF_u32,))?;
let val: u32 = val.extract()?;
Ok(val as i32)
@ -170,8 +170,8 @@ impl Number {
self.0 as f64
}
fn __complex__<'py>(&self, py: Python<'py>) -> &'py PyComplex {
PyComplex::from_doubles(py, self.0 as f64, 0.0)
fn __complex__<'py>(&self, py: Python<'py>) -> Bound<'py, PyComplex> {
PyComplex::from_doubles_bound(py, self.0 as f64, 0.0)
}
}
```
@ -206,14 +206,13 @@ assert hash_djb2('l50_50') == Number(-1152549421)
```rust
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::convert::TryInto;
use pyo3::exceptions::{PyValueError, PyZeroDivisionError};
use pyo3::prelude::*;
use pyo3::class::basic::CompareOp;
use pyo3::types::PyComplex;
use pyo3::types::{PyComplex, PyString};
fn wrap(obj: &PyAny) -> Result<i32, PyErr> {
fn wrap(obj: &Bound<'_, PyAny>) -> PyResult<i32> {
let val = obj.call_method1("__and__", (0xFFFFFFFF_u32,))?;
let val: u32 = val.extract()?;
Ok(val as i32)
@ -230,9 +229,9 @@ impl Number {
Self(value)
}
fn __repr__(slf: &PyCell<Self>) -> PyResult<String> {
fn __repr__(slf: &Bound<'_, Self>) -> PyResult<String> {
// Get the class name dynamically in case `Number` is subclassed
let class_name: &str = slf.get_type().name()?;
let class_name: Bound<'_, PyString> = slf.get_type().qualname()?;
Ok(format!("{}({})", class_name, slf.borrow().0))
}
@ -321,13 +320,13 @@ impl Number {
self.0 as f64
}
fn __complex__<'py>(&self, py: Python<'py>) -> &'py PyComplex {
PyComplex::from_doubles(py, self.0 as f64, 0.0)
fn __complex__<'py>(&self, py: Python<'py>) -> Bound<'py, PyComplex> {
PyComplex::from_doubles_bound(py, self.0 as f64, 0.0)
}
}
#[pymodule]
fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Number>()?;
Ok(())
}
@ -387,10 +386,10 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let globals = PyModule::import(py, "__main__")?.dict();
# globals.set_item("Number", Number::type_object(py))?;
# let globals = PyModule::import_bound(py, "__main__")?.dict();
# globals.set_item("Number", Number::type_object_bound(py))?;
#
# py.run(SCRIPT, Some(globals), None)?;
# py.run_bound(SCRIPT, Some(&globals), None)?;
# Ok(())
# })
# }
@ -412,8 +411,8 @@ the contracts of this function. Let's review those contracts:
- The GIL must be held. If it's 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(&PyAny) -> PyResult<T>`.
- `&PyAny` represents a checked borrowed reference, so the pointer derived from it is valid (and not null).
Let's create that helper function. The signature has to be `fn(&Bound<'_, PyAny>) -> PyResult<T>`.
- `&Bound<'_, PyAny>` represents a checked borrowed reference, so the pointer derived from it is valid (and not null).
- Whenever we have borrowed references to Python objects in scope, it is guaranteed that the GIL is held. This reference is also where we can get a [`Python`] token to use in our call to [`PyErr::take`].
```rust
@ -422,7 +421,7 @@ use std::os::raw::c_ulong;
use pyo3::prelude::*;
use pyo3::ffi;
fn wrap(obj: &PyAny) -> Result<i32, PyErr> {
fn wrap(obj: &Bound<'_, PyAny>) -> Result<i32, PyErr> {
let py: Python<'_> = obj.py();
unsafe {
@ -441,6 +440,6 @@ fn wrap(obj: &PyAny) -> Result<i32, PyErr> {
```
[`PyErr::take`]: {{#PYO3_DOCS_URL}}/pyo3/prelude/struct.PyErr.html#method.take
[`Python`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html
[`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

View File

@ -3,6 +3,7 @@
Recall the `Number` class from the previous chapter:
```rust
# #![allow(dead_code)]
use pyo3::prelude::*;
#[pyclass]
@ -17,7 +18,7 @@ impl Number {
}
#[pymodule]
fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Number>()?;
Ok(())
}
@ -75,19 +76,20 @@ 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 `PyCell` as the `self` argument.
*and* the Rust struct, we need to use a `Bound` as the `self` argument.
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyString;
#
# #[pyclass]
# struct Number(i32);
#
#[pymethods]
impl Number {
fn __repr__(slf: &PyCell<Self>) -> PyResult<String> {
fn __repr__(slf: &Bound<'_, Self>) -> PyResult<String> {
// This is the equivalent of `self.__class__.__name__` in Python.
let class_name: &str = slf.get_type().name()?;
let class_name: Bound<'_, PyString> = slf.get_type().qualname()?;
// To access fields of the Rust struct, we need to borrow the `PyCell`.
Ok(format!("{}({})", class_name, slf.borrow().0))
}
@ -120,6 +122,19 @@ impl Number {
}
}
```
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
# use pyo3::prelude::*;
#
#[pyclass(frozen, eq, hash)]
#[derive(PartialEq, Hash)]
struct Number(i32);
```
> **Note**: When implementing `__hash__` and comparisons, it is important that the following property holds:
>
@ -216,8 +231,8 @@ impl Number {
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let x = PyCell::new(py, Number(4))?;
# let y = PyCell::new(py, Number(4))?;
# let x = &Bound::new(py, Number(4))?;
# let y = &Bound::new(py, Number(4))?;
# assert!(x.eq(y)?);
# assert!(!x.ne(y)?);
# Ok(())
@ -225,6 +240,26 @@ impl Number {
# }
```
To implement `__eq__` using the Rust [`PartialEq`] trait implementation, the `eq` option can be used.
```rust
# use pyo3::prelude::*;
#
#[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
# use pyo3::prelude::*;
#
#[pyclass(eq, ord)]
#[derive(PartialEq, PartialOrd)]
struct Number(i32);
```
### Truthyness
We'll consider `Number` to be `True` if it is nonzero:
@ -251,6 +286,7 @@ use std::hash::{Hash, Hasher};
use pyo3::prelude::*;
use pyo3::class::basic::CompareOp;
use pyo3::types::PyString;
#[pyclass]
struct Number(i32);
@ -262,8 +298,8 @@ impl Number {
Self(value)
}
fn __repr__(slf: &PyCell<Self>) -> PyResult<String> {
let class_name: &str = slf.get_type().name()?;
fn __repr__(slf: &Bound<'_, Self>) -> PyResult<String> {
let class_name: Bound<'_, PyString> = slf.get_type().qualname()?;
Ok(format!("{}({})", class_name, slf.borrow().0))
}
@ -294,7 +330,7 @@ impl Number {
}
#[pymodule]
fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Number>()?;
Ok(())
}
@ -304,3 +340,4 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
[`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

View File

@ -1,23 +1,30 @@
# Magic methods and slots
# Class customizations
Python's object model defines several protocols for different object behavior, such as the sequence, mapping, and number protocols. You may be familiar with implementing these protocols in Python classes by "magic" methods, such as `__str__` or `__repr__`. Because of the double-underscores surrounding their name, these are also known as "dunder" methods.
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.
In the Python C-API which PyO3 is implemented upon, many of these magic methods have to be placed into special "slots" on the class type object, as covered in the previous section.
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__` and `__init__` are 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 function name in `#[pymethods]` is a recognised magic method, 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.
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.
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 the subset which have slots as [defined here](https://docs.python.org/3/c-api/typeobj.html). Some of the slots do not have a magic method in Python, which leads to a few additional magic methods defined only in PyO3:
- Magic methods for garbage collection
- Magic methods for the buffer protocol
## 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 of all magic methods PyO3 currently handles. The
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 `<self>`. It can be
`&self`, `&mut self` or a `PyCell` reference like `self_: PyRef<'_, Self>` and
`&self`, `&mut self` or a `Bound` reference like `self_: PyRef<'_, Self>` and
`self_: PyRefMut<'_, Self>`, as described [here](../class.md#inheritance).
- An optional `Python<'py>` argument is always allowed as the first argument.
- Return values can be optionally wrapped in `PyResult`.
@ -31,7 +38,6 @@ given signatures should be interpreted as follows:
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__(<self>) -> object (str)`
@ -207,7 +213,7 @@ impl Container {
# Python::with_gil(|py| {
# let container = Container { iter: vec![1, 2, 3, 4] };
# let inst = pyo3::PyCell::new(py, container).unwrap();
# 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]");
# });
@ -450,7 +456,7 @@ Usually, an implementation of `__traverse__` should do nothing but calls to `vis
Most importantly, safe access to the GIL is prohibited inside implementations of `__traverse__`,
i.e. `Python::with_gil` will panic.
> Note: these methods are part of the C API, PyPy does not necessarily honor them. If you are building for PyPy you should measure memory consumption to make sure you do not have runaway memory growth. See [this issue on the PyPy bug tracker](https://foss.heptapod.net/pypy/pypy/-/issues/3899).
> Note: these methods are part of the C API, PyPy does not necessarily honor them. If you are building for PyPy you should measure memory consumption to make sure you do not have runaway memory growth. See [this issue on the PyPy bug tracker](https://github.com/pypy/pypy/issues/3848).
[`IterNextOutput`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/enum.IterNextOutput.html
[`PySequence`]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.PySequence.html

View File

@ -12,47 +12,48 @@ The table below contains the Python type and the corresponding function argument
| Python | Rust | Rust (Python-native) |
| ------------- |:-------------------------------:|:--------------------:|
| `object` | - | `&PyAny` |
| `str` | `String`, `Cow<str>`, `&str`, `OsString`, `PathBuf`, `Path` | `&PyString`, `&PyUnicode` |
| `bytes` | `Vec<u8>`, `&[u8]`, `Cow<[u8]>` | `&PyBytes` |
| `bool` | `bool` | `&PyBool` |
| `int` | `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`, `num_bigint::BigInt`[^1], `num_bigint::BigUint`[^1] | `&PyLong` |
| `float` | `f32`, `f64` | `&PyFloat` |
| `complex` | `num_complex::Complex`[^2] | `&PyComplex` |
| `list[T]` | `Vec<T>` | `&PyList` |
| `dict[K, V]` | `HashMap<K, V>`, `BTreeMap<K, V>`, `hashbrown::HashMap<K, V>`[^3], `indexmap::IndexMap<K, V>`[^4] | `&PyDict` |
| `tuple[T, U]` | `(T, U)`, `Vec<T>` | `&PyTuple` |
| `set[T]` | `HashSet<T>`, `BTreeSet<T>`, `hashbrown::HashSet<T>`[^3] | `&PySet` |
| `frozenset[T]` | `HashSet<T>`, `BTreeSet<T>`, `hashbrown::HashSet<T>`[^3] | `&PyFrozenSet` |
| `bytearray` | `Vec<u8>`, `Cow<[u8]>` | `&PyByteArray` |
| `slice` | - | `&PySlice` |
| `type` | - | `&PyType` |
| `module` | - | `&PyModule` |
| `object` | - | `PyAny` |
| `str` | `String`, `Cow<str>`, `&str`, `char`, `OsString`, `PathBuf`, `Path` | `PyString`, `PyUnicode` |
| `bytes` | `Vec<u8>`, `&[u8]`, `Cow<[u8]>` | `PyBytes` |
| `bool` | `bool` | `PyBool` |
| `int` | `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`, `num_bigint::BigInt`[^1], `num_bigint::BigUint`[^1] | `PyLong` |
| `float` | `f32`, `f64` | `PyFloat` |
| `complex` | `num_complex::Complex`[^2] | `PyComplex` |
| `fractions.Fraction`| `num_rational::Ratio`[^8] | - |
| `list[T]` | `Vec<T>` | `PyList` |
| `dict[K, V]` | `HashMap<K, V>`, `BTreeMap<K, V>`, `hashbrown::HashMap<K, V>`[^3], `indexmap::IndexMap<K, V>`[^4] | `PyDict` |
| `tuple[T, U]` | `(T, U)`, `Vec<T>` | `PyTuple` |
| `set[T]` | `HashSet<T>`, `BTreeSet<T>`, `hashbrown::HashSet<T>`[^3] | `PySet` |
| `frozenset[T]` | `HashSet<T>`, `BTreeSet<T>`, `hashbrown::HashSet<T>`[^3] | `PyFrozenSet` |
| `bytearray` | `Vec<u8>`, `Cow<[u8]>` | `PyByteArray` |
| `slice` | - | `PySlice` |
| `type` | - | `PyType` |
| `module` | - | `PyModule` |
| `collections.abc.Buffer` | - | `PyBuffer<T>` |
| `datetime.datetime` | - | `&PyDateTime` |
| `datetime.date` | - | `&PyDate` |
| `datetime.time` | - | `&PyTime` |
| `datetime.tzinfo` | - | `&PyTzInfo` |
| `datetime.timedelta` | - | `&PyDelta` |
| `decimal.Decimal` | `rust_decimal::Decimal`[^5] | - |
| `datetime.datetime` | `SystemTime`, `chrono::DateTime<Tz>`[^5], `chrono::NaiveDateTime`[^5] | `PyDateTime` |
| `datetime.date` | `chrono::NaiveDate`[^5] | `PyDate` |
| `datetime.time` | `chrono::NaiveTime`[^5] | `PyTime` |
| `datetime.tzinfo` | `chrono::FixedOffset`[^5], `chrono::Utc`[^5], `chrono_tz::TimeZone`[^6] | `PyTzInfo` |
| `datetime.timedelta` | `Duration`, `chrono::Duration`[^5] | `PyDelta` |
| `decimal.Decimal` | `rust_decimal::Decimal`[^7] | - |
| `ipaddress.IPv4Address` | `std::net::IpAddr`, `std::net::IpV4Addr` | - |
| `ipaddress.IPv6Address` | `std::net::IpAddr`, `std::net::IpV6Addr` | - |
| `os.PathLike ` | `PathBuf`, `Path` | `&PyString`, `&PyUnicode` |
| `pathlib.Path` | `PathBuf`, `Path` | `&PyString`, `&PyUnicode` |
| `os.PathLike ` | `PathBuf`, `Path` | `PyString`, `PyUnicode` |
| `pathlib.Path` | `PathBuf`, `Path` | `PyString`, `PyUnicode` |
| `typing.Optional[T]` | `Option<T>` | - |
| `typing.Sequence[T]` | `Vec<T>` | `&PySequence` |
| `typing.Sequence[T]` | `Vec<T>` | `PySequence` |
| `typing.Mapping[K, V]` | `HashMap<K, V>`, `BTreeMap<K, V>`, `hashbrown::HashMap<K, V>`[^3], `indexmap::IndexMap<K, V>`[^4] | `&PyMapping` |
| `typing.Iterator[Any]` | - | `&PyIterator` |
| `typing.Union[...]` | See [`#[derive(FromPyObject)]`](traits.html#deriving-a-hrefhttpsdocsrspyo3latestpyo3conversiontraitfrompyobjecthtmlfrompyobjecta-for-enums) | - |
| `typing.Iterator[Any]` | - | `PyIterator` |
| `typing.Union[...]` | See [`#[derive(FromPyObject)]`](traits.md#deriving-frompyobject-for-enums) | - |
There are also a few special types related to the GIL and Rust-defined `#[pyclass]`es which may come in useful:
It is also worth remembering the following special types:
| What | Description |
| ------------- | ------------------------------------- |
| `Python` | A GIL token, used to pass to PyO3 constructors to prove ownership of the GIL |
| ---------------- | ------------------------------------- |
| `Python<'py>` | A GIL token, used to pass to PyO3 constructors to prove ownership of the GIL. |
| `Bound<'py, T>` | A Python object connected to the GIL lifetime. This provides access to most of PyO3's APIs. |
| `Py<T>` | A Python object isolated from the GIL lifetime. This can be sent to other threads. |
| `PyObject` | An alias for `Py<PyAny>` |
| `&PyCell<T>` | A `#[pyclass]` value owned by Python. |
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
| `PyRefMut<T>` | A `#[pyclass]` borrowed mutably. |
@ -72,9 +73,9 @@ For most PyO3 usage the conversion cost is worth paying to get these benefits. A
### Returning Rust values to Python
When returning values from functions callable from Python, Python-native types (`&PyAny`, `&PyDict` etc.) can be used with zero cost.
When returning values from functions callable from Python, [PyO3's smart pointers](../types.md#pyo3s-smart-pointers) (`Py<T>`, `Bound<'py, T>`, and `Borrowed<'a, 'py, T>`) can be used with zero cost.
Because these types are references, in some situations the Rust compiler may ask for lifetime annotations. If this is the case, you should use `Py<PyAny>`, `Py<PyDict>` etc. instead - which are also zero-cost. For all of these Python-native types `T`, `Py<T>` can be created from `T` with an `.into()` conversion.
Because `Bound<'py, T>` and `Borrowed<'a, 'py, T>` have lifetime parameters, the Rust compiler may ask for lifetime annotations to be added to your function. See the [section of the guide dedicated to this](../types.md#function-argument-lifetimes).
If your function is fallible, it should return `PyResult<T>` or `Result<T, E>` where `E` implements `From<E> for PyErr`. This will raise a `Python` exception if the `Err` variant is returned.
@ -95,7 +96,8 @@ Finally, the following Rust types are also able to convert to Python as return v
| `BTreeMap<K, V>` | `Dict[K, V]` |
| `HashSet<T>` | `Set[T]` |
| `BTreeSet<T>` | `Set[T]` |
| `&PyCell<T: PyClass>` | `T` |
| `Py<T>` | `T` |
| `Bound<T>` | `T` |
| `PyRef<T: PyClass>` | `T` |
| `PyRefMut<T: PyClass>` | `T` |
@ -107,4 +109,10 @@ Finally, the following Rust types are also able to convert to Python as return v
[^4]: Requires the `indexmap` optional feature.
[^5]: Requires the `rust_decimal` optional feature.
[^5]: Requires the `chrono` optional feature.
[^6]: Requires the `chrono-tz` optional feature.
[^7]: Requires the `rust_decimal` optional feature.
[^8]: Requires the `num-rational` optional feature.

View File

@ -13,7 +13,7 @@ fails, so usually you will use something like
# use pyo3::types::PyList;
# fn main() -> PyResult<()> {
# Python::with_gil(|py| {
# let list = PyList::new(py, b"foo");
# let list = PyList::new_bound(py, b"foo");
let v: Vec<i32> = list.extract()?;
# assert_eq!(&v, &[102, 111, 111]);
# Ok(())
@ -54,7 +54,7 @@ struct RustyStruct {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let module = PyModule::from_code(
# let module = PyModule::from_code_bound(
# py,
# "class Foo:
# def __init__(self):
@ -86,7 +86,7 @@ struct RustyStruct {
# use pyo3::types::PyDict;
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let dict = PyDict::new(py);
# let dict = PyDict::new_bound(py);
# dict.set_item("my_string", "test")?;
#
# let rustystruct: RustyStruct = dict.extract()?;
@ -111,7 +111,7 @@ struct RustyStruct {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let module = PyModule::from_code(
# let module = PyModule::from_code_bound(
# py,
# "class Foo(dict):
# def __init__(self):
@ -155,7 +155,7 @@ struct RustyStruct {
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let py_dict = py.eval("{'foo': 'foo', 'bar': 'bar', 'foobar': 'foobar'}", None, None)?;
# let py_dict = py.eval_bound("{'foo': 'foo', 'bar': 'bar', 'foobar': 'foobar'}", None, None)?;
# let rustystruct: RustyStruct = py_dict.extract()?;
# assert_eq!(rustystruct.foo, "foo");
# assert_eq!(rustystruct.bar, "bar");
@ -181,7 +181,7 @@ struct RustyTuple(String, String);
# use pyo3::types::PyTuple;
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let tuple = PyTuple::new(py, vec!["test", "test2"]);
# let tuple = PyTuple::new_bound(py, vec!["test", "test2"]);
#
# let rustytuple: RustyTuple = tuple.extract()?;
# assert_eq!(rustytuple.0, "test");
@ -204,7 +204,7 @@ struct RustyTuple((String,));
# use pyo3::types::PyTuple;
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let tuple = PyTuple::new(py, vec!["test"]);
# let tuple = PyTuple::new_bound(py, vec!["test"]);
#
# let rustytuple: RustyTuple = tuple.extract()?;
# assert_eq!((rustytuple.0).0, "test");
@ -236,7 +236,7 @@ struct RustyTransparentStruct {
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let s = PyString::new(py, "test");
# let s = PyString::new_bound(py, "test");
#
# let tup: RustyTransparentTupleStruct = s.extract()?;
# assert_eq!(tup.0, "test");
@ -265,7 +265,7 @@ use pyo3::prelude::*;
#[derive(FromPyObject)]
# #[derive(Debug)]
enum RustyEnum<'a> {
enum RustyEnum<'py> {
Int(usize), // input is a positive int
String(String), // input is a string
IntTuple(usize, usize), // input is a 2-tuple with positive ints
@ -284,7 +284,7 @@ enum RustyEnum<'a> {
b: usize,
},
#[pyo3(transparent)]
CatchAll(&'a PyAny), // This extraction never fails
CatchAll(Bound<'py, PyAny>), // This extraction never fails
}
#
# use pyo3::types::{PyBytes, PyString};
@ -303,7 +303,7 @@ enum RustyEnum<'a> {
# );
# }
# {
# let thing = PyString::new(py, "text");
# let thing = PyString::new_bound(py, "text");
# let rust_thing: RustyEnum<'_> = thing.extract()?;
#
# assert_eq!(
@ -339,7 +339,7 @@ enum RustyEnum<'a> {
# );
# }
# {
# let module = PyModule::from_code(
# let module = PyModule::from_code_bound(
# py,
# "class Foo(dict):
# def __init__(self):
@ -364,7 +364,7 @@ enum RustyEnum<'a> {
# }
#
# {
# let module = PyModule::from_code(
# let module = PyModule::from_code_bound(
# py,
# "class Foo(dict):
# def __init__(self):
@ -388,13 +388,13 @@ enum RustyEnum<'a> {
# }
#
# {
# let thing = PyBytes::new(py, b"text");
# let thing = PyBytes::new_bound(py, b"text");
# let rust_thing: RustyEnum<'_> = thing.extract()?;
#
# assert_eq!(
# b"text",
# match rust_thing {
# RustyEnum::CatchAll(i) => i.downcast::<PyBytes>()?.as_bytes(),
# RustyEnum::CatchAll(ref i) => i.downcast::<PyBytes>()?.as_bytes(),
# other => unreachable!("Error extracting: {:?}", other),
# }
# );
@ -484,7 +484,7 @@ If the input is neither a string nor an integer, the error message will be:
- `pyo3(from_py_with = "...")`
- apply a custom function to convert the field from Python the desired Rust type.
- the argument must be the name of the function as a string.
- the function signature must be `fn(&PyAny) -> PyResult<T>` where `T` is the Rust type of the argument.
- the function signature must be `fn(&Bound<PyAny>) -> PyResult<T>` where `T` is the Rust type of the argument.
### `IntoPy<T>`
@ -499,7 +499,7 @@ _without_ having a unique python type.
```rust
use pyo3::prelude::*;
# #[allow(dead_code)]
struct MyPyObjectWrapper(PyObject);
impl IntoPy<PyObject> for MyPyObjectWrapper {

View File

@ -16,7 +16,7 @@ You can also debug classic `!`-macros by adding `-Z trace-macros`:
cargo rustc --profile=check -- -Z unstable-options --pretty=expanded -Z trace-macros > expanded.rs; rustfmt expanded.rs
```
See [cargo expand](https://github.com/dtolnay/cargo-expand) for a more elaborate version of those commands.
Note that those commands require using the nightly build of rust and may occasionally have bugs. See [cargo expand](https://github.com/dtolnay/cargo-expand) for a more elaborate and stable version of those commands.
## Running with Valgrind

View File

@ -1,5 +1,7 @@
# Using `async` and `await`
*`async`/`await` support is currently being integrated in PyO3. See the [dedicated documentation](../async-await.md)*
If you are working with a Python library that makes use of async functions or wish to provide
Python bindings for an async Rust library, [`pyo3-asyncio`](https://github.com/awestlake87/pyo3-asyncio)
likely has the tools you need. It provides conversions between async functions in both Python and
@ -118,7 +120,7 @@ Export an async function that makes use of `async-std`:
use pyo3::{prelude::*, wrap_pyfunction};
#[pyfunction]
fn rust_sleep(py: Python<'_>) -> PyResult<&PyAny> {
fn rust_sleep(py: Python<'_>) -> PyResult<&Bound<'_, PyAny>> {
pyo3_asyncio::async_std::future_into_py(py, async {
async_std::task::sleep(std::time::Duration::from_secs(1)).await;
Ok(Python::with_gil(|py| py.None()))
@ -126,10 +128,8 @@ fn rust_sleep(py: Python<'_>) -> PyResult<&PyAny> {
}
#[pymodule]
fn my_async_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(rust_sleep, m)?)?;
Ok(())
fn my_async_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(rust_sleep, m)?)
}
```
@ -141,7 +141,7 @@ If you want to use `tokio` instead, here's what your module should look like:
use pyo3::{prelude::*, wrap_pyfunction};
#[pyfunction]
fn rust_sleep(py: Python<'_>) -> PyResult<&PyAny> {
fn rust_sleep(py: Python<'_>) -> PyResult<&Bound<'_, PyAny>>> {
pyo3_asyncio::tokio::future_into_py(py, async {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
Ok(Python::with_gil(|py| py.None()))
@ -149,9 +149,8 @@ fn rust_sleep(py: Python<'_>) -> PyResult<&PyAny> {
}
#[pymodule]
fn my_async_module(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(rust_sleep, m)?)?;
Ok(())
fn my_async_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(rust_sleep, m)?)
}
```
@ -195,7 +194,7 @@ to do something special with the object that it returns.
Normally in Python, that something special is the `await` keyword, but in order to await this
coroutine in Rust, we first need to convert it into Rust's version of a `coroutine`: a `Future`.
That's where `pyo3-asyncio` comes in.
[`pyo3_asyncio::into_future`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/fn.into_future.html)
[`pyo3_asyncio::async_std::into_future`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/async_std/fn.into_future.html)
performs this conversion for us.
The following example uses `into_future` to call the `py_sleep` function shown above and then await the
@ -231,7 +230,7 @@ a coroutine argument:
```rust
#[pyfunction]
fn await_coro(coro: &PyAny) -> PyResult<()> {
fn await_coro(coro: &Bound<'_, PyAny>>) -> PyResult<()> {
// convert the coroutine into a Rust future using the
// async_std runtime
let f = pyo3_asyncio::async_std::into_future(coro)?;
@ -255,11 +254,11 @@ async def py_sleep():
await_coro(py_sleep())
```
If for you wanted to pass a callable function to the `#[pyfunction]` instead, (i.e. the last line becomes `await_coro(py_sleep))`, then the above example needs to be tweaked to first call the callable to get the coroutine:
If you wanted to pass a callable function to the `#[pyfunction]` instead, (i.e. the last line becomes `await_coro(py_sleep))`, then the above example needs to be tweaked to first call the callable to get the coroutine:
```rust
#[pyfunction]
fn await_coro(callable: &PyAny) -> PyResult<()> {
fn await_coro(callable: &Bound<'_, PyAny>>) -> PyResult<()> {
// get the coroutine by calling the callable
let coro = callable.call0()?;
@ -315,7 +314,7 @@ async fn rust_sleep() {
}
#[pyfunction]
fn call_rust_sleep(py: Python<'_>) -> PyResult<&PyAny> {
fn call_rust_sleep(py: Python<'_>) -> PyResult<&Bound<'_, PyAny>>> {
pyo3_asyncio::async_std::future_into_py(py, async move {
rust_sleep().await;
Ok(Python::with_gil(|py| py.None()))
@ -347,7 +346,7 @@ implementations _prefer_ control over the main thread, this can still make some
Because Python needs to control the main thread, we can't use the convenient proc macros from Rust
runtimes to handle the `main` function or `#[test]` functions. Instead, the initialization for PyO3 has to be done from the `main` function and the main
thread must block on [`pyo3_asyncio::run_forever`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/fn.run_forever.html) or [`pyo3_asyncio::async_std::run_until_complete`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/async_std/fn.run_until_complete.html).
thread must block on [`pyo3_asyncio::async_std::run_until_complete`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/async_std/fn.run_until_complete.html).
Because we have to block on one of those functions, we can't use [`#[async_std::main]`](https://docs.rs/async-std/latest/async_std/attr.main.html) or [`#[tokio::main]`](https://docs.rs/tokio/1.1.0/tokio/attr.main.html)
since it's not a good idea to make long blocking calls during an async function.
@ -465,7 +464,7 @@ tokio = "1.4"
use pyo3::{prelude::*, wrap_pyfunction};
#[pyfunction]
fn rust_sleep(py: Python<'_>) -> PyResult<&PyAny> {
fn rust_sleep(py: Python<'_>) -> PyResult<&Bound<'_, PyAny>>> {
pyo3_asyncio::tokio::future_into_py(py, async {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
Ok(Python::with_gil(|py| py.None()))
@ -473,7 +472,7 @@ fn rust_sleep(py: Python<'_>) -> PyResult<&PyAny> {
}
#[pymodule]
fn my_async_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn my_async_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(rust_sleep, m)?)?;
Ok(())

View File

@ -30,11 +30,11 @@ fn log_something() {
}
#[pymodule]
fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
// A good place to install the Rust -> Python logger.
pyo3_log::init();
m.add_function(wrap_pyfunction!(log_something))?;
m.add_function(wrap_pyfunction!(log_something, m)?)?;
Ok(())
}
```

View File

@ -24,7 +24,7 @@ use pyo3::exceptions::PyException;
create_exception!(mymodule, CustomError, PyException);
Python::with_gil(|py| {
let ctx = [("CustomError", py.get_type::<CustomError>())].into_py_dict(py);
let ctx = [("CustomError", py.get_type_bound::<CustomError>())].into_py_dict_bound(py);
pyo3::py_run!(
py,
*ctx,
@ -44,9 +44,9 @@ use pyo3::exceptions::PyException;
pyo3::create_exception!(mymodule, CustomError, PyException);
#[pymodule]
fn mymodule(py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn mymodule(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
// ... other elements added to module ...
m.add("CustomError", py.get_type::<CustomError>())?;
m.add("CustomError", py.get_type_bound::<CustomError>())?;
Ok(())
}
@ -54,7 +54,7 @@ fn mymodule(py: Python<'_>, m: &PyModule) -> PyResult<()> {
## Raising an exception
As described in the [function error handling](./function/error_handling.md) chapter, to raise an exception from a `#[pyfunction]` or `#[pymethods]`, return an `Err(PyErr)`. PyO3 will automatically raise this exception for you when returning the result to Python.
As described in the [function error handling](./function/error-handling.md) chapter, to raise an exception from a `#[pyfunction]` or `#[pymethods]`, return an `Err(PyErr)`. PyO3 will automatically raise this exception for you when returning the result to Python.
You can also manually write and fetch errors in the Python interpreter's global state:
@ -75,12 +75,12 @@ Python has an [`isinstance`](https://docs.python.org/3/library/functions.html#is
In PyO3 every object has the [`PyAny::is_instance`] and [`PyAny::is_instance_of`] methods which do the same thing.
```rust
use pyo3::Python;
use pyo3::prelude::*;
use pyo3::types::{PyBool, PyList};
Python::with_gil(|py| {
assert!(PyBool::new(py, true).is_instance_of::<PyBool>());
let list = PyList::new(py, &[1, 2, 3, 4]);
assert!(PyBool::new_bound(py, true).is_instance_of::<PyBool>());
let list = PyList::new_bound(py, &[1, 2, 3, 4]);
assert!(!list.is_instance_of::<PyBool>());
assert!(list.is_instance_of::<PyList>());
});
@ -111,7 +111,7 @@ mod io {
pyo3::import_exception!(io, UnsupportedOperation);
}
fn tell(file: &PyAny) -> PyResult<u64> {
fn tell(file: &Bound<'_, PyAny>) -> PyResult<u64> {
match file.call_method0("tell") {
Err(_) => Err(io::UnsupportedOperation::new_err("not supported: tell")),
Ok(x) => x.extract::<u64>(),
@ -128,5 +128,5 @@ defines exceptions for several standard library modules.
[`PyErr`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html
[`PyResult`]: {{#PYO3_DOCS_URL}}/pyo3/type.PyResult.html
[`PyErr::from_value`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html#method.from_value
[`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#method.is_instance
[`PyAny::is_instance_of`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#method.is_instance_of
[`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.is_instance
[`PyAny::is_instance_of`]: {{#PYO3_DOCS_URL}}/pyo3/types/trait.PyAnyMethods.html#tymethod.is_instance_of

View File

@ -1,5 +1,7 @@
# Frequently Asked Questions and troubleshooting
Sorry that you're having trouble using PyO3. If you can't find the answer to your problem in the list below, you can also reach out for help on [GitHub Discussions](https://github.com/PyO3/pyo3/discussions) and on [Discord](https://discord.gg/33kcChzH7f).
## I'm experiencing deadlocks using PyO3 with lazy_static or once_cell!
`lazy_static` and `once_cell::sync` both use locks to ensure that initialization is performed only by a single thread. Because the Python GIL is an additional lock this can lead to deadlocks in the following way:
@ -125,12 +127,10 @@ If you don't want that cloning to happen, a workaround is to allocate the field
```rust
# use pyo3::prelude::*;
#[pyclass]
#[derive(Clone)]
struct Inner {/* fields omitted */}
#[pyclass]
struct Outer {
#[pyo3(get)]
inner: Py<Inner>,
}
@ -142,6 +142,11 @@ impl Outer {
inner: Py::new(py, Inner {})?,
})
}
#[getter]
fn inner(&self, py: Python<'_>) -> Py<Inner> {
self.inner.clone_ref(py)
}
}
```
This time `a` and `b` *are* the same object:
@ -161,7 +166,7 @@ b: <builtins.Inner object at 0x0000020044FCC670>
```
The downside to this approach is that any Rust code working on the `Outer` struct now has to acquire the GIL to do anything with its field.
## I want to use the `pyo3` crate re-exported from from dependency but the proc-macros fail!
## I want to use the `pyo3` crate re-exported from dependency but the proc-macros fail!
All PyO3 proc-macros (`#[pyclass]`, `#[pyfunction]`, `#[derive(FromPyObject)]`
and so on) expect the `pyo3` crate to be available under that name in your crate

Some files were not shown because too many files have changed in this diff Show More