Compare commits

...

14 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
110 changed files with 692 additions and 1691 deletions

View File

@ -16,6 +16,9 @@ on:
rust-target:
required: true
type: string
MSRV:
required: true
type: string
jobs:
build:
@ -51,9 +54,9 @@ jobs:
name: Prepare LD_LIBRARY_PATH (Ubuntu only)
run: echo LD_LIBRARY_PATH=${pythonLocation}/lib >> $GITHUB_ENV
- if: inputs.rust == '1.63.0'
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 != 'stable'
name: Ignore changed error messages when using trybuild

View File

@ -31,6 +31,18 @@ jobs:
- name: Check rust formatting (rustfmt)
run: nox -s rustfmt
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]
@ -41,13 +53,13 @@ jobs:
- uses: obi1kenobi/cargo-semver-checks-action@v2
check-msrv:
needs: [fmt]
needs: [fmt, resolve]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.63.0
toolchain: ${{ needs.resolve.outputs.MSRV }}
targets: x86_64-unknown-linux-gnu
components: rust-src
- uses: actions/setup-python@v5
@ -57,9 +69,11 @@ jobs:
with:
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
@ -141,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 }}
@ -149,6 +163,7 @@ jobs:
python-architecture: ${{ matrix.platform.python-architecture }}
rust: ${{ matrix.rust }}
rust-target: ${{ matrix.platform.rust-target }}
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
@ -198,7 +213,7 @@ jobs:
build-full:
if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }}
name: python${{ matrix.python-version }}-${{ matrix.platform.python-architecture }} ${{ matrix.platform.os }} rust-${{ matrix.rust }}
needs: [fmt]
needs: [fmt, resolve]
uses: ./.github/workflows/build.yml
with:
os: ${{ matrix.platform.os }}
@ -206,6 +221,7 @@ jobs:
python-architecture: ${{ matrix.platform.python-architecture }}
rust: ${{ matrix.rust }}
rust-target: ${{ matrix.platform.rust-target }}
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
@ -251,7 +267,7 @@ jobs:
]
include:
# Test minimal supported Rust version
- rust: 1.63.0
- rust: ${{ needs.resolve.outputs.MSRV }}
python-version: "3.12"
platform:
{
@ -487,21 +503,31 @@ jobs:
- run: python3 -m nox -s test-version-limits
check-feature-powerset:
needs: [fmt]
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@stable
- uses: dtolnay/rust-toolchain@master
with:
components: rust-src
- uses: taiki-e/install-action@cargo-hack
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
- run: python3 -m nox -s check-feature-powerset -- ${{ matrix.rust != 'stable' && 'minimal-versions' || '' }}
test-cross-compilation:
needs: [fmt]

View File

@ -22,7 +22,7 @@ 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

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3"
version = "0.22.1"
version = "0.23.0-dev"
description = "Bindings to Python interpreter"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
readme = "README.md"
@ -18,13 +18,13 @@ rust-version = "1.63"
cfg-if = "1.0"
libc = "0.2.62"
memoffset = "0.9"
once_cell = "1.13.0"
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.22.1" }
pyo3-ffi = { path = "pyo3-ffi", version = "=0.23.0-dev" }
# support crates for macros feature
pyo3-macros = { path = "pyo3-macros", version = "=0.22.1", 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,17 +32,17 @@ 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 }
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 }
num-rational = {version = "0.4.1", optional = true }
rust_decimal = { version = "1.0.0", default-features = false, optional = true }
rust_decimal = { version = "1.15", default-features = false, optional = true }
serde = { version = "1.0", optional = true }
smallvec = { version = "1.0", optional = true }
@ -63,7 +63,7 @@ rayon = "1.6.1"
futures = "0.3.28"
[build-dependencies]
pyo3-build-config = { path = "pyo3-build-config", version = "=0.22.1", features = ["resolve-config"] }
pyo3-build-config = { path = "pyo3-build-config", version = "=0.23.0-dev", features = ["resolve-config"] }
[features]
default = ["macros"]

View File

@ -193,6 +193,7 @@ 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._

View File

@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.1");
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

@ -75,8 +75,7 @@ impl ExampleContainer {
}
}
#[pymodule]
#[pyo3(name = "getitem")]
#[pymodule(name = "getitem")]
fn example(m: &Bound<'_, PyModule>) -> PyResult<()> {
// ? -https://github.com/PyO3/maturin/issues/475
m.add_class::<ExampleContainer>()?;

View File

@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.1");
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,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.1");
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

@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.1");
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

@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.1");
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

@ -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

View File

@ -129,9 +129,7 @@ fn rust_sleep(py: Python<'_>) -> PyResult<&Bound<'_, PyAny>> {
#[pymodule]
fn my_async_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(rust_sleep, m)?)?;
Ok(())
m.add_function(wrap_pyfunction!(rust_sleep, m)?)
}
```
@ -152,8 +150,7 @@ fn rust_sleep(py: Python<'_>) -> PyResult<&Bound<'_, PyAny>>> {
#[pymodule]
fn my_async_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(rust_sleep, m)?)?;
Ok(())
m.add_function(wrap_pyfunction!(rust_sleep, m)?)
}
```
@ -257,7 +254,7 @@ 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]

View File

@ -14,8 +14,7 @@ fn double(x: usize) -> usize {
#[pymodule]
fn my_extension(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, m)?)?;
Ok(())
m.add_function(wrap_pyfunction!(double, m)?)
}
```
@ -56,8 +55,7 @@ The `#[pyo3]` attribute can be used to modify properties of the generated Python
#[pymodule]
fn module_with_functions(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(no_args_py, m)?)?;
Ok(())
m.add_function(wrap_pyfunction!(no_args_py, m)?)
}
# Python::with_gil(|py| {
@ -113,8 +111,7 @@ The `#[pyo3]` attribute can be used on individual arguments to modify properties
use pyo3::prelude::*;
fn get_length(obj: &Bound<'_, PyAny>) -> PyResult<usize> {
let length = obj.len()?;
Ok(length)
obj.len()
}
#[pyfunction]
@ -204,8 +201,7 @@ fn my_extension(m: &Bound<'_, PyModule>) -> PyResult<()> {
x * 2
}
m.add_function(wrap_pyfunction!(double, m)?)?;
Ok(())
m.add_function(wrap_pyfunction!(double, m)?)
}
```

View File

@ -22,8 +22,7 @@ fn num_kwds(kwds: Option<&Bound<'_, PyDict>>) -> usize {
#[pymodule]
fn module_with_functions(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(num_kwds, m)?).unwrap();
Ok(())
m.add_function(wrap_pyfunction!(num_kwds, m)?)
}
```

View File

@ -159,8 +159,7 @@ fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
/// import the module.
#[pymodule]
fn pyo3_example(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
Ok(())
m.add_function(wrap_pyfunction!(sum_as_string, m)?)
}
```

View File

@ -5,6 +5,15 @@ For a detailed list of all changes, see the [CHANGELOG](changelog.md).
## from 0.21.* to 0.22
### Deprecation of `gil-refs` feature continues
<details open>
<summary><small>Click to expand</small></summary>
Following the introduction of the "Bound" API in PyO3 0.21 and the planned removal of the "GIL Refs" API, all functionality related to GIL Refs is now gated behind the `gil-refs` feature and emits a deprecation warning on use.
See <a href="#from-021-to-022">the 0.21 migration entry</a> for help upgrading.
</details>
### Deprecation of implicit default for trailing optional arguments
<details open>
<summary><small>Click to expand</small></summary>
@ -235,8 +244,7 @@ The `__next__` and `__anext__` magic methods can now return any type convertible
Starting with an implementation of a Python iterator using `IterNextOutput`, e.g.
```rust
#![allow(deprecated)]
```rust,ignore
use pyo3::prelude::*;
use pyo3::iter::IterNextOutput;
@ -529,6 +537,7 @@ assert_eq!(&*name, "list");
# }
# Python::with_gil(example).unwrap();
```
</details>
## from 0.19.* to 0.20

View File

@ -13,8 +13,7 @@ fn double(x: usize) -> usize {
/// This module is implemented in Rust.
#[pymodule]
fn my_extension(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, m)?)?;
Ok(())
m.add_function(wrap_pyfunction!(double, m)?)
}
```
@ -32,11 +31,9 @@ fn double(x: usize) -> usize {
x * 2
}
#[pymodule]
#[pyo3(name = "custom_name")]
#[pymodule(name = "custom_name")]
fn my_extension(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, m)?)?;
Ok(())
m.add_function(wrap_pyfunction!(double, m)?)
}
```
@ -80,8 +77,7 @@ fn parent_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
fn register_child_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
let child_module = PyModule::new_bound(parent_module.py(), "child_module")?;
child_module.add_function(wrap_pyfunction!(func, &child_module)?)?;
parent_module.add_submodule(&child_module)?;
Ok(())
parent_module.add_submodule(&child_module)
}
#[pyfunction]
@ -143,8 +139,7 @@ mod my_extension {
#[pymodule_init]
fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
// Arbitrary code to run at the module initialization
m.add("double2", m.getattr("double")?)?;
Ok(())
m.add("double2", m.getattr("double")?)
}
}
# }
@ -155,7 +150,6 @@ For nested modules, the name of the parent module is automatically added.
In the following example, the `Unit` class will have for `module` `my_extension.submodule` because it is properly nested
but the `Ext` class will have for `module` the default `builtins` because it not nested.
You can provide the `submodule` argument to `pymodule()` for modules that are not top-level modules.
```rust
# mod declarative_module_module_attr_test {
use pyo3::prelude::*;
@ -170,7 +164,7 @@ mod my_extension {
#[pymodule_export]
use super::Ext;
#[pymodule(submodule)]
#[pymodule]
mod submodule {
use super::*;
// This is a submodule
@ -183,3 +177,4 @@ mod my_extension {
```
It is possible to customize the `module` value for a `#[pymodule]` with the `#[pyo3(module = "MY_MODULE")]` option.
You can provide the `submodule` argument to `pymodule()` for modules that are not top-level modules -- it is automatically set for modules nested inside of a `#[pymodule]`.

View File

@ -0,0 +1 @@
Nested declarative `#[pymodule]` are automatically treated as submodules (no `PyInit_` entrypoint is created)

View File

@ -0,0 +1 @@
Deprecate `PyAnyMethods::is_ellipsis` (`Py::is_ellpsis` was deprecated in PyO3 0.20).

View File

@ -0,0 +1 @@
Remove all functionality deprecated in PyO3 0.20.

View File

@ -0,0 +1 @@
Remove all functionality deprecated in PyO3 0.21.

View File

@ -0,0 +1,3 @@
This PR lets PyO3 checks `Py_GIL_DISABLED` build flag and prevents `pyo3-ffi` crate building against GIL-less Python,
unless
explicitly opt using the `UNSAFE_PYO3_BUILD_FREE_THREADED` environment flag.

View File

@ -0,0 +1 @@
Fix compile failure in declarative `#[pymodule]` under presence of `#![no_implicit_prelude]`.

View File

@ -0,0 +1 @@
`#[pymodule(...)]` now directly accepts all relevant `#[pyo3(...)]` options.

View File

@ -9,7 +9,7 @@ import tempfile
from functools import lru_cache
from glob import glob
from pathlib import Path
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple
from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple
import nox
import nox.command
@ -394,8 +394,17 @@ def check_guide(session: nox.Session):
docs(session)
session.posargs.extend(posargs)
if toml is None:
session.error("requires Python 3.11 or `toml` to be installed")
pyo3_version = toml.loads((PYO3_DIR / "Cargo.toml").read_text())["package"][
"version"
]
remaps = {
f"file://{PYO3_GUIDE_SRC}/([^/]*/)*?%7B%7B#PYO3_DOCS_URL}}}}": f"file://{PYO3_DOCS_TARGET}",
f"https://pyo3.rs/v{pyo3_version}": f"file://{PYO3_GUIDE_TARGET}",
"https://pyo3.rs/main/": f"file://{PYO3_GUIDE_TARGET}/",
"https://pyo3.rs/latest/": f"file://{PYO3_GUIDE_TARGET}/",
"%7B%7B#PYO3_DOCS_VERSION}}": "latest",
}
remap_args = []
@ -416,8 +425,7 @@ def check_guide(session: nox.Session):
session,
"lychee",
str(PYO3_DOCS_TARGET),
f"--remap=https://pyo3.rs/main/ file://{PYO3_GUIDE_TARGET}/",
f"--remap=https://pyo3.rs/latest/ file://{PYO3_GUIDE_TARGET}/",
*remap_args,
f"--exclude=file://{PYO3_DOCS_TARGET}",
"--exclude=http://www.adobe.com/",
*session.posargs,
@ -543,8 +551,8 @@ def check_changelog(session: nox.Session):
print(fragment.name)
@nox.session(name="set-minimal-package-versions", venv_backend="none")
def set_minimal_package_versions(session: nox.Session):
@nox.session(name="set-msrv-package-versions", venv_backend="none")
def set_msrv_package_versions(session: nox.Session):
from collections import defaultdict
if toml is None:
@ -647,6 +655,14 @@ def test_version_limits(session: nox.Session):
config_file.set("PyPy", "3.11")
_run_cargo(session, "check", env=env, expect_error=True)
# Python build with GIL disabled should fail building
config_file.set("CPython", "3.13", build_flags=["Py_GIL_DISABLED"])
_run_cargo(session, "check", env=env, expect_error=True)
# Python build with GIL disabled should pass with env flag on
env["UNSAFE_PYO3_BUILD_FREE_THREADED"] = "1"
_run_cargo(session, "check", env=env)
@nox.session(name="check-feature-powerset", venv_backend="none")
def check_feature_powerset(session: nox.Session):
@ -708,10 +724,14 @@ def check_feature_powerset(session: nox.Session):
rust_flags = env.get("RUSTFLAGS", "")
env["RUSTFLAGS"] = f"{rust_flags} -Dwarnings"
subcommand = "hack"
if "minimal-versions" in session.posargs:
subcommand = "minimal-versions"
comma_join = ",".join
_run_cargo(
session,
"hack",
subcommand,
"--feature-powerset",
'--optional-deps=""',
f'--skip="{comma_join(features_to_skip)}"',
@ -907,7 +927,9 @@ class _ConfigFile:
def __init__(self, config_file) -> None:
self._config_file = config_file
def set(self, implementation: str, version: str) -> None:
def set(
self, implementation: str, version: str, build_flags: Iterable[str] = ()
) -> None:
"""Set the contents of this config file to the given implementation and version."""
self._config_file.seek(0)
self._config_file.truncate(0)
@ -915,6 +937,7 @@ class _ConfigFile:
f"""\
implementation={implementation}
version={version}
build_flags={','.join(build_flags)}
suppress_build_script_link_lines=true
"""
)

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3-build-config"
version = "0.22.1"
version = "0.23.0-dev"
description = "Build configuration for the PyO3 ecosystem"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]

View File

@ -996,6 +996,7 @@ pub enum BuildFlag {
Py_DEBUG,
Py_REF_DEBUG,
Py_TRACE_REFS,
Py_GIL_DISABLED,
COUNT_ALLOCS,
Other(String),
}
@ -1016,6 +1017,7 @@ impl FromStr for BuildFlag {
"Py_DEBUG" => Ok(BuildFlag::Py_DEBUG),
"Py_REF_DEBUG" => Ok(BuildFlag::Py_REF_DEBUG),
"Py_TRACE_REFS" => Ok(BuildFlag::Py_TRACE_REFS),
"Py_GIL_DISABLED" => Ok(BuildFlag::Py_GIL_DISABLED),
"COUNT_ALLOCS" => Ok(BuildFlag::COUNT_ALLOCS),
other => Ok(BuildFlag::Other(other.to_owned())),
}
@ -1039,10 +1041,11 @@ impl FromStr for BuildFlag {
pub struct BuildFlags(pub HashSet<BuildFlag>);
impl BuildFlags {
const ALL: [BuildFlag; 4] = [
const ALL: [BuildFlag; 5] = [
BuildFlag::Py_DEBUG,
BuildFlag::Py_REF_DEBUG,
BuildFlag::Py_TRACE_REFS,
BuildFlag::Py_GIL_DISABLED,
BuildFlag::COUNT_ALLOCS,
];

View File

@ -39,7 +39,9 @@ use target_lexicon::OperatingSystem;
/// | `#[cfg(PyPy)]` | This marks code which is run when compiling for PyPy. |
/// | `#[cfg(GraalPy)]` | This marks code which is run when compiling for GraalPy. |
///
/// For examples of how to use these attributes, [see PyO3's guide](https://pyo3.rs/latest/building-and-distribution/multiple_python_versions.html).
/// For examples of how to use these attributes,
#[doc = concat!("[see PyO3's guide](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/building-and-distribution/multiple_python_versions.html)")]
/// .
#[cfg(feature = "resolve-config")]
pub fn use_pyo3_cfgs() {
print_expected_cfgs();

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3-ffi"
version = "0.22.1"
version = "0.23.0-dev"
description = "Python-API bindings for the PyO3 ecosystem"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]
@ -38,7 +38,7 @@ abi3-py312 = ["abi3", "pyo3-build-config/abi3-py312"]
generate-import-lib = ["pyo3-build-config/python3-dll-a"]
[build-dependencies]
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.22.1", features = ["resolve-config"] }
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.23.0-dev", features = ["resolve-config"] }
[lints]
workspace = true

View File

@ -4,8 +4,9 @@ use pyo3_build_config::{
cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config,
InterpreterConfig, PythonVersion,
},
warn, PythonImplementation,
warn, BuildFlag, PythonImplementation,
};
use std::ops::Not;
/// Minimum Python version PyO3 supports.
struct SupportedVersions {
@ -120,6 +121,24 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> {
Ok(())
}
fn ensure_gil_enabled(interpreter_config: &InterpreterConfig) -> Result<()> {
let gil_enabled = interpreter_config
.build_flags
.0
.contains(&BuildFlag::Py_GIL_DISABLED)
.not();
ensure!(
gil_enabled || std::env::var("UNSAFE_PYO3_BUILD_FREE_THREADED").map_or(false, |os_str| os_str == "1"),
"the Python interpreter was built with the GIL disabled, which is not yet supported by PyO3\n\
= help: see https://github.com/PyO3/pyo3/issues/4265 for more information\n\
= help: please check if an updated version of PyO3 is available. Current version: {}\n\
= help: set UNSAFE_PYO3_BUILD_FREE_THREADED=1 to suppress this check and build anyway for free-threaded Python",
std::env::var("CARGO_PKG_VERSION").unwrap()
);
Ok(())
}
fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> {
if let Some(pointer_width) = interpreter_config.pointer_width {
// Try to check whether the target architecture matches the python library
@ -185,6 +204,7 @@ fn configure_pyo3() -> Result<()> {
ensure_python_version(&interpreter_config)?;
ensure_target_pointer_width(&interpreter_config)?;
ensure_gil_enabled(&interpreter_config)?;
// Serialize the whole interpreter config into DEP_PYTHON_PYO3_CONFIG env var.
interpreter_config.to_cargo_dep_env()?;

View File

@ -221,11 +221,10 @@
//! [`maturin`]: https://github.com/PyO3/maturin "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages"
//! [`pyo3-build-config`]: https://docs.rs/pyo3-build-config
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/features.html "Features - The Cargo Book"
//! [manual_builds]: https://pyo3.rs/latest/building-and-distribution.html#manual-builds "Manual builds - Building and Distribution - PyO3 user guide"
#![doc = concat!("[manual_builds]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/building-and-distribution.html#manual-builds \"Manual builds - Building and Distribution - PyO3 user guide\"")]
//! [setuptools-rust]: https://github.com/PyO3/setuptools-rust "Setuptools plugin for Rust extensions"
//! [PEP 384]: https://www.python.org/dev/peps/pep-0384 "PEP 384 -- Defining a Stable ABI"
//! [Features chapter of the guide]: https://pyo3.rs/latest/features.html#features-reference "Features Reference - PyO3 user guide"
#![doc = concat!("[Features chapter of the guide]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/features.html#features-reference \"Features Reference - PyO3 user guide\"")]
#![allow(
missing_docs,
non_camel_case_types,

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3-macros-backend"
version = "0.22.1"
version = "0.23.0-dev"
description = "Code generation for PyO3 package"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]
@ -16,7 +16,7 @@ edition = "2021"
[dependencies]
heck = "0.5"
proc-macro2 = { version = "1.0.60", default-features = false }
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.22.1", features = ["resolve-config"] }
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.23.0-dev", features = ["resolve-config"] }
quote = { version = "1", default-features = false }
[dependencies.syn]
@ -25,7 +25,7 @@ default-features = false
features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits"]
[build-dependencies]
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.22.1" }
pyo3-build-config = { path = "../pyo3-build-config", version = "=0.23.0-dev" }
[lints]
workspace = true

View File

@ -1,53 +1,6 @@
use crate::{
method::{FnArg, FnSpec},
utils::Ctx,
};
use proc_macro2::{Span, TokenStream};
use quote::{quote_spanned, ToTokens};
pub enum Deprecation {
PyMethodsNewDeprecatedForm,
}
impl Deprecation {
fn ident(&self, span: Span) -> syn::Ident {
let string = match self {
Deprecation::PyMethodsNewDeprecatedForm => "PYMETHODS_NEW_DEPRECATED_FORM",
};
syn::Ident::new(string, span)
}
}
pub struct Deprecations<'ctx>(Vec<(Deprecation, Span)>, &'ctx Ctx);
impl<'ctx> Deprecations<'ctx> {
pub fn new(ctx: &'ctx Ctx) -> Self {
Deprecations(Vec::new(), ctx)
}
pub fn push(&mut self, deprecation: Deprecation, span: Span) {
self.0.push((deprecation, span))
}
}
impl<'ctx> ToTokens for Deprecations<'ctx> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self(deprecations, Ctx { pyo3_path, .. }) = self;
for (deprecation, span) in deprecations {
let pyo3_path = pyo3_path.to_tokens_spanned(*span);
let ident = deprecation.ident(*span);
quote_spanned!(
*span =>
#[allow(clippy::let_unit_value)]
{
let _ = #pyo3_path::impl_::deprecations::#ident;
}
)
.to_tokens(tokens)
}
}
}
use crate::method::{FnArg, FnSpec};
use proc_macro2::TokenStream;
use quote::quote_spanned;
pub(crate) fn deprecate_trailing_option_default(spec: &FnSpec<'_>) -> TokenStream {
if spec.signature.attribute.is_none()

View File

@ -1,7 +1,7 @@
use crate::attributes::{self, get_pyo3_options, CrateAttribute, FromPyWithAttribute};
use crate::utils::Ctx;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use quote::{format_ident, quote};
use syn::{
parenthesized,
parse::{Parse, ParseStream},
@ -44,16 +44,14 @@ impl<'a> Enum<'a> {
}
/// Build derivation body for enums.
fn build(&self, ctx: &Ctx) -> (TokenStream, TokenStream) {
fn build(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
let mut var_extracts = Vec::new();
let mut variant_names = Vec::new();
let mut error_names = Vec::new();
let mut deprecations = TokenStream::new();
for var in &self.variants {
let (struct_derive, dep) = var.build(ctx);
deprecations.extend(dep);
let struct_derive = var.build(ctx);
let ext = quote!({
let maybe_ret = || -> #pyo3_path::PyResult<Self> {
#struct_derive
@ -70,22 +68,19 @@ impl<'a> Enum<'a> {
error_names.push(&var.err_name);
}
let ty_name = self.enum_ident.to_string();
(
quote!(
let errors = [
#(#var_extracts),*
];
::std::result::Result::Err(
#pyo3_path::impl_::frompyobject::failed_to_extract_enum(
obj.py(),
#ty_name,
&[#(#variant_names),*],
&[#(#error_names),*],
&errors
)
quote!(
let errors = [
#(#var_extracts),*
];
::std::result::Result::Err(
#pyo3_path::impl_::frompyobject::failed_to_extract_enum(
obj.py(),
#ty_name,
&[#(#variant_names),*],
&[#(#error_names),*],
&errors
)
),
deprecations,
)
)
}
}
@ -244,7 +239,7 @@ impl<'a> Container<'a> {
}
/// Build derivation body for a struct.
fn build(&self, ctx: &Ctx) -> (TokenStream, TokenStream) {
fn build(&self, ctx: &Ctx) -> TokenStream {
match &self.ty {
ContainerType::StructNewtype(ident, from_py_with) => {
self.build_newtype_struct(Some(ident), from_py_with, ctx)
@ -262,73 +257,42 @@ impl<'a> Container<'a> {
field_ident: Option<&Ident>,
from_py_with: &Option<FromPyWithAttribute>,
ctx: &Ctx,
) -> (TokenStream, TokenStream) {
) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
let self_ty = &self.path;
let struct_name = self.name();
if let Some(ident) = field_ident {
let field_name = ident.to_string();
match from_py_with {
None => (
quote! {
Ok(#self_ty {
#ident: #pyo3_path::impl_::frompyobject::extract_struct_field(obj, #struct_name, #field_name)?
})
},
TokenStream::new(),
),
None => quote! {
Ok(#self_ty {
#ident: #pyo3_path::impl_::frompyobject::extract_struct_field(obj, #struct_name, #field_name)?
})
},
Some(FromPyWithAttribute {
value: expr_path, ..
}) => (
quote! {
Ok(#self_ty {
#ident: #pyo3_path::impl_::frompyobject::extract_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, #field_name)?
})
},
quote_spanned! { expr_path.span() =>
const _: () = {
fn check_from_py_with() {
let e = #pyo3_path::impl_::deprecations::GilRefs::new();
#pyo3_path::impl_::deprecations::inspect_fn(#expr_path, &e);
e.from_py_with_arg();
}
};
},
),
}) => quote! {
Ok(#self_ty {
#ident: #pyo3_path::impl_::frompyobject::extract_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, #field_name)?
})
},
}
} else {
match from_py_with {
None => (
quote!(
#pyo3_path::impl_::frompyobject::extract_tuple_struct_field(obj, #struct_name, 0).map(#self_ty)
),
TokenStream::new(),
),
None => quote! {
#pyo3_path::impl_::frompyobject::extract_tuple_struct_field(obj, #struct_name, 0).map(#self_ty)
},
Some(FromPyWithAttribute {
value: expr_path, ..
}) => (
quote! (
#pyo3_path::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, 0).map(#self_ty)
),
quote_spanned! { expr_path.span() =>
const _: () = {
fn check_from_py_with() {
let e = #pyo3_path::impl_::deprecations::GilRefs::new();
#pyo3_path::impl_::deprecations::inspect_fn(#expr_path, &e);
e.from_py_with_arg();
}
};
},
),
}) => quote! {
#pyo3_path::impl_::frompyobject::extract_tuple_struct_field_with(#expr_path as fn(_) -> _, obj, #struct_name, 0).map(#self_ty)
},
}
}
}
fn build_tuple_struct(
&self,
struct_fields: &[TupleStructField],
ctx: &Ctx,
) -> (TokenStream, TokenStream) {
fn build_tuple_struct(&self, struct_fields: &[TupleStructField], ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
let self_ty = &self.path;
let struct_name = &self.name();
@ -348,40 +312,15 @@ impl<'a> Container<'a> {
}
});
let deprecations = struct_fields
.iter()
.filter_map(|field| {
let FromPyWithAttribute {
value: expr_path, ..
} = field.from_py_with.as_ref()?;
Some(quote_spanned! { expr_path.span() =>
const _: () = {
fn check_from_py_with() {
let e = #pyo3_path::impl_::deprecations::GilRefs::new();
#pyo3_path::impl_::deprecations::inspect_fn(#expr_path, &e);
e.from_py_with_arg();
}
};
})
})
.collect::<TokenStream>();
(
quote!(
match #pyo3_path::types::PyAnyMethods::extract(obj) {
::std::result::Result::Ok((#(#field_idents),*)) => ::std::result::Result::Ok(#self_ty(#(#fields),*)),
::std::result::Result::Err(err) => ::std::result::Result::Err(err),
}
),
deprecations,
quote!(
match #pyo3_path::types::PyAnyMethods::extract(obj) {
::std::result::Result::Ok((#(#field_idents),*)) => ::std::result::Result::Ok(#self_ty(#(#fields),*)),
::std::result::Result::Err(err) => ::std::result::Result::Err(err),
}
)
}
fn build_struct(
&self,
struct_fields: &[NamedStructField<'_>],
ctx: &Ctx,
) -> (TokenStream, TokenStream) {
fn build_struct(&self, struct_fields: &[NamedStructField<'_>], ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
let self_ty = &self.path;
let struct_name = &self.name();
@ -420,28 +359,7 @@ impl<'a> Container<'a> {
fields.push(quote!(#ident: #extractor));
}
let deprecations = struct_fields
.iter()
.filter_map(|field| {
let FromPyWithAttribute {
value: expr_path, ..
} = field.from_py_with.as_ref()?;
Some(quote_spanned! { expr_path.span() =>
const _: () = {
fn check_from_py_with() {
let e = #pyo3_path::impl_::deprecations::GilRefs::new();
#pyo3_path::impl_::deprecations::inspect_fn(#expr_path, &e);
e.from_py_with_arg();
}
};
})
})
.collect::<TokenStream>();
(
quote!(::std::result::Result::Ok(#self_ty{#fields})),
deprecations,
)
quote!(::std::result::Result::Ok(#self_ty{#fields}))
}
}
@ -673,7 +591,7 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
let ctx = &Ctx::new(&options.krate, None);
let Ctx { pyo3_path, .. } = &ctx;
let (derives, from_py_with_deprecations) = match &tokens.data {
let derives = match &tokens.data {
syn::Data::Enum(en) => {
if options.transparent || options.annotation.is_some() {
bail_spanned!(tokens.span() => "`transparent` or `annotation` is not supported \
@ -703,7 +621,5 @@ pub fn build_derive_from_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
#derives
}
}
#from_py_with_deprecations
))
}

View File

@ -1,11 +1,8 @@
use std::borrow::Cow;
use std::ffi::CString;
use crate::attributes::{self, get_pyo3_options, take_attributes, NameAttribute};
use crate::utils::{Ctx, LitCStr};
use crate::{
attributes::{self, get_pyo3_options, take_attributes, NameAttribute},
deprecations::Deprecations,
};
use proc_macro2::{Ident, Span};
use syn::{
ext::IdentExt,
@ -14,12 +11,12 @@ use syn::{
Result,
};
pub struct ConstSpec<'ctx> {
pub struct ConstSpec {
pub rust_ident: syn::Ident,
pub attributes: ConstAttributes<'ctx>,
pub attributes: ConstAttributes,
}
impl ConstSpec<'_> {
impl ConstSpec {
pub fn python_name(&self) -> Cow<'_, Ident> {
if let Some(name) = &self.attributes.name {
Cow::Borrowed(&name.value.0)
@ -35,10 +32,9 @@ impl ConstSpec<'_> {
}
}
pub struct ConstAttributes<'ctx> {
pub struct ConstAttributes {
pub is_class_attr: bool,
pub name: Option<NameAttribute>,
pub deprecations: Deprecations<'ctx>,
}
pub enum PyO3ConstAttribute {
@ -56,12 +52,11 @@ impl Parse for PyO3ConstAttribute {
}
}
impl<'ctx> ConstAttributes<'ctx> {
pub fn from_attrs(attrs: &mut Vec<syn::Attribute>, ctx: &'ctx Ctx) -> syn::Result<Self> {
impl ConstAttributes {
pub fn from_attrs(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Self> {
let mut attributes = ConstAttributes {
is_class_attr: false,
name: None,
deprecations: Deprecations::new(ctx),
};
take_attributes(attrs, |attr| {

View File

@ -10,7 +10,6 @@ use crate::deprecations::deprecate_trailing_option_default;
use crate::utils::{Ctx, LitCStr};
use crate::{
attributes::{FromPyWithAttribute, TextSignatureAttribute, TextSignatureAttributeValue},
deprecations::{Deprecation, Deprecations},
params::{impl_arg_params, Holders},
pyfunction::{
FunctionSignature, PyFunctionArgPyO3Attributes, PyFunctionOptions, SignatureAttribute,
@ -411,7 +410,6 @@ pub struct FnSpec<'a> {
pub text_signature: Option<TextSignatureAttribute>,
pub asyncness: Option<syn::Token![async]>,
pub unsafety: Option<syn::Token![unsafe]>,
pub deprecations: Deprecations<'a>,
}
pub fn parse_method_receiver(arg: &syn::FnArg) -> Result<SelfType> {
@ -443,7 +441,6 @@ impl<'a> FnSpec<'a> {
sig: &'a mut syn::Signature,
meth_attrs: &mut Vec<syn::Attribute>,
options: PyFunctionOptions,
ctx: &'a Ctx,
) -> Result<FnSpec<'a>> {
let PyFunctionOptions {
text_signature,
@ -453,9 +450,8 @@ impl<'a> FnSpec<'a> {
} = options;
let mut python_name = name.map(|name| name.value.0);
let mut deprecations = Deprecations::new(ctx);
let fn_type = Self::parse_fn_type(sig, meth_attrs, &mut python_name, &mut deprecations)?;
let fn_type = Self::parse_fn_type(sig, meth_attrs, &mut python_name)?;
ensure_signatures_on_valid_method(&fn_type, signature.as_ref(), text_signature.as_ref())?;
let name = &sig.ident;
@ -493,7 +489,6 @@ impl<'a> FnSpec<'a> {
text_signature,
asyncness: sig.asyncness,
unsafety: sig.unsafety,
deprecations,
})
}
@ -507,9 +502,8 @@ impl<'a> FnSpec<'a> {
sig: &syn::Signature,
meth_attrs: &mut Vec<syn::Attribute>,
python_name: &mut Option<syn::Ident>,
deprecations: &mut Deprecations<'_>,
) -> Result<FnType> {
let mut method_attributes = parse_method_attributes(meth_attrs, deprecations)?;
let mut method_attributes = parse_method_attributes(meth_attrs)?;
let name = &sig.ident;
let parse_receiver = |msg: &'static str| {
@ -683,11 +677,10 @@ impl<'a> FnSpec<'a> {
}
_ => {
if let Some(self_arg) = self_arg() {
let self_checker = holders.push_gil_refs_checker(self_arg.span());
quote! {
function(
// NB #self_arg includes a comma, so none inserted here
#pyo3_path::impl_::deprecations::inspect_type(#self_arg &#self_checker),
#self_arg
#(#args),*
)
}
@ -714,11 +707,10 @@ impl<'a> FnSpec<'a> {
}
call
} else if let Some(self_arg) = self_arg() {
let self_checker = holders.push_gil_refs_checker(self_arg.span());
quote! {
function(
// NB #self_arg includes a comma, so none inserted here
#pyo3_path::impl_::deprecations::inspect_type(#self_arg &#self_checker),
#self_arg
#(#args),*
)
}
@ -762,7 +754,6 @@ impl<'a> FnSpec<'a> {
})
.collect();
let call = rust_call(args, &mut holders);
let check_gil_refs = holders.check_gil_refs();
let init_holders = holders.init_holders(ctx);
quote! {
unsafe fn #ident<'py>(
@ -774,7 +765,6 @@ impl<'a> FnSpec<'a> {
let function = #rust_name; // Shadow the function name to avoid #3017
#init_holders
let result = #call;
#check_gil_refs
result
}
}
@ -784,7 +774,6 @@ impl<'a> FnSpec<'a> {
let (arg_convert, args) = impl_arg_params(self, cls, true, &mut holders, ctx);
let call = rust_call(args, &mut holders);
let init_holders = holders.init_holders(ctx);
let check_gil_refs = holders.check_gil_refs();
quote! {
unsafe fn #ident<'py>(
@ -800,7 +789,6 @@ impl<'a> FnSpec<'a> {
#arg_convert
#init_holders
let result = #call;
#check_gil_refs
result
}
}
@ -810,7 +798,6 @@ impl<'a> FnSpec<'a> {
let (arg_convert, args) = impl_arg_params(self, cls, false, &mut holders, ctx);
let call = rust_call(args, &mut holders);
let init_holders = holders.init_holders(ctx);
let check_gil_refs = holders.check_gil_refs();
quote! {
unsafe fn #ident<'py>(
@ -825,7 +812,6 @@ impl<'a> FnSpec<'a> {
#arg_convert
#init_holders
let result = #call;
#check_gil_refs
result
}
}
@ -838,7 +824,6 @@ impl<'a> FnSpec<'a> {
.self_arg(cls, ExtractErrorMode::Raise, &mut holders, ctx);
let call = quote_spanned! {*output_span=> #rust_name(#self_arg #(#args),*) };
let init_holders = holders.init_holders(ctx);
let check_gil_refs = holders.check_gil_refs();
quote! {
unsafe fn #ident(
py: #pyo3_path::Python<'_>,
@ -854,7 +839,6 @@ impl<'a> FnSpec<'a> {
#init_holders
let result = #call;
let initializer: #pyo3_path::PyClassInitializer::<#cls> = result.convert(py)?;
#check_gil_refs
#pyo3_path::impl_::pymethods::tp_new_impl(py, initializer, _slf)
}
}
@ -992,10 +976,7 @@ impl MethodTypeAttribute {
/// If the attribute does not match one of the attribute names, returns `Ok(None)`.
///
/// Otherwise will either return a parse error or the attribute.
fn parse_if_matching_attribute(
attr: &syn::Attribute,
deprecations: &mut Deprecations<'_>,
) -> Result<Option<Self>> {
fn parse_if_matching_attribute(attr: &syn::Attribute) -> Result<Option<Self>> {
fn ensure_no_arguments(meta: &syn::Meta, ident: &str) -> syn::Result<()> {
match meta {
syn::Meta::Path(_) => Ok(()),
@ -1039,11 +1020,6 @@ impl MethodTypeAttribute {
if path.is_ident("new") {
ensure_no_arguments(meta, "new")?;
Ok(Some(MethodTypeAttribute::New(path.span())))
} else if path.is_ident("__new__") {
let span = path.span();
deprecations.push(Deprecation::PyMethodsNewDeprecatedForm, span);
ensure_no_arguments(meta, "__new__")?;
Ok(Some(MethodTypeAttribute::New(span)))
} else if path.is_ident("classmethod") {
ensure_no_arguments(meta, "classmethod")?;
Ok(Some(MethodTypeAttribute::ClassMethod(path.span())))
@ -1078,15 +1054,12 @@ impl Display for MethodTypeAttribute {
}
}
fn parse_method_attributes(
attrs: &mut Vec<syn::Attribute>,
deprecations: &mut Deprecations<'_>,
) -> Result<Vec<MethodTypeAttribute>> {
fn parse_method_attributes(attrs: &mut Vec<syn::Attribute>) -> Result<Vec<MethodTypeAttribute>> {
let mut new_attrs = Vec::new();
let mut found_attrs = Vec::new();
for attr in attrs.drain(..) {
match MethodTypeAttribute::parse_if_matching_attribute(&attr, deprecations)? {
match MethodTypeAttribute::parse_if_matching_attribute(&attr)? {
Some(attr) => found_attrs.push(attr),
None => new_attrs.push(attr),
}

View File

@ -2,8 +2,8 @@
use crate::{
attributes::{
self, take_attributes, take_pyo3_options, CrateAttribute, ModuleAttribute, NameAttribute,
SubmoduleAttribute,
self, kw, take_attributes, take_pyo3_options, CrateAttribute, ModuleAttribute,
NameAttribute, SubmoduleAttribute,
},
get_doc,
pyclass::PyClassPyO3Option,
@ -26,97 +26,88 @@ use syn::{
#[derive(Default)]
pub struct PyModuleOptions {
krate: Option<CrateAttribute>,
name: Option<syn::Ident>,
name: Option<NameAttribute>,
module: Option<ModuleAttribute>,
is_submodule: bool,
submodule: Option<kw::submodule>,
}
impl PyModuleOptions {
pub fn from_attrs(attrs: &mut Vec<syn::Attribute>) -> Result<Self> {
impl Parse for PyModuleOptions {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut options: PyModuleOptions = Default::default();
for option in take_pyo3_options(attrs)? {
match option {
PyModulePyO3Option::Name(name) => options.set_name(name.value.0)?,
PyModulePyO3Option::Crate(path) => options.set_crate(path)?,
PyModulePyO3Option::Module(module) => options.set_module(module)?,
PyModulePyO3Option::Submodule(submod) => options.set_submodule(submod)?,
}
}
options.add_attributes(
Punctuated::<PyModulePyO3Option, syn::Token![,]>::parse_terminated(input)?,
)?;
Ok(options)
}
}
fn set_name(&mut self, name: syn::Ident) -> Result<()> {
ensure_spanned!(
self.name.is_none(),
name.span() => "`name` may only be specified once"
);
self.name = Some(name);
Ok(())
impl PyModuleOptions {
fn take_pyo3_options(&mut self, attrs: &mut Vec<syn::Attribute>) -> Result<()> {
self.add_attributes(take_pyo3_options(attrs)?)
}
fn set_crate(&mut self, path: CrateAttribute) -> Result<()> {
ensure_spanned!(
self.krate.is_none(),
path.span() => "`crate` may only be specified once"
);
self.krate = Some(path);
Ok(())
}
fn set_module(&mut self, name: ModuleAttribute) -> Result<()> {
ensure_spanned!(
self.module.is_none(),
name.span() => "`module` may only be specified once"
);
self.module = Some(name);
Ok(())
}
fn set_submodule(&mut self, submod: SubmoduleAttribute) -> Result<()> {
ensure_spanned!(
!self.is_submodule,
submod.span() => "`submodule` may only be specified once"
);
self.is_submodule = true;
fn add_attributes(
&mut self,
attrs: impl IntoIterator<Item = PyModulePyO3Option>,
) -> Result<()> {
macro_rules! set_option {
($key:ident $(, $extra:literal)?) => {
{
ensure_spanned!(
self.$key.is_none(),
$key.span() => concat!("`", stringify!($key), "` may only be specified once" $(, $extra)?)
);
self.$key = Some($key);
}
};
}
for attr in attrs {
match attr {
PyModulePyO3Option::Crate(krate) => set_option!(krate),
PyModulePyO3Option::Name(name) => set_option!(name),
PyModulePyO3Option::Module(module) => set_option!(module),
PyModulePyO3Option::Submodule(submodule) => set_option!(
submodule,
" (it is implicitly always specified for nested modules)"
),
}
}
Ok(())
}
}
pub fn pymodule_module_impl(
mut module: syn::ItemMod,
mut is_submodule: bool,
module: &mut syn::ItemMod,
mut options: PyModuleOptions,
) -> Result<TokenStream> {
let syn::ItemMod {
attrs,
vis,
unsafety: _,
ident,
mod_token: _,
mod_token,
content,
semi: _,
} = &mut module;
} = module;
let items = if let Some((_, items)) = content {
items
} else {
bail_spanned!(module.span() => "`#[pymodule]` can only be used on inline modules")
bail_spanned!(mod_token.span() => "`#[pymodule]` can only be used on inline modules")
};
let options = PyModuleOptions::from_attrs(attrs)?;
options.take_pyo3_options(attrs)?;
let ctx = &Ctx::new(&options.krate, None);
let Ctx { pyo3_path, .. } = ctx;
let doc = get_doc(attrs, None, ctx);
let name = options.name.unwrap_or_else(|| ident.unraw());
let name = options
.name
.map_or_else(|| ident.unraw(), |name| name.value.0);
let full_name = if let Some(module) = &options.module {
format!("{}.{}", module.value.value(), name)
} else {
name.to_string()
};
is_submodule = is_submodule || options.is_submodule;
let mut module_items = Vec::new();
let mut module_items_cfg_attrs = Vec::new();
@ -273,6 +264,9 @@ pub fn pymodule_module_impl(
)? {
set_module_attribute(&mut item_mod.attrs, &full_name);
}
item_mod
.attrs
.push(parse_quote_spanned!(item_mod.mod_token.span()=> #[pyo3(submodule)]));
}
}
Item::ForeignMod(item) => {
@ -350,10 +344,11 @@ pub fn pymodule_module_impl(
)
}
}};
let initialization = module_initialization(&name, ctx, module_def, is_submodule);
let initialization = module_initialization(&name, ctx, module_def, options.submodule.is_some());
Ok(quote!(
#(#attrs)*
#vis mod #ident {
#vis #mod_token #ident {
#(#items)*
#initialization
@ -365,7 +360,7 @@ pub fn pymodule_module_impl(
#module_items::_PYO3_DEF.add_to_module(module)?;
)*
#pymodule_init
Ok(())
::std::result::Result::Ok(())
}
}
))
@ -373,14 +368,18 @@ pub fn pymodule_module_impl(
/// Generates the function that is called by the python interpreter to initialize the native
/// module
pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result<TokenStream> {
let options = PyModuleOptions::from_attrs(&mut function.attrs)?;
process_functions_in_module(&options, &mut function)?;
pub fn pymodule_function_impl(
function: &mut syn::ItemFn,
mut options: PyModuleOptions,
) -> Result<TokenStream> {
options.take_pyo3_options(&mut function.attrs)?;
process_functions_in_module(&options, function)?;
let ctx = &Ctx::new(&options.krate, None);
let stmts = std::mem::take(&mut function.block.stmts);
let Ctx { pyo3_path, .. } = ctx;
let ident = &function.sig.ident;
let name = options.name.unwrap_or_else(|| ident.unraw());
let name = options
.name
.map_or_else(|| ident.unraw(), |name| name.value.0);
let vis = &function.vis;
let doc = get_doc(&function.attrs, None, ctx);
@ -394,32 +393,7 @@ pub fn pymodule_function_impl(mut function: syn::ItemFn) -> Result<TokenStream>
module_args
.push(quote!(::std::convert::Into::into(#pyo3_path::impl_::pymethods::BoundRef(module))));
let extractors = function
.sig
.inputs
.iter()
.filter_map(|param| {
if let syn::FnArg::Typed(pat_type) = param {
if let syn::Pat::Ident(pat_ident) = &*pat_type.pat {
let ident: &syn::Ident = &pat_ident.ident;
return Some([
parse_quote!{ let check_gil_refs = #pyo3_path::impl_::deprecations::GilRefs::new(); },
parse_quote! { let #ident = #pyo3_path::impl_::deprecations::inspect_type(#ident, &check_gil_refs); },
parse_quote_spanned! { pat_type.span() => check_gil_refs.function_arg(); },
]);
}
}
None
})
.flatten();
function.block.stmts = extractors.chain(stmts).collect();
function
.attrs
.push(parse_quote!(#[allow(clippy::used_underscore_binding)]));
Ok(quote! {
#function
#[doc(hidden)]
#vis mod #ident {
#initialization

View File

@ -10,14 +10,12 @@ use syn::spanned::Spanned;
pub struct Holders {
holders: Vec<syn::Ident>,
gil_refs_checkers: Vec<GilRefChecker>,
}
impl Holders {
pub fn new() -> Self {
Holders {
holders: Vec::new(),
gil_refs_checkers: Vec::new(),
}
}
@ -27,58 +25,14 @@ impl Holders {
holder
}
pub fn push_gil_refs_checker(&mut self, span: Span) -> syn::Ident {
let gil_refs_checker = syn::Ident::new(
&format!("gil_refs_checker_{}", self.gil_refs_checkers.len()),
span,
);
self.gil_refs_checkers
.push(GilRefChecker::FunctionArg(gil_refs_checker.clone()));
gil_refs_checker
}
pub fn push_from_py_with_checker(&mut self, span: Span) -> syn::Ident {
let gil_refs_checker = syn::Ident::new(
&format!("gil_refs_checker_{}", self.gil_refs_checkers.len()),
span,
);
self.gil_refs_checkers
.push(GilRefChecker::FromPyWith(gil_refs_checker.clone()));
gil_refs_checker
}
pub fn init_holders(&self, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
let holders = &self.holders;
let gil_refs_checkers = self.gil_refs_checkers.iter().map(|checker| match checker {
GilRefChecker::FunctionArg(ident) => ident,
GilRefChecker::FromPyWith(ident) => ident,
});
quote! {
#[allow(clippy::let_unit_value)]
#(let mut #holders = #pyo3_path::impl_::extract_argument::FunctionArgumentHolder::INIT;)*
#(let #gil_refs_checkers = #pyo3_path::impl_::deprecations::GilRefs::new();)*
}
}
pub fn check_gil_refs(&self) -> TokenStream {
self.gil_refs_checkers
.iter()
.map(|checker| match checker {
GilRefChecker::FunctionArg(ident) => {
quote_spanned! { ident.span() => #ident.function_arg(); }
}
GilRefChecker::FromPyWith(ident) => {
quote_spanned! { ident.span() => #ident.from_py_with_arg(); }
}
})
.collect()
}
}
enum GilRefChecker {
FunctionArg(syn::Ident),
FromPyWith(syn::Ident),
}
/// Return true if the argument list is simply (*args, **kwds).
@ -89,17 +43,6 @@ pub fn is_forwarded_args(signature: &FunctionSignature<'_>) -> bool {
)
}
pub(crate) fn check_arg_for_gil_refs(
tokens: TokenStream,
gil_refs_checker: syn::Ident,
ctx: &Ctx,
) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
quote! {
#pyo3_path::impl_::deprecations::inspect_type(#tokens, &#gil_refs_checker)
}
}
pub fn impl_arg_params(
spec: &FnSpec<'_>,
self_: Option<&syn::Type>,
@ -119,9 +62,7 @@ pub fn impl_arg_params(
let from_py_with = &arg.from_py_with()?.value;
let from_py_with_holder = format_ident!("from_py_with_{}", i);
Some(quote_spanned! { from_py_with.span() =>
let e = #pyo3_path::impl_::deprecations::GilRefs::new();
let #from_py_with_holder = #pyo3_path::impl_::deprecations::inspect_fn(#from_py_with, &e);
e.from_py_with_arg();
let #from_py_with_holder = #from_py_with;
})
})
.collect::<TokenStream>();
@ -250,8 +191,7 @@ fn impl_arg_param(
let from_py_with = format_ident!("from_py_with_{}", pos);
let arg_value = quote!(#args_array[#option_pos].as_deref());
*option_pos += 1;
let tokens = impl_regular_arg_param(arg, from_py_with, arg_value, holders, ctx);
check_arg_for_gil_refs(tokens, holders.push_gil_refs_checker(arg.ty.span()), ctx)
impl_regular_arg_param(arg, from_py_with, arg_value, holders, ctx)
}
FnArg::VarArgs(arg) => {
let holder = holders.push_holder(arg.name.span());

View File

@ -12,7 +12,6 @@ use crate::attributes::{
self, kw, take_pyo3_options, CrateAttribute, ExtendsAttribute, FreelistAttribute,
ModuleAttribute, NameAttribute, NameLitStr, RenameAllAttribute,
};
use crate::deprecations::Deprecations;
use crate::konst::{ConstAttributes, ConstSpec};
use crate::method::{FnArg, FnSpec, PyArg, RegularArg};
use crate::pyfunction::ConstructorAttribute;
@ -232,17 +231,19 @@ pub fn build_py_class(
if let Some(lt) = class.generics.lifetimes().next() {
bail_spanned!(
lt.span() =>
"#[pyclass] cannot have lifetime parameters. \
For an explanation, see https://pyo3.rs/latest/class.html#no-lifetime-parameters"
lt.span() => concat!(
"#[pyclass] cannot have lifetime parameters. For an explanation, see \
https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#no-lifetime-parameters"
)
);
}
ensure_spanned!(
class.generics.params.is_empty(),
class.generics.span() =>
"#[pyclass] cannot have generic parameters. \
For an explanation, see https://pyo3.rs/latest/class.html#no-generic-parameters"
class.generics.span() => concat!(
"#[pyclass] cannot have generic parameters. For an explanation, see \
https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#no-generic-parameters"
)
);
let mut field_options: Vec<(&syn::Field, FieldPyO3Options)> = match &mut class.fields {
@ -384,7 +385,7 @@ fn impl_class(
ctx: &Ctx,
) -> syn::Result<TokenStream> {
let Ctx { pyo3_path, .. } = ctx;
let pytypeinfo_impl = impl_pytypeinfo(cls, args, None, ctx);
let pytypeinfo_impl = impl_pytypeinfo(cls, args, ctx);
let (default_richcmp, default_richcmp_slot) =
pyclass_richcmp(&args.options, &syn::parse_quote!(#cls), ctx)?;
@ -779,7 +780,7 @@ fn impl_simple_enum(
let cls = simple_enum.ident;
let ty: syn::Type = syn::parse_quote!(#cls);
let variants = simple_enum.variants;
let pytypeinfo = impl_pytypeinfo(cls, args, None, ctx);
let pytypeinfo = impl_pytypeinfo(cls, args, ctx);
for variant in &variants {
ensure_spanned!(variant.options.constructor.is_none(), variant.options.constructor.span() => "`constructor` can't be used on a simple enum variant");
@ -889,7 +890,7 @@ fn impl_complex_enum(
let ctx = &Ctx::new(&args.options.krate, None);
let cls = complex_enum.ident;
let variants = complex_enum.variants;
let pytypeinfo = impl_pytypeinfo(cls, &args, None, ctx);
let pytypeinfo = impl_pytypeinfo(cls, &args, ctx);
let (default_richcmp, default_richcmp_slot) = pyclass_richcmp(&args.options, &ty, ctx)?;
let (default_hash, default_hash_slot) = pyclass_hash(&args.options, &ty, ctx)?;
@ -977,7 +978,7 @@ fn impl_complex_enum(
},
};
let variant_cls_pytypeinfo = impl_pytypeinfo(&variant_cls, &variant_args, None, ctx);
let variant_cls_pytypeinfo = impl_pytypeinfo(&variant_cls, &variant_args, ctx);
variant_cls_pytypeinfos.push(variant_cls_pytypeinfo);
let (variant_cls_impl, field_getters, mut slots) =
@ -1057,7 +1058,6 @@ fn impl_complex_enum_variant_match_args(
attributes: ConstAttributes {
is_class_attr: true,
name: None,
deprecations: Deprecations::new(ctx),
},
};
@ -1318,7 +1318,6 @@ fn generate_protocol_slot(
&mut method.sig,
&mut Vec::new(),
PyFunctionOptions::default(),
ctx,
)
.unwrap();
slot.generate_type_slot(&syn::parse_quote!(#cls), &spec, name, ctx)
@ -1334,7 +1333,6 @@ fn generate_default_protocol_slot(
&mut method.sig,
&mut Vec::new(),
PyFunctionOptions::default(),
ctx,
)
.unwrap();
let name = spec.name.to_string();
@ -1360,7 +1358,6 @@ fn simple_enum_default_methods<'a>(
kw: syn::parse_quote! { name },
value: NameLitStr(py_ident.clone()),
}),
deprecations: Deprecations::new(ctx),
},
};
unit_variant_names
@ -1383,7 +1380,6 @@ fn complex_enum_default_methods<'a>(
kw: syn::parse_quote! { name },
value: NameLitStr(py_ident.clone()),
}),
deprecations: Deprecations::new(ctx),
},
};
variant_names
@ -1397,19 +1393,17 @@ fn complex_enum_default_methods<'a>(
pub fn gen_complex_enum_variant_attr(
cls: &syn::Ident,
cls_type: &syn::Type,
spec: &ConstSpec<'_>,
spec: &ConstSpec,
ctx: &Ctx,
) -> MethodAndMethodDef {
let Ctx { pyo3_path, .. } = ctx;
let member = &spec.rust_ident;
let wrapper_ident = format_ident!("__pymethod_variant_cls_{}__", member);
let deprecations = &spec.attributes.deprecations;
let python_name = spec.null_terminated_python_name(ctx);
let variant_cls = format_ident!("{}_{}", cls, member);
let associated_method = quote! {
fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::PyObject> {
#deprecations
::std::result::Result::Ok(py.get_type_bound::<#variant_cls>().into_any().unbind())
}
};
@ -1497,7 +1491,6 @@ fn complex_enum_struct_variant_new<'a>(
text_signature: None,
asyncness: None,
unsafety: None,
deprecations: Deprecations::new(ctx),
};
crate::pymethod::impl_py_method_def_new(&variant_cls_type, &spec, ctx)
@ -1552,7 +1545,6 @@ fn complex_enum_tuple_variant_new<'a>(
text_signature: None,
asyncness: None,
unsafety: None,
deprecations: Deprecations::new(ctx),
};
crate::pymethod::impl_py_method_def_new(&variant_cls_type, &spec, ctx)
@ -1577,7 +1569,6 @@ fn complex_enum_variant_field_getter<'a>(
text_signature: None,
asyncness: None,
unsafety: None,
deprecations: Deprecations::new(ctx),
};
let property_type = crate::pymethod::PropertyType::Function {
@ -1641,12 +1632,7 @@ fn descriptors_to_items(
Ok(items)
}
fn impl_pytypeinfo(
cls: &syn::Ident,
attr: &PyClassArgs,
deprecations: Option<&Deprecations<'_>>,
ctx: &Ctx,
) -> TokenStream {
fn impl_pytypeinfo(cls: &syn::Ident, attr: &PyClassArgs, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
let cls_name = get_class_python_name(cls, attr).to_string();
@ -1677,8 +1663,6 @@ fn impl_pytypeinfo(
#[inline]
fn type_object_raw(py: #pyo3_path::Python<'_>) -> *mut #pyo3_path::ffi::PyTypeObject {
use #pyo3_path::prelude::PyTypeMethods;
#deprecations
<#cls as #pyo3_path::impl_::pyclass::PyClassImpl>::lazy_type_object()
.get_or_init(py)
.as_type_ptr()

View File

@ -4,7 +4,6 @@ use crate::{
self, get_pyo3_options, take_attributes, take_pyo3_options, CrateAttribute,
FromPyWithAttribute, NameAttribute, TextSignatureAttribute,
},
deprecations::Deprecations,
method::{self, CallingConvention, FnArg},
pymethod::check_generic,
};
@ -252,7 +251,6 @@ pub fn impl_wrap_pyfunction(
text_signature,
asyncness: func.sig.asyncness,
unsafety: func.sig.unsafety,
deprecations: Deprecations::new(ctx),
};
let vis = &func.vis;

View File

@ -130,7 +130,7 @@ pub fn impl_methods(
}
syn::ImplItem::Const(konst) => {
let ctx = &Ctx::new(&options.krate, None);
let attributes = ConstAttributes::from_attrs(&mut konst.attrs, ctx)?;
let attributes = ConstAttributes::from_attrs(&mut konst.attrs)?;
if attributes.is_class_attr {
let spec = ConstSpec {
rust_ident: konst.ident.clone(),
@ -182,16 +182,14 @@ pub fn impl_methods(
})
}
pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec<'_>, ctx: &Ctx) -> MethodAndMethodDef {
pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec, ctx: &Ctx) -> MethodAndMethodDef {
let member = &spec.rust_ident;
let wrapper_ident = format_ident!("__pymethod_{}__", member);
let deprecations = &spec.attributes.deprecations;
let python_name = spec.null_terminated_python_name(ctx);
let Ctx { pyo3_path, .. } = ctx;
let associated_method = quote! {
fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::PyObject> {
#deprecations
::std::result::Result::Ok(#pyo3_path::IntoPy::into_py(#cls::#member, py))
}
};

View File

@ -4,7 +4,7 @@ use std::ffi::CString;
use crate::attributes::{NameAttribute, RenamingRule};
use crate::deprecations::deprecate_trailing_option_default;
use crate::method::{CallingConvention, ExtractErrorMode, PyArg};
use crate::params::{check_arg_for_gil_refs, impl_regular_arg_param, Holders};
use crate::params::{impl_regular_arg_param, Holders};
use crate::utils::PythonDoc;
use crate::utils::{Ctx, LitCStr};
use crate::{
@ -164,9 +164,8 @@ impl<'a> PyMethod<'a> {
sig: &'a mut syn::Signature,
meth_attrs: &mut Vec<syn::Attribute>,
options: PyFunctionOptions,
ctx: &'a Ctx,
) -> Result<Self> {
let spec = FnSpec::parse(sig, meth_attrs, options, ctx)?;
let spec = FnSpec::parse(sig, meth_attrs, options)?;
let method_name = spec.python_name.to_string();
let kind = PyMethodKind::from_name(&method_name);
@ -195,7 +194,7 @@ pub fn gen_py_method(
) -> Result<GeneratedPyMethod> {
check_generic(sig)?;
ensure_function_options_valid(&options)?;
let method = PyMethod::parse(sig, meth_attrs, options, ctx)?;
let method = PyMethod::parse(sig, meth_attrs, options)?;
let spec = &method.spec;
let Ctx { pyo3_path, .. } = ctx;
@ -356,7 +355,6 @@ pub fn impl_py_method_def_new(
|| quote!(::std::option::Option::None),
|text_signature| quote!(::std::option::Option::Some(#text_signature)),
);
let deprecations = &spec.deprecations;
let slot_def = quote! {
#pyo3_path::ffi::PyType_Slot {
slot: #pyo3_path::ffi::Py_tp_new,
@ -365,10 +363,7 @@ pub fn impl_py_method_def_new(
subtype: *mut #pyo3_path::ffi::PyTypeObject,
args: *mut #pyo3_path::ffi::PyObject,
kwargs: *mut #pyo3_path::ffi::PyObject,
) -> *mut #pyo3_path::ffi::PyObject
{
#deprecations
) -> *mut #pyo3_path::ffi::PyObject {
use #pyo3_path::impl_::pyclass::*;
#[allow(unknown_lints, non_local_definitions)]
impl PyClassNewTextSignature<#cls> for PyClassImplCollector<#cls> {
@ -612,21 +607,18 @@ pub fn impl_py_setter_def(
PropertyType::Function { spec, .. } => {
let (_, args) = split_off_python_arg(&spec.signature.arguments);
let value_arg = &args[0];
let (from_py_with, ident) = if let Some(from_py_with) =
&value_arg.from_py_with().as_ref().map(|f| &f.value)
{
let ident = syn::Ident::new("from_py_with", from_py_with.span());
(
quote_spanned! { from_py_with.span() =>
let e = #pyo3_path::impl_::deprecations::GilRefs::new();
let #ident = #pyo3_path::impl_::deprecations::inspect_fn(#from_py_with, &e);
e.from_py_with_arg();
},
ident,
)
} else {
(quote!(), syn::Ident::new("dummy", Span::call_site()))
};
let (from_py_with, ident) =
if let Some(from_py_with) = &value_arg.from_py_with().as_ref().map(|f| &f.value) {
let ident = syn::Ident::new("from_py_with", from_py_with.span());
(
quote_spanned! { from_py_with.span() =>
let #ident = #from_py_with;
},
ident,
)
} else {
(quote!(), syn::Ident::new("dummy", Span::call_site()))
};
let arg = if let FnArg::Regular(arg) = &value_arg {
arg
@ -634,15 +626,13 @@ pub fn impl_py_setter_def(
bail_spanned!(value_arg.name().span() => "The #[setter] value argument can't be *args, **kwargs or `cancel_handle`.");
};
let tokens = impl_regular_arg_param(
let extract = impl_regular_arg_param(
arg,
ident,
quote!(::std::option::Option::Some(_value.into())),
&mut holders,
ctx,
);
let extract =
check_arg_for_gil_refs(tokens, holders.push_gil_refs_checker(arg.ty.span()), ctx);
let deprecation = deprecate_trailing_option_default(spec);
quote! {
@ -660,12 +650,8 @@ pub fn impl_py_setter_def(
.unwrap_or_default();
let holder = holders.push_holder(span);
let gil_refs_checker = holders.push_gil_refs_checker(span);
quote! {
let _val = #pyo3_path::impl_::deprecations::inspect_type(
#pyo3_path::impl_::extract_argument::extract_argument(_value.into(), &mut #holder, #name)?,
&#gil_refs_checker
);
let _val = #pyo3_path::impl_::extract_argument::extract_argument(_value.into(), &mut #holder, #name)?;
}
}
};
@ -682,7 +668,6 @@ pub fn impl_py_setter_def(
}
let init_holders = holders.init_holders(ctx);
let check_gil_refs = holders.check_gil_refs();
let associated_method = quote! {
#cfg_attrs
unsafe fn #wrapper_ident(
@ -698,7 +683,6 @@ pub fn impl_py_setter_def(
#init_holders
#extract
let result = #setter_impl;
#check_gil_refs
#pyo3_path::callback::convert(py, result)
}
};
@ -824,7 +808,6 @@ pub fn impl_py_getter_def(
};
let init_holders = holders.init_holders(ctx);
let check_gil_refs = holders.check_gil_refs();
let associated_method = quote! {
#cfg_attrs
unsafe fn #wrapper_ident(
@ -833,7 +816,6 @@ pub fn impl_py_getter_def(
) -> #pyo3_path::PyResult<*mut #pyo3_path::ffi::PyObject> {
#init_holders
let result = #body;
#check_gil_refs
result
}
};
@ -1139,35 +1121,30 @@ fn extract_object(
ctx: &Ctx,
) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
let gil_refs_checker = holders.push_gil_refs_checker(arg.ty().span());
let name = arg.name().unraw().to_string();
let extract = if let Some(from_py_with) =
arg.from_py_with().map(|from_py_with| &from_py_with.value)
{
let from_py_with_checker = holders.push_from_py_with_checker(from_py_with.span());
quote! {
#pyo3_path::impl_::extract_argument::from_py_with(
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr).0,
#name,
#pyo3_path::impl_::deprecations::inspect_fn(#from_py_with, &#from_py_with_checker) as fn(_) -> _,
)
}
} else {
let holder = holders.push_holder(Span::call_site());
quote! {
#pyo3_path::impl_::extract_argument::extract_argument(
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr).0,
&mut #holder,
#name
)
}
};
let extract =
if let Some(from_py_with) = arg.from_py_with().map(|from_py_with| &from_py_with.value) {
quote! {
#pyo3_path::impl_::extract_argument::from_py_with(
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr).0,
#name,
#from_py_with as fn(_) -> _,
)
}
} else {
let holder = holders.push_holder(Span::call_site());
quote! {
#pyo3_path::impl_::extract_argument::extract_argument(
#pyo3_path::impl_::pymethods::BoundRef::ref_from_ptr(py, &#source_ptr).0,
&mut #holder,
#name
)
}
};
let extracted = extract_error_mode.handle_error(extract, ctx);
quote! {
#pyo3_path::impl_::deprecations::inspect_type(#extracted, &#gil_refs_checker)
}
quote!(#extracted)
}
enum ReturnMode {
@ -1177,15 +1154,13 @@ enum ReturnMode {
}
impl ReturnMode {
fn return_call_output(&self, call: TokenStream, ctx: &Ctx, holders: &Holders) -> TokenStream {
fn return_call_output(&self, call: TokenStream, ctx: &Ctx) -> TokenStream {
let Ctx { pyo3_path, .. } = ctx;
let check_gil_refs = holders.check_gil_refs();
match self {
ReturnMode::Conversion(conversion) => {
let conversion = TokenGeneratorCtx(*conversion, ctx);
quote! {
let _result: #pyo3_path::PyResult<#conversion> = #pyo3_path::callback::convert(py, #call);
#check_gil_refs
#pyo3_path::callback::convert(py, _result)
}
}
@ -1195,14 +1170,12 @@ impl ReturnMode {
quote! {
let _result = #call;
use #pyo3_path::impl_::pymethods::{#traits};
#check_gil_refs
(&_result).#tag().convert(py, _result)
}
}
ReturnMode::ReturnSelf => quote! {
let _result: #pyo3_path::PyResult<()> = #pyo3_path::callback::convert(py, #call);
_result?;
#check_gil_refs
#pyo3_path::ffi::Py_XINCREF(_raw_slf);
::std::result::Result::Ok(_raw_slf)
},
@ -1369,12 +1342,10 @@ fn generate_method_body(
let args = extract_proto_arguments(spec, arguments, extract_error_mode, holders, ctx)?;
let call = quote! { #cls::#rust_name(#self_arg #(#args),*) };
Ok(if let Some(return_mode) = return_mode {
return_mode.return_call_output(call, ctx, holders)
return_mode.return_call_output(call, ctx)
} else {
let check_gil_refs = holders.check_gil_refs();
quote! {
let result = #call;
#check_gil_refs;
#pyo3_path::callback::convert(py, result)
}
})

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3-macros"
version = "0.22.1"
version = "0.23.0-dev"
description = "Proc macros for PyO3 package"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]
@ -22,7 +22,7 @@ gil-refs = ["pyo3-macros-backend/gil-refs"]
proc-macro2 = { version = "1.0.60", default-features = false }
quote = "1"
syn = { version = "2", features = ["full", "extra-traits"] }
pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.22.1" }
pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.23.0-dev" }
[lints]
workspace = true

View File

@ -3,14 +3,14 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use proc_macro2::TokenStream as TokenStream2;
use pyo3_macros_backend::{
build_derive_from_pyobject, build_py_class, build_py_enum, build_py_function, build_py_methods,
pymodule_function_impl, pymodule_module_impl, PyClassArgs, PyClassMethodsType,
PyFunctionOptions,
PyFunctionOptions, PyModuleOptions,
};
use quote::quote;
use syn::{parse::Nothing, parse_macro_input, Item};
use syn::{parse_macro_input, Item};
/// A proc macro used to implement Python modules.
///
@ -24,6 +24,9 @@ use syn::{parse::Nothing, parse_macro_input, Item};
/// | Annotation | Description |
/// | :- | :- |
/// | `#[pyo3(name = "...")]` | Defines the name of the module in Python. |
/// | `#[pyo3(submodule)]` | Skips adding a `PyInit_` FFI symbol to the compiled binary. |
/// | `#[pyo3(module = "...")]` | Defines the Python `dotted.path` to the parent module for use in introspection. |
/// | `#[pyo3(crate = "pyo3")]` | Defines the path to PyO3 to use code generated by the macro. |
///
/// For more on creating Python modules see the [module section of the guide][1].
///
@ -32,35 +35,32 @@ use syn::{parse::Nothing, parse_macro_input, Item};
/// `#[pymodule]` implementation generates a hidden module with the same name containing
/// metadata about the module, which is used by `wrap_pymodule!`).
///
/// [1]: https://pyo3.rs/latest/module.html
#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/module.html")]
#[proc_macro_attribute]
pub fn pymodule(args: TokenStream, input: TokenStream) -> TokenStream {
match parse_macro_input!(input as Item) {
let options = parse_macro_input!(args as PyModuleOptions);
let mut ast = parse_macro_input!(input as Item);
let expanded = match &mut ast {
Item::Mod(module) => {
let is_submodule = match parse_macro_input!(args as Option<syn::Ident>) {
Some(i) if i == "submodule" => true,
Some(_) => {
return syn::Error::new(
Span::call_site(),
"#[pymodule] only accepts submodule as an argument",
)
.into_compile_error()
.into();
}
None => false,
};
pymodule_module_impl(module, is_submodule)
}
Item::Fn(function) => {
parse_macro_input!(args as Nothing);
pymodule_function_impl(function)
match pymodule_module_impl(module, options) {
// #[pymodule] on a module will rebuild the original ast, so we don't emit it here
Ok(expanded) => return expanded.into(),
Err(e) => Err(e),
}
}
Item::Fn(function) => pymodule_function_impl(function, options),
unsupported => Err(syn::Error::new_spanned(
unsupported,
"#[pymodule] only supports modules and functions.",
)),
}
.unwrap_or_compile_error()
.unwrap_or_compile_error();
quote!(
#ast
#expanded
)
.into()
}
@ -99,17 +99,17 @@ pub fn pyclass(attr: TokenStream, input: TokenStream) -> TokenStream {
/// multiple `#[pymethods]` blocks for a single `#[pyclass]`.
/// This will add a transitive dependency on the [`inventory`][3] crate.
///
/// [1]: https://pyo3.rs/latest/class.html#instance-methods
/// [2]: https://pyo3.rs/latest/features.html#multiple-pymethods
#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#instance-methods")]
#[doc = concat!("[2]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/features.html#multiple-pymethods")]
/// [3]: https://docs.rs/inventory/
/// [4]: https://pyo3.rs/latest/class.html#constructor
/// [5]: https://pyo3.rs/latest/class.html#object-properties-using-getter-and-setter
/// [6]: https://pyo3.rs/latest/class.html#static-methods
/// [7]: https://pyo3.rs/latest/class.html#class-methods
/// [8]: https://pyo3.rs/latest/class.html#callable-objects
/// [9]: https://pyo3.rs/latest/class.html#class-attributes
/// [10]: https://pyo3.rs/latest/class.html#method-arguments
/// [11]: https://pyo3.rs/latest/class.html#object-properties-using-pyo3get-set
#[doc = concat!("[4]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#constructor")]
#[doc = concat!("[5]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#object-properties-using-getter-and-setter")]
#[doc = concat!("[6]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#static-methods")]
#[doc = concat!("[7]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#class-methods")]
#[doc = concat!("[8]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#callable-objects")]
#[doc = concat!("[9]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#class-attributes")]
#[doc = concat!("[10]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#method-arguments")]
#[doc = concat!("[11]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#object-properties-using-pyo3get-set")]
#[proc_macro_attribute]
pub fn pymethods(attr: TokenStream, input: TokenStream) -> TokenStream {
let methods_type = if cfg!(feature = "multiple-pymethods") {
@ -138,7 +138,7 @@ pub fn pymethods(attr: TokenStream, input: TokenStream) -> TokenStream {
/// `#[pyfunction]` implementation generates a hidden module with the same name containing
/// metadata about the function, which is used by `wrap_pyfunction!`).
///
/// [1]: https://pyo3.rs/latest/function.html
#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/function.html")]
#[proc_macro_attribute]
pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as syn::ItemFn);

View File

@ -3,7 +3,7 @@
[tool.towncrier]
filename = "CHANGELOG.md"
version = "0.22.1"
version = "0.23.0-dev"
start_string = "<!-- towncrier release notes start -->\n"
template = ".towncrier.template.md"
title_format = "## [{version}] - {project_date}"

View File

@ -8,10 +8,7 @@ use crate::types::PyTuple;
use crate::{ffi, Borrowed, Bound, Py, PyAny, PyClass, PyObject, PyRef, PyRefMut, Python};
#[cfg(feature = "gil-refs")]
use {
crate::{
err::{self, PyDowncastError},
gil, PyNativeType,
},
crate::{err, gil, PyNativeType},
std::ptr::NonNull,
};
@ -395,121 +392,6 @@ where
}
}
/// Trait implemented by Python object types that allow a checked downcast.
/// If `T` implements `PyTryFrom`, we can convert `&PyAny` to `&T`.
///
/// This trait is similar to `std::convert::TryFrom`
#[cfg(feature = "gil-refs")]
#[deprecated(since = "0.21.0")]
pub trait PyTryFrom<'v>: Sized + PyNativeType {
/// Cast from a concrete Python object type to PyObject.
#[deprecated(
since = "0.21.0",
note = "use `value.downcast::<T>()` instead of `T::try_from(value)`"
)]
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>>;
/// Cast from a concrete Python object type to PyObject. With exact type check.
#[deprecated(
since = "0.21.0",
note = "use `value.downcast_exact::<T>()` instead of `T::try_from_exact(value)`"
)]
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>>;
/// Cast a PyAny to a specific type of PyObject. The caller must
/// have already verified the reference is for this type.
///
/// # Safety
///
/// Callers must ensure that the type is valid or risk type confusion.
#[deprecated(
since = "0.21.0",
note = "use `value.downcast_unchecked::<T>()` instead of `T::try_from_unchecked(value)`"
)]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self;
}
/// Trait implemented by Python object types that allow a checked downcast.
/// This trait is similar to `std::convert::TryInto`
#[cfg(feature = "gil-refs")]
#[deprecated(since = "0.21.0")]
pub trait PyTryInto<T>: Sized {
/// Cast from PyObject to a concrete Python object type.
#[deprecated(
since = "0.21.0",
note = "use `value.downcast()` instead of `value.try_into()`"
)]
fn try_into(&self) -> Result<&T, PyDowncastError<'_>>;
/// Cast from PyObject to a concrete Python object type. With exact type check.
#[deprecated(
since = "0.21.0",
note = "use `value.downcast()` instead of `value.try_into_exact()`"
)]
fn try_into_exact(&self) -> Result<&T, PyDowncastError<'_>>;
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
mod implementations {
use super::*;
use crate::type_object::PyTypeInfo;
// TryFrom implies TryInto
impl<U> PyTryInto<U> for PyAny
where
U: for<'v> PyTryFrom<'v>,
{
fn try_into(&self) -> Result<&U, PyDowncastError<'_>> {
<U as PyTryFrom<'_>>::try_from(self)
}
fn try_into_exact(&self) -> Result<&U, PyDowncastError<'_>> {
U::try_from_exact(self)
}
}
impl<'v, T> PyTryFrom<'v> for T
where
T: PyTypeInfo<AsRefTarget = Self> + PyNativeType,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
value.into().downcast()
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
value.into().downcast_exact()
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
value.into().downcast_unchecked()
}
}
impl<'v, T> PyTryFrom<'v> for crate::PyCell<T>
where
T: 'v + PyClass,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
value.into().downcast()
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_exact_type_of(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, T::NAME))
}
}
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
value.into().downcast_unchecked()
}
}
}
/// Converts `()` to an empty Python tuple.
impl IntoPy<Py<PyTuple>> for () {
fn into_py(self, py: Python<'_>) -> Py<PyTuple> {
@ -665,51 +547,3 @@ where
/// })
/// ```
mod test_no_clone {}
#[cfg(test)]
mod tests {
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
mod deprecated {
use super::super::PyTryFrom;
use crate::types::{IntoPyDict, PyAny, PyDict, PyList};
use crate::{Python, ToPyObject};
#[test]
fn test_try_from() {
Python::with_gil(|py| {
let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py);
let dict: &PyAny = vec![("reverse", true)].into_py_dict(py).as_ref();
assert!(<PyList as PyTryFrom<'_>>::try_from(list).is_ok());
assert!(<PyDict as PyTryFrom<'_>>::try_from(dict).is_ok());
assert!(<PyAny as PyTryFrom<'_>>::try_from(list).is_ok());
assert!(<PyAny as PyTryFrom<'_>>::try_from(dict).is_ok());
});
}
#[test]
fn test_try_from_exact() {
Python::with_gil(|py| {
let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py);
let dict: &PyAny = vec![("reverse", true)].into_py_dict(py).as_ref();
assert!(PyList::try_from_exact(list).is_ok());
assert!(PyDict::try_from_exact(dict).is_ok());
assert!(PyAny::try_from_exact(list).is_err());
assert!(PyAny::try_from_exact(dict).is_err());
});
}
#[test]
fn test_try_from_unchecked() {
Python::with_gil(|py| {
let list = PyList::new(py, [1, 2, 3]);
let val = unsafe { <PyList as PyTryFrom>::try_from_unchecked(list.as_ref()) };
assert!(list.is(val));
});
}
}
}

View File

@ -8,7 +8,6 @@
#[cfg(feature = "experimental-async")]
pub mod coroutine;
pub mod deprecations;
pub mod exceptions;
pub mod extract_argument;
pub mod freelist;

View File

@ -1,76 +0,0 @@
//! Symbols used to denote deprecated usages of PyO3's proc macros.
use crate::{PyResult, Python};
#[deprecated(since = "0.20.0", note = "use `#[new]` instead of `#[__new__]`")]
pub const PYMETHODS_NEW_DEPRECATED_FORM: () = ();
pub fn inspect_type<T>(t: T, _: &GilRefs<T>) -> T {
t
}
pub fn inspect_fn<A, T>(f: fn(A) -> PyResult<T>, _: &GilRefs<A>) -> fn(A) -> PyResult<T> {
f
}
pub struct GilRefs<T>(OptionGilRefs<T>);
pub struct OptionGilRefs<T>(NotAGilRef<T>);
pub struct NotAGilRef<T>(std::marker::PhantomData<T>);
pub trait IsGilRef {}
#[cfg(feature = "gil-refs")]
impl<T: crate::PyNativeType> IsGilRef for &'_ T {}
impl<T> GilRefs<T> {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
GilRefs(OptionGilRefs(NotAGilRef(std::marker::PhantomData)))
}
}
impl GilRefs<Python<'_>> {
#[deprecated(since = "0.21.0", note = "use `wrap_pyfunction_bound!` instead")]
pub fn is_python(&self) {}
}
impl<T: IsGilRef> GilRefs<T> {
#[deprecated(
since = "0.21.0",
note = "use `&Bound<'_, T>` instead for this function argument"
)]
pub fn function_arg(&self) {}
#[deprecated(
since = "0.21.0",
note = "use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor"
)]
pub fn from_py_with_arg(&self) {}
}
impl<T: IsGilRef> OptionGilRefs<Option<T>> {
#[deprecated(
since = "0.21.0",
note = "use `Option<&Bound<'_, T>>` instead for this function argument"
)]
pub fn function_arg(&self) {}
}
impl<T> NotAGilRef<T> {
pub fn function_arg(&self) {}
pub fn from_py_with_arg(&self) {}
pub fn is_python(&self) {}
}
impl<T> std::ops::Deref for GilRefs<T> {
type Target = OptionGilRefs<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::Deref for OptionGilRefs<T> {
type Target = NotAGilRef<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@ -65,7 +65,19 @@ pub unsafe trait PyNativeType: Sized {
}
}
/// A GIL-attached equivalent to `Py`.
/// A GIL-attached equivalent to [`Py<T>`].
///
/// This type can be thought of as equivalent to the tuple `(Py<T>, Python<'py>)`. By having the `'py`
/// lifetime of the [`Python<'py>`] token, this ties the lifetime of the [`Bound<'py, T>`] smart pointer
/// to the lifetime of the GIL and allows PyO3 to call Python APIs at maximum efficiency.
///
/// To access the object in situations where the GIL is not held, convert it to [`Py<T>`]
/// using [`.unbind()`][Bound::unbind]. This includes situations where the GIL is temporarily
/// released, such as [`Python::allow_threads`](crate::Python::allow_threads)'s closure.
///
/// See
#[doc = concat!("[the guide](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/types.html#boundpy-t)")]
/// for more detail.
#[repr(transparent)]
pub struct Bound<'py, T>(Python<'py>, ManuallyDrop<Py<T>>);
@ -732,7 +744,8 @@ impl<T> IntoPy<PyObject> for Borrowed<'_, '_, T> {
/// - [`Py::borrow`], [`Py::try_borrow`], [`Py::borrow_mut`], or [`Py::try_borrow_mut`],
///
/// to get a (mutable) reference to a contained pyclass, using a scheme similar to std's [`RefCell`].
/// See the [guide entry](https://pyo3.rs/latest/class.html#bound-and-interior-mutability)
/// See the
#[doc = concat!("[guide entry](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#bound-and-interior-mutability)")]
/// for more information.
/// - You can call methods directly on `Py` with [`Py::call_bound`], [`Py::call_method_bound`] and friends.
///
@ -1329,8 +1342,11 @@ impl<T> Py<T> {
/// # }
/// ```
#[inline]
pub fn clone_ref(&self, py: Python<'_>) -> Py<T> {
unsafe { Py::from_borrowed_ptr(py, self.0.as_ptr()) }
pub fn clone_ref(&self, _py: Python<'_>) -> Py<T> {
unsafe {
ffi::Py_INCREF(self.as_ptr());
Self::from_non_null(self.0)
}
}
/// Drops `self` and immediately decreases its reference count.
@ -1369,22 +1385,6 @@ impl<T> Py<T> {
unsafe { ffi::Py_None() == self.as_ptr() }
}
/// Returns whether the object is Ellipsis, e.g. `...`.
///
/// This is equivalent to the Python expression `self is ...`.
#[deprecated(since = "0.20.0", note = "use `.is(py.Ellipsis())` instead")]
pub fn is_ellipsis(&self) -> bool {
unsafe { ffi::Py_Ellipsis() == self.as_ptr() }
}
/// Returns whether the object is considered to be true.
///
/// This is equivalent to the Python expression `bool(self)`.
#[deprecated(since = "0.21.0", note = "use `.is_truthy()` instead")]
pub fn is_true(&self, py: Python<'_>) -> PyResult<bool> {
self.is_truthy(py)
}
/// Returns whether the object is considered to be true.
///
/// This applies truth value testing equivalent to the Python expression `bool(self)`.
@ -2158,23 +2158,6 @@ a = A()
})
}
#[test]
#[allow(deprecated)]
fn test_is_ellipsis() {
Python::with_gil(|py| {
let v = py
.eval_bound("...", None, None)
.map_err(|e| e.display(py))
.unwrap()
.to_object(py);
assert!(v.is_ellipsis());
let not_ellipsis = 5.to_object(py);
assert!(!not_ellipsis.is_ellipsis());
});
}
#[test]
fn test_debug_fmt() {
Python::with_gil(|py| {
@ -2374,18 +2357,5 @@ a = A()
}
})
}
#[test]
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
fn cell_tryfrom() {
use crate::{PyCell, PyTryInto};
// More detailed tests of the underlying semantics in pycell.rs
Python::with_gil(|py| {
let instance: &PyAny = Py::new(py, SomeClass(0)).unwrap().into_ref(py);
let _: &PyCell<SomeClass> = PyTryInto::try_into(instance).unwrap();
let _: &PyCell<SomeClass> = PyTryInto::try_into_exact(instance).unwrap();
})
}
}
}

View File

@ -295,32 +295,29 @@
//! [`rust_decimal`]: ./rust_decimal/index.html "Documenation about the `rust_decimal` feature."
//! [`Decimal`]: https://docs.rs/rust_decimal/latest/rust_decimal/struct.Decimal.html
//! [`serde`]: <./serde/index.html> "Documentation about the `serde` feature."
//! [calling_rust]: https://pyo3.rs/latest/python-from-rust.html "Calling Python from Rust - PyO3 user guide"
#![doc = concat!("[calling_rust]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/python-from-rust.html \"Calling Python from Rust - PyO3 user guide\"")]
//! [examples subdirectory]: https://github.com/PyO3/pyo3/tree/main/examples
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/features.html "Features - The Cargo Book"
//! [global interpreter lock]: https://docs.python.org/3/glossary.html#term-global-interpreter-lock
//! [hashbrown]: https://docs.rs/hashbrown
//! [smallvec]: https://docs.rs/smallvec
//! [indexmap]: https://docs.rs/indexmap
//! [manual_builds]: https://pyo3.rs/latest/building-and-distribution.html#manual-builds "Manual builds - Building and Distribution - PyO3 user guide"
#![doc = concat!("[manual_builds]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/building-and-distribution.html#manual-builds \"Manual builds - Building and Distribution - PyO3 user guide\"")]
//! [num-bigint]: https://docs.rs/num-bigint
//! [num-complex]: https://docs.rs/num-complex
//! [num-rational]: https://docs.rs/num-rational
//! [serde]: https://docs.rs/serde
//! [setuptools-rust]: https://github.com/PyO3/setuptools-rust "Setuptools plugin for Rust extensions"
//! [the guide]: https://pyo3.rs "PyO3 user guide"
//! [types]: https://pyo3.rs/latest/types.html "GIL lifetimes, mutability and Python object types"
#![doc = concat!("[types]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/types.html \"GIL lifetimes, mutability and Python object types\"")]
//! [PEP 384]: https://www.python.org/dev/peps/pep-0384 "PEP 384 -- Defining a Stable ABI"
//! [Python from Rust]: https://github.com/PyO3/pyo3#using-python-from-rust
//! [Rust from Python]: https://github.com/PyO3/pyo3#using-rust-from-python
//! [Features chapter of the guide]: https://pyo3.rs/latest/features.html#features-reference "Features Reference - PyO3 user guide"
#![doc = concat!("[Features chapter of the guide]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/features.html#features-reference \"Features Reference - PyO3 user guide\"")]
//! [`Ungil`]: crate::marker::Ungil
pub use crate::class::*;
pub use crate::conversion::{AsPyPointer, FromPyObject, IntoPy, ToPyObject};
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
pub use crate::conversion::{FromPyPointer, PyTryFrom, PyTryInto};
#[cfg(feature = "gil-refs")]
pub use crate::err::PyDowncastError;
pub use crate::err::{DowncastError, DowncastIntoError, PyErr, PyErrArguments, PyResult, ToPyErr};
#[cfg(feature = "gil-refs")]
@ -382,30 +379,6 @@ pub mod class {
pub use crate::pyclass::CompareOp;
}
/// Old module which contained some implementation details of the `#[pyproto]` module.
///
/// Prefer using the same content from `pyo3::pyclass`, e.g. `use pyo3::pyclass::IterANextOutput` instead
/// of `use pyo3::class::pyasync::IterANextOutput`.
///
/// For compatibility reasons this has not yet been removed, however will be done so
/// once <https://github.com/rust-lang/rust/issues/30827> is resolved.
pub mod pyasync {
#[allow(deprecated)]
pub use crate::pyclass::{IterANextOutput, PyIterANextOutput};
}
/// Old module which contained some implementation details of the `#[pyproto]` module.
///
/// Prefer using the same content from `pyo3::pyclass`, e.g. `use pyo3::pyclass::IterNextOutput` instead
/// of `use pyo3::class::pyasync::IterNextOutput`.
///
/// For compatibility reasons this has not yet been removed, however will be done so
/// once <https://github.com/rust-lang/rust/issues/30827> is resolved.
pub mod iter {
#[allow(deprecated)]
pub use crate::pyclass::{IterNextOutput, PyIterNextOutput};
}
/// Old module which contained some implementation details of the `#[pyproto]` module.
///
/// Prefer using the same content from `pyo3::pyclass`, e.g. `use pyo3::pyclass::PyTraverseError` instead
@ -461,7 +434,6 @@ pub mod marshal;
#[macro_use]
pub mod sync;
pub mod panic;
pub mod prelude;
pub mod pybacked;
pub mod pycell;
pub mod pyclass;
@ -484,7 +456,7 @@ pub use pyo3_macros::{pyfunction, pymethods, pymodule, FromPyObject};
/// For more on creating Python classes,
/// see the [class section of the guide][1].
///
/// [1]: https://pyo3.rs/latest/class.html
#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html")]
#[cfg(feature = "macros")]
pub use pyo3_macros::pyclass;
@ -495,15 +467,9 @@ mod macros;
#[cfg(feature = "experimental-inspect")]
pub mod inspect;
/// Ths module only contains re-exports of pyo3 deprecation warnings and exists
/// purely to make compiler error messages nicer.
///
/// (The compiler uses this module in error messages, probably because it's a public
/// re-export at a shorter path than `pyo3::impl_::deprecations`.)
#[doc(hidden)]
pub mod deprecations {
pub use crate::impl_::deprecations::*;
}
// Putting the declaration of prelude at the end seems to help encourage rustc and rustdoc to prefer using
// other paths to the same items. (e.g. `pyo3::types::PyAnyMethods` instead of `pyo3::prelude::PyAnyMethods`).
pub mod prelude;
/// Test readme and user guide
#[cfg(doctest)]

View File

@ -145,12 +145,8 @@ macro_rules! wrap_pyfunction {
};
($function:path, $py_or_module:expr) => {{
use $function as wrapped_pyfunction;
let check_gil_refs = $crate::impl_::deprecations::GilRefs::new();
let py_or_module =
$crate::impl_::deprecations::inspect_type($py_or_module, &check_gil_refs);
check_gil_refs.is_python();
$crate::impl_::pyfunction::WrapPyFunctionArg::wrap_pyfunction(
py_or_module,
$py_or_module,
&wrapped_pyfunction::_PYO3_DEF,
)
}};

View File

@ -126,10 +126,10 @@ use crate::types::{
PyAny, PyDict, PyEllipsis, PyModule, PyNone, PyNotImplemented, PyString, PyType,
};
use crate::version::PythonVersionInfo;
use crate::{ffi, Bound, IntoPy, Py, PyObject, PyTypeInfo};
#[allow(deprecated)]
#[cfg(feature = "gil-refs")]
use crate::{gil::GILPool, FromPyPointer, PyNativeType};
use crate::{conversion::FromPyPointer, gil::GILPool, PyNativeType};
use crate::{ffi, Bound, IntoPy, Py, PyObject, PyTypeInfo};
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::os::raw::c_int;
@ -801,42 +801,6 @@ impl<'py> Python<'py> {
PythonVersionInfo::from_str(version_number_str).unwrap()
}
/// Registers the object in the release pool, and tries to downcast to specific type.
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "use `obj.downcast_bound::<T>(py)` instead of `py.checked_cast_as::<T>(obj)`"
)]
pub fn checked_cast_as<T>(
self,
obj: PyObject,
) -> Result<&'py T, crate::err::PyDowncastError<'py>>
where
T: crate::PyTypeCheck<AsRefTarget = T>,
{
#[allow(deprecated)]
obj.into_ref(self).downcast()
}
/// Registers the object in the release pool, and does an unchecked downcast
/// to the specific type.
///
/// # Safety
///
/// Callers must ensure that ensure that the cast is valid.
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "use `obj.downcast_bound_unchecked::<T>(py)` instead of `py.cast_as::<T>(obj)`"
)]
pub unsafe fn cast_as<T>(self, obj: PyObject) -> &'py T
where
T: crate::type_object::HasPyGilRef<AsRefTarget = T>,
{
#[allow(deprecated)]
obj.into_ref(self).downcast_unchecked()
}
/// Registers the object pointer in the release pool,
/// and does an unchecked downcast to the specific type.
///

View File

@ -9,9 +9,6 @@
//! ```
pub use crate::conversion::{FromPyObject, IntoPy, ToPyObject};
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
pub use crate::conversion::{PyTryFrom, PyTryInto};
pub use crate::err::{PyErr, PyResult};
pub use crate::instance::{Borrowed, Bound, Py, PyObject};
pub use crate::marker::Python;

View File

@ -1,8 +1,5 @@
//! `PyClass` and related traits.
use crate::{
callback::IntoPyCallbackOutput, ffi, impl_::pyclass::PyClassImpl, IntoPy, PyObject, PyResult,
PyTypeInfo, Python,
};
use crate::{ffi, impl_::pyclass::PyClassImpl, PyTypeInfo};
use std::{cmp::Ordering, os::raw::c_int};
mod create_type_object;
@ -99,111 +96,6 @@ impl CompareOp {
}
}
/// Output of `__next__` which can either `yield` the next value in the iteration, or
/// `return` a value to raise `StopIteration` in Python.
///
/// Usage example:
///
/// ```rust
/// # #![allow(deprecated)]
/// use pyo3::prelude::*;
/// use pyo3::iter::IterNextOutput;
///
/// #[pyclass]
/// struct PyClassIter {
/// count: usize,
/// }
///
/// #[pymethods]
/// impl PyClassIter {
/// #[new]
/// pub fn new() -> Self {
/// PyClassIter { count: 0 }
/// }
///
/// fn __next__(&mut self) -> IterNextOutput<usize, &'static str> {
/// if self.count < 5 {
/// self.count += 1;
/// // Given an instance `counter`, First five `next(counter)` calls yield 1, 2, 3, 4, 5.
/// IterNextOutput::Yield(self.count)
/// } else {
/// // At the sixth time, we get a `StopIteration` with `'Ended'`.
/// // try:
/// // next(counter)
/// // except StopIteration as e:
/// // assert e.value == 'Ended'
/// IterNextOutput::Return("Ended")
/// }
/// }
/// }
/// ```
#[deprecated(since = "0.21.0", note = "Use `Option` or `PyStopIteration` instead.")]
pub enum IterNextOutput<T, U> {
/// The value yielded by the iterator.
Yield(T),
/// The `StopIteration` object.
Return(U),
}
/// Alias of `IterNextOutput` with `PyObject` yield & return values.
#[deprecated(since = "0.21.0", note = "Use `Option` or `PyStopIteration` instead.")]
#[allow(deprecated)]
pub type PyIterNextOutput = IterNextOutput<PyObject, PyObject>;
#[allow(deprecated)]
impl<T, U> IntoPyCallbackOutput<*mut ffi::PyObject> for IterNextOutput<T, U>
where
T: IntoPy<PyObject>,
U: IntoPy<PyObject>,
{
fn convert(self, py: Python<'_>) -> PyResult<*mut ffi::PyObject> {
match self {
IterNextOutput::Yield(o) => Ok(o.into_py(py).into_ptr()),
IterNextOutput::Return(o) => {
Err(crate::exceptions::PyStopIteration::new_err(o.into_py(py)))
}
}
}
}
/// Output of `__anext__`.
///
/// <https://docs.python.org/3/reference/expressions.html#agen.__anext__>
#[deprecated(
since = "0.21.0",
note = "Use `Option` or `PyStopAsyncIteration` instead."
)]
pub enum IterANextOutput<T, U> {
/// An expression which the generator yielded.
Yield(T),
/// A `StopAsyncIteration` object.
Return(U),
}
/// An [IterANextOutput] of Python objects.
#[deprecated(
since = "0.21.0",
note = "Use `Option` or `PyStopAsyncIteration` instead."
)]
#[allow(deprecated)]
pub type PyIterANextOutput = IterANextOutput<PyObject, PyObject>;
#[allow(deprecated)]
impl<T, U> IntoPyCallbackOutput<*mut ffi::PyObject> for IterANextOutput<T, U>
where
T: IntoPy<PyObject>,
U: IntoPy<PyObject>,
{
fn convert(self, py: Python<'_>) -> PyResult<*mut ffi::PyObject> {
match self {
IterANextOutput::Yield(o) => Ok(o.into_py(py).into_ptr()),
IterANextOutput::Return(o) => Err(crate::exceptions::PyStopAsyncIteration::new_err(
o.into_py(py),
)),
}
}
}
/// A workaround for [associated const equality](https://github.com/rust-lang/rust/issues/92827).
///
/// This serves to have True / False values in the [`PyClass`] trait's `Frozen` type.

View File

@ -59,7 +59,9 @@ unsafe impl<T> Sync for GILProtected<T> where T: Send {}
/// Unlike `once_cell::sync` which blocks threads to achieve thread safety, this implementation
/// uses the Python GIL to mediate concurrent access. This helps in cases where `once_cell` or
/// `lazy_static`'s synchronization strategy can lead to deadlocks when interacting with the Python
/// GIL. For an example, see [the FAQ section](https://pyo3.rs/latest/faq.html) of the guide.
/// GIL. For an example, see
#[doc = concat!("[the FAQ section](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/faq.html)")]
/// of the guide.
///
/// Note that:
/// 1) `get_or_init` and `get_or_try_init` do not protect against infinite recursion

View File

@ -1,17 +1,13 @@
#![no_implicit_prelude]
#[derive(crate::FromPyObject)]
#[pyo3(crate = "crate")]
struct Derive1(i32); // newtype case
#[derive(crate::FromPyObject)]
#[pyo3(crate = "crate")]
struct Derive1(#[allow(dead_code)] i32); // newtype case
#[derive(crate::FromPyObject)]
#[pyo3(crate = "crate")]
#[allow(dead_code)]
struct Derive2(i32, i32); // tuple case
#[derive(crate::FromPyObject)]
#[pyo3(crate = "crate")]
#[allow(dead_code)]
struct Derive3 {
f: i32,
#[pyo3(item(42))]
@ -20,7 +16,6 @@ struct Derive3 {
#[derive(crate::FromPyObject)]
#[pyo3(crate = "crate")]
#[allow(dead_code)]
enum Derive4 {
A(i32),
B { f: i32 },
@ -29,23 +24,16 @@ enum Derive4 {
crate::create_exception!(mymodule, CustomError, crate::exceptions::PyException);
crate::import_exception!(socket, gaierror);
#[allow(dead_code)]
fn intern(py: crate::Python<'_>) {
let _foo = crate::intern!(py, "foo");
let _bar = crate::intern!(py, stringify!(bar));
}
#[allow(dead_code)]
#[cfg(not(PyPy))]
fn append_to_inittab() {
#[crate::pymodule]
#[pyo3(crate = "crate")]
#[allow(clippy::unnecessary_wraps)]
fn module_for_inittab(
_: crate::Python<'_>,
_: &crate::Bound<'_, crate::types::PyModule>,
) -> crate::PyResult<()> {
::std::result::Result::Ok(())
}
mod module_for_inittab {}
crate::append_to_inittab!(module_for_inittab);
}

View File

@ -1,3 +1,6 @@
#![no_implicit_prelude]
#![allow(dead_code, unused_variables, clippy::unnecessary_wraps)]
// The modules in this test are used to check PyO3 macro expansion is hygienic. By locating the test
// inside the crate the global `::pyo3` namespace is not available, so in combination with
// #[pyo3(crate = "crate")] this validates that all macro expansion respects the setting.

View File

@ -1,6 +1,3 @@
#![no_implicit_prelude]
#![allow(unused_variables)]
#[crate::pyclass]
#[pyo3(crate = "crate")]
#[derive(::std::clone::Clone)]

View File

@ -1,6 +1,3 @@
#![no_implicit_prelude]
#![allow(unused_variables, clippy::unnecessary_wraps)]
#[crate::pyfunction]
#[pyo3(crate = "crate")]
fn do_something(x: i32) -> crate::PyResult<i32> {
@ -8,19 +5,9 @@ fn do_something(x: i32) -> crate::PyResult<i32> {
}
#[test]
#[cfg(feature = "gil-refs")]
fn invoke_wrap_pyfunction() {
crate::Python::with_gil(|py| {
#[allow(deprecated)]
let func = crate::wrap_pyfunction!(do_something)(py).unwrap();
crate::py_run!(py, func, r#"func(5)"#);
});
}
#[test]
fn invoke_wrap_pyfunction_bound() {
crate::Python::with_gil(|py| {
let func = crate::wrap_pyfunction_bound!(do_something, py).unwrap();
let func = crate::wrap_pyfunction!(do_something, py).unwrap();
crate::py_run!(py, func, r#"func(5)"#);
});
}

View File

@ -1,6 +1,3 @@
#![no_implicit_prelude]
#![allow(unused_variables, clippy::unnecessary_wraps)]
#[crate::pyclass]
#[pyo3(crate = "crate")]
pub struct Dummy;

View File

@ -1,51 +1,36 @@
#![no_implicit_prelude]
#![allow(unused_variables, clippy::unnecessary_wraps)]
#[crate::pyfunction]
#[pyo3(crate = "crate")]
fn do_something(x: i32) -> crate::PyResult<i32> {
::std::result::Result::Ok(x)
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
#[crate::pymodule]
#[pyo3(crate = "crate")]
fn foo(_py: crate::Python<'_>, _m: &crate::types::PyModule) -> crate::PyResult<()> {
::std::result::Result::Ok(())
}
#[crate::pymodule]
#[pyo3(crate = "crate")]
fn foo_bound(
fn foo(
_py: crate::Python<'_>,
_m: &crate::Bound<'_, crate::types::PyModule>,
) -> crate::PyResult<()> {
::std::result::Result::Ok(())
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
#[crate::pymodule]
#[pyo3(crate = "crate")]
fn my_module(_py: crate::Python<'_>, m: &crate::types::PyModule) -> crate::PyResult<()> {
m.add_function(crate::wrap_pyfunction!(do_something, m)?)?;
m.add_wrapped(crate::wrap_pymodule!(foo))?;
::std::result::Result::Ok(())
}
#[crate::pymodule]
#[pyo3(crate = "crate")]
fn my_module_bound(m: &crate::Bound<'_, crate::types::PyModule>) -> crate::PyResult<()> {
fn my_module(m: &crate::Bound<'_, crate::types::PyModule>) -> crate::PyResult<()> {
<crate::Bound<'_, crate::types::PyModule> as crate::types::PyModuleMethods>::add_function(
m,
crate::wrap_pyfunction_bound!(do_something, m)?,
)?;
<crate::Bound<'_, crate::types::PyModule> as crate::types::PyModuleMethods>::add_wrapped(
m,
crate::wrap_pymodule!(foo_bound),
crate::wrap_pymodule!(foo),
)?;
::std::result::Result::Ok(())
}
#[crate::pymodule(submodule)]
#[pyo3(crate = "crate")]
mod my_module_declarative {
#[pymodule_export]
use super::{do_something, foo};
}

View File

@ -19,26 +19,15 @@ use std::os::raw::c_int;
/// Represents any Python object.
///
/// It currently only appears as a *reference*, `&PyAny`,
/// with a lifetime that represents the scope during which the GIL is held.
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyAny>`][crate::Py] or [`Bound<'py, PyAny>`][Bound].
///
/// `PyAny` has some interesting properties, which it shares
/// with the other [native Python types](crate::types):
/// For APIs available on all Python objects, see the [`PyAnyMethods`] trait which is implemented for
/// [`Bound<'py, PyAny>`][Bound].
///
/// - It can only be obtained and used while the GIL is held,
/// therefore its API does not require a [`Python<'py>`](crate::Python) token.
/// - It can't be used in situations where the GIL is temporarily released,
/// such as [`Python::allow_threads`](crate::Python::allow_threads)'s closure.
/// - The underlying Python object, if mutable, can be mutated through any reference.
/// - It can be converted to the GIL-independent [`Py`]`<`[`PyAny`]`>`,
/// allowing it to outlive the GIL scope. However, using [`Py`]`<`[`PyAny`]`>`'s API
/// *does* require a [`Python<'py>`](crate::Python) token.
///
/// It can be cast to a concrete type with PyAny::downcast (for native Python types only)
/// and FromPyObject::extract. See their documentation for more information.
///
/// See [the guide](https://pyo3.rs/latest/types.html) for an explanation
/// of the different Python object types.
/// See
#[doc = concat!("[the guide](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/types.html#concrete-python-types)")]
/// for an explanation of the different Python object types.
#[repr(transparent)]
pub struct PyAny(UnsafeCell<ffi::PyObject>);
@ -592,14 +581,6 @@ impl PyAny {
.map(Bound::into_gil_ref)
}
/// Returns whether the object is considered to be true.
///
/// This is equivalent to the Python expression `bool(self)`.
#[deprecated(since = "0.21.0", note = "use `.is_truthy()` instead")]
pub fn is_true(&self) -> PyResult<bool> {
self.is_truthy()
}
/// Returns whether the object is considered to be true.
///
/// This applies truth value testing equivalent to the Python expression `bool(self)`.
@ -615,14 +596,6 @@ impl PyAny {
self.as_borrowed().is_none()
}
/// Returns whether the object is Ellipsis, e.g. `...`.
///
/// This is equivalent to the Python expression `self is ...`.
#[deprecated(since = "0.20.0", note = "use `.is(py.Ellipsis())` instead")]
pub fn is_ellipsis(&self) -> bool {
self.as_borrowed().is_ellipsis()
}
/// Returns true if the sequence or mapping has a length of 0.
///
/// This is equivalent to the Python expression `len(self) == 0`.
@ -1506,6 +1479,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// Returns whether the object is Ellipsis, e.g. `...`.
///
/// This is equivalent to the Python expression `self is ...`.
#[deprecated(since = "0.23.0", note = "use `.is(py.Ellipsis())` instead")]
fn is_ellipsis(&self) -> bool;
/// Returns true if the sequence or mapping has a length of 0.
@ -2796,6 +2770,7 @@ class SimpleClass:
}
#[test]
#[allow(deprecated)]
fn test_is_ellipsis() {
Python::with_gil(|py| {
let v = py

View File

@ -11,6 +11,12 @@ use crate::{
use super::any::PyAnyMethods;
/// Represents a Python `bool`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyBool>`][crate::Py] or [`Bound<'py, PyBool>`][Bound].
///
/// For APIs available on `bool` objects, see the [`PyBoolMethods`] trait which is implemented for
/// [`Bound<'py, PyBool>`][Bound].
#[repr(transparent)]
pub struct PyBool(PyAny);

View File

@ -9,6 +9,12 @@ use crate::{AsPyPointer, PyNativeType};
use std::slice;
/// Represents a Python `bytearray`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyByteArray>`][crate::Py] or [`Bound<'py, PyByteArray>`][Bound].
///
/// For APIs available on `bytearray` objects, see the [`PyByteArrayMethods`] trait which is implemented for
/// [`Bound<'py, PyByteArray>`][Bound].
#[repr(transparent)]
pub struct PyByteArray(PyAny);

View File

@ -12,10 +12,16 @@ use std::str;
///
/// This type is immutable.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyBytes>`][crate::Py] or [`Bound<'py, PyBytes>`][Bound].
///
/// For APIs available on `bytes` objects, see the [`PyBytesMethods`] trait which is implemented for
/// [`Bound<'py, PyBytes>`][Bound].
///
/// # Equality
///
/// For convenience, [`Bound<'py, PyBytes>`] implements [`PartialEq<[u8]>`] to allow comparing the
/// data in the Python bytes to a Rust `[u8]`.
/// For convenience, [`Bound<'py, PyBytes>`][Bound] implements [`PartialEq<[u8]>`][PartialEq] to allow comparing the
/// data in the Python bytes to a Rust `[u8]` byte slice.
///
/// This is not always the most appropriate way to compare Python bytes, as Python bytes subclasses
/// may have different equality semantics. In situations where subclasses overriding equality might be

View File

@ -15,6 +15,11 @@ use std::os::raw::{c_char, c_int, c_void};
/// > in one module available to other modules, so the regular import mechanism can
/// > be used to access C APIs defined in dynamically loaded modules.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyCapsule>`][crate::Py] or [`Bound<'py, PyCapsule>`][Bound].
///
/// For APIs available on capsule objects, see the [`PyCapsuleMethods`] trait which is implemented for
/// [`Bound<'py, PyCapsule>`][Bound].
///
/// # Example
/// ```

View File

@ -2,6 +2,9 @@ use crate::ffi;
use crate::PyAny;
/// Represents a Python code object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyCode>`][crate::Py] or [`Bound<'py, PyCode>`][crate::Bound].
#[repr(transparent)]
pub struct PyCode(PyAny);

View File

@ -7,6 +7,12 @@ use std::os::raw::c_double;
/// Represents a Python [`complex`](https://docs.python.org/3/library/functions.html#complex) object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyComplex>`][crate::Py] or [`Bound<'py, PyComplex>`][Bound].
///
/// For APIs available on `complex` objects, see the [`PyComplexMethods`] trait which is implemented for
/// [`Bound<'py, PyComplex>`][Bound].
///
/// Note that `PyComplex` supports only basic operations. For advanced operations
/// consider using [num-complex](https://docs.rs/num-complex)'s [`Complex`] type instead.
/// This optional dependency can be activated with the `num-complex` feature flag.

View File

@ -191,7 +191,10 @@ pub trait PyTzInfoAccess<'py> {
fn get_tzinfo_bound(&self) -> Option<Bound<'py, PyTzInfo>>;
}
/// Bindings around `datetime.date`
/// Bindings around `datetime.date`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyDate>`][crate::Py] or [`Bound<'py, PyDate>`][Bound].
#[repr(transparent)]
pub struct PyDate(PyAny);
pyobject_native_type!(
@ -279,7 +282,10 @@ impl PyDateAccess for Bound<'_, PyDate> {
}
}
/// Bindings for `datetime.datetime`
/// Bindings for `datetime.datetime`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyDateTime>`][crate::Py] or [`Bound<'py, PyDateTime>`][Bound].
#[repr(transparent)]
pub struct PyDateTime(PyAny);
pyobject_native_type!(
@ -578,7 +584,10 @@ impl<'py> PyTzInfoAccess<'py> for Bound<'py, PyDateTime> {
}
}
/// Bindings for `datetime.time`
/// Bindings for `datetime.time`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyTime>`][crate::Py] or [`Bound<'py, PyTime>`][Bound].
#[repr(transparent)]
pub struct PyTime(PyAny);
pyobject_native_type!(
@ -781,6 +790,9 @@ impl<'py> PyTzInfoAccess<'py> for Bound<'py, PyTime> {
/// Bindings for `datetime.tzinfo`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyTzInfo>`][crate::Py] or [`Bound<'py, PyTzInfo>`][Bound].
///
/// This is an abstract base class and cannot be constructed directly.
/// For concrete time zone implementations, see [`timezone_utc_bound`] and
/// the [`zoneinfo` module](https://docs.python.org/3/library/zoneinfo.html).
@ -834,7 +846,10 @@ pub(crate) fn timezone_from_offset<'py>(
}
}
/// Bindings for `datetime.timedelta`
/// Bindings for `datetime.timedelta`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyDelta>`][crate::Py] or [`Bound<'py, PyDelta>`][Bound].
#[repr(transparent)]
pub struct PyDelta(PyAny);
pyobject_native_type!(

View File

@ -11,6 +11,12 @@ use crate::PyNativeType;
use crate::{ffi, Python, ToPyObject};
/// Represents a Python `dict`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyDict>`][crate::Py] or [`Bound<'py, PyDict>`][Bound].
///
/// For APIs available on `dict` objects, see the [`PyDictMethods`] trait which is implemented for
/// [`Bound<'py, PyDict>`][Bound].
#[repr(transparent)]
pub struct PyDict(PyAny);
@ -191,19 +197,6 @@ impl PyDict {
}
}
/// Deprecated version of `get_item`.
#[deprecated(
since = "0.20.0",
note = "this is now equivalent to `PyDict::get_item`"
)]
#[inline]
pub fn get_item_with_error<K>(&self, key: K) -> PyResult<Option<&PyAny>>
where
K: ToPyObject,
{
self.get_item(key)
}
/// Sets an item value.
///
/// This is equivalent to the Python statement `self[key] = value`.
@ -951,31 +944,6 @@ mod tests {
});
}
#[test]
#[allow(deprecated)]
#[cfg(all(not(any(PyPy, GraalPy)), feature = "gil-refs"))]
fn test_get_item_with_error() {
Python::with_gil(|py| {
let mut v = HashMap::new();
v.insert(7, 32);
let ob = v.to_object(py);
let dict = ob.downcast::<PyDict>(py).unwrap();
assert_eq!(
32,
dict.get_item_with_error(7i32)
.unwrap()
.unwrap()
.extract::<i32>()
.unwrap()
);
assert!(dict.get_item_with_error(8i32).unwrap().is_none());
assert!(dict
.get_item_with_error(dict)
.unwrap_err()
.is_instance_of::<crate::exceptions::PyTypeError>(py));
});
}
#[test]
fn test_set_item() {
Python::with_gil(|py| {

View File

@ -4,6 +4,9 @@ use crate::{
};
/// Represents the Python `Ellipsis` object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyEllipsis>`][crate::Py] or [`Bound<'py, PyEllipsis>`][Bound].
#[repr(transparent)]
pub struct PyEllipsis(PyAny);

View File

@ -11,9 +11,15 @@ use std::os::raw::c_double;
/// Represents a Python `float` object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyFloat>`][crate::Py] or [`Bound<'py, PyFloat>`][Bound].
///
/// For APIs available on `float` objects, see the [`PyFloatMethods`] trait which is implemented for
/// [`Bound<'py, PyFloat>`][Bound].
///
/// You can usually avoid directly working with this type
/// by using [`ToPyObject`] and [`extract`](PyAnyMethods::extract)
/// with `f32`/`f64`.
/// by using [`ToPyObject`] and [`extract`][PyAnyMethods::extract]
/// with [`f32`]/[`f64`].
#[repr(transparent)]
pub struct PyFloat(PyAny);

View File

@ -2,6 +2,9 @@ use crate::ffi;
use crate::PyAny;
/// Represents a Python frame.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyFrame>`][crate::Py] or [`Bound<'py, PyFrame>`][crate::Bound].
#[repr(transparent)]
pub struct PyFrame(PyAny);

View File

@ -56,7 +56,13 @@ impl<'py> PyFrozenSetBuilder<'py> {
}
}
/// Represents a Python `frozenset`
/// Represents a Python `frozenset`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyFrozenSet>`][crate::Py] or [`Bound<'py, PyFrozenSet>`][Bound].
///
/// For APIs available on `frozenset` objects, see the [`PyFrozenSetMethods`] trait which is implemented for
/// [`Bound<'py, PyFrozenSet>`][Bound].
#[repr(transparent)]
pub struct PyFrozenSet(PyAny);

View File

@ -16,6 +16,9 @@ use std::cell::UnsafeCell;
use std::ffi::CStr;
/// Represents a builtin Python function object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyCFunction>`][crate::Py] or [`Bound<'py, PyCFunction>`][Bound].
#[repr(transparent)]
pub struct PyCFunction(PyAny);
@ -241,6 +244,9 @@ struct ClosureDestructor<F> {
unsafe impl<F: Send> Send for ClosureDestructor<F> {}
/// Represents a Python function object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyFunction>`][crate::Py] or [`Bound<'py, PyFunction>`][Bound].
#[repr(transparent)]
#[cfg(all(not(Py_LIMITED_API), not(all(PyPy, not(Py_3_8)))))]
pub struct PyFunction(PyAny);

View File

@ -3,10 +3,13 @@ use crate::instance::Borrowed;
use crate::py_result_ext::PyResultExt;
use crate::{ffi, Bound, PyAny, PyErr, PyResult, PyTypeCheck};
#[cfg(feature = "gil-refs")]
use crate::{AsPyPointer, PyDowncastError, PyNativeType};
use crate::{AsPyPointer, PyNativeType};
/// A Python iterator object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyIterator>`][crate::Py] or [`Bound<'py, PyIterator>`][Bound].
///
/// # Examples
///
/// ```rust
@ -127,31 +130,6 @@ impl PyTypeCheck for PyIterator {
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<'v> crate::PyTryFrom<'v> for PyIterator {
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if ffi::PyIter_Check(value.as_ptr()) != 0 {
Ok(value.downcast_unchecked())
} else {
Err(PyDowncastError::new(value, "Iterator"))
}
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> {
value.into().downcast()
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v PyIterator {
let ptr = value.into() as *const _ as *const PyIterator;
&*ptr
}
}
#[cfg(test)]
mod tests {
use super::PyIterator;
@ -295,18 +273,6 @@ def fibonacci(target):
});
}
#[test]
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
fn iterator_try_from() {
Python::with_gil(|py| {
let obj: crate::Py<crate::PyAny> =
vec![10, 20].to_object(py).as_ref(py).iter().unwrap().into();
let iter = <PyIterator as crate::PyTryFrom>::try_from(obj.as_ref(py)).unwrap();
assert!(obj.is(iter));
});
}
#[test]
#[cfg(feature = "macros")]
fn python_class_not_iterator() {

View File

@ -14,6 +14,12 @@ use crate::types::any::PyAnyMethods;
use crate::types::sequence::PySequenceMethods;
/// Represents a Python `list`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyList>`][crate::Py] or [`Bound<'py, PyList>`][Bound].
///
/// For APIs available on `list` objects, see the [`PyListMethods`] trait which is implemented for
/// [`Bound<'py, PyDict>`][Bound].
#[repr(transparent)]
pub struct PyList(PyAny);

View File

@ -7,10 +7,16 @@ use crate::type_object::PyTypeInfo;
use crate::types::any::PyAnyMethods;
use crate::types::{PyAny, PyDict, PySequence, PyType};
#[cfg(feature = "gil-refs")]
use crate::{err::PyDowncastError, PyNativeType};
use crate::PyNativeType;
use crate::{ffi, Py, PyTypeCheck, Python, ToPyObject};
/// Represents a reference to a Python object supporting the mapping protocol.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyMapping>`][crate::Py] or [`Bound<'py, PyMapping>`][Bound].
///
/// For APIs available on mapping objects, see the [`PyMappingMethods`] trait which is implemented for
/// [`Bound<'py, PyMapping>`][Bound].
#[repr(transparent)]
pub struct PyMapping(PyAny);
pyobject_native_type_named!(PyMapping);
@ -260,34 +266,6 @@ impl PyTypeCheck for PyMapping {
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<'v> crate::PyTryFrom<'v> for PyMapping {
/// Downcasting to `PyMapping` requires the concrete class to be a subclass (or registered
/// subclass) of `collections.abc.Mapping` (from the Python standard library) - i.e.
/// `isinstance(<class>, collections.abc.Mapping) == True`.
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyMapping, PyDowncastError<'v>> {
let value = value.into();
if PyMapping::type_check(&value.as_borrowed()) {
unsafe { return Ok(value.downcast_unchecked()) }
}
Err(PyDowncastError::new(value, "Mapping"))
}
#[inline]
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyMapping, PyDowncastError<'v>> {
value.into().downcast()
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v PyMapping {
let ptr = value.into() as *const _ as *const PyMapping;
&*ptr
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
@ -439,16 +417,4 @@ mod tests {
assert_eq!(32 + 42 + 123, values_sum);
});
}
#[test]
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
fn test_mapping_try_from() {
use crate::PyTryFrom;
Python::with_gil(|py| {
let dict = PyDict::new(py);
let _ = <PyMapping as PyTryFrom>::try_from(dict).unwrap();
let _ = PyMapping::try_from_exact(dict).unwrap();
});
}
}

View File

@ -6,6 +6,9 @@ use crate::{ffi, Bound, PyAny};
use crate::{AsPyPointer, PyNativeType};
/// Represents a Python `memoryview`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyMemoryView>`][crate::Py] or [`Bound<'py, PyMemoryView>`][Bound].
#[repr(transparent)]
pub struct PyMemoryView(PyAny);

View File

@ -15,6 +15,12 @@ use {super::PyStringMethods, crate::PyNativeType};
/// Represents a Python [`module`][1] object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyModule>`][crate::Py] or [`Bound<'py, PyModule>`][Bound].
///
/// For APIs available on `module` objects, see the [`PyModuleMethods`] trait which is implemented for
/// [`Bound<'py, PyModule>`][Bound].
///
/// As with all other Python objects, modules are first class citizens.
/// This means they can be passed to or returned from functions,
/// created dynamically, assigned to variables and so forth.
@ -298,7 +304,7 @@ impl PyModule {
/// make an *instance* of `Foo` (or *get* one for that matter, as we haven't exported
/// anything that can return instances of `Foo`).
///
/// [1]: https://pyo3.rs/latest/class.html#constructor
#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#constructor")]
pub fn add_class<T>(&self) -> PyResult<()>
where
T: PyClass,
@ -503,7 +509,7 @@ pub trait PyModuleMethods<'py>: crate::sealed::Sealed {
/// make an *instance* of `Foo` (or *get* one for that matter, as we haven't exported
/// anything that can return instances of `Foo`).
///
/// [1]: https://pyo3.rs/latest/class.html#constructor
#[doc = concat!("[1]: https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#constructor")]
fn add_class<T>(&self) -> PyResult<()>
where
T: PyClass;

View File

@ -5,6 +5,9 @@ use crate::{
};
/// Represents the Python `None` object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyNone>`][crate::Py] or [`Bound<'py, PyNone>`][Bound].
#[repr(transparent)]
pub struct PyNone(PyAny);

View File

@ -4,6 +4,9 @@ use crate::{
};
/// Represents the Python `NotImplemented` object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyNotImplemented>`][crate::Py] or [`Bound<'py, PyNotImplemented>`][Bound].
#[repr(transparent)]
pub struct PyNotImplemented(PyAny);

View File

@ -2,6 +2,9 @@ use crate::{ffi, PyAny};
/// Represents a Python `int` object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyLong>`][crate::Py] or [`Bound<'py, PyLong>`][crate::Bound].
///
/// You can usually avoid directly working with this type
/// by using [`ToPyObject`](crate::conversion::ToPyObject)
/// and [`extract`](super::PyAnyMethods::extract)

View File

@ -6,7 +6,8 @@ use crate::{PyAny, PyResult};
/// Represents a Python `super` object.
///
/// This type is immutable.
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PySuper>`][crate::Py] or [`Bound<'py, PySuper>`][Bound].
#[repr(transparent)]
pub struct PySuper(PyAny);

View File

@ -10,10 +10,16 @@ use crate::sync::GILOnceCell;
use crate::type_object::PyTypeInfo;
use crate::types::{any::PyAnyMethods, PyAny, PyList, PyString, PyTuple, PyType};
#[cfg(feature = "gil-refs")]
use crate::{err::PyDowncastError, PyNativeType};
use crate::PyNativeType;
use crate::{ffi, FromPyObject, Py, PyTypeCheck, Python, ToPyObject};
/// Represents a reference to a Python object supporting the sequence protocol.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PySequence>`][crate::Py] or [`Bound<'py, PySequence>`][Bound].
///
/// For APIs available on sequence objects, see the [`PySequenceMethods`] trait which is implemented for
/// [`Bound<'py, PySequence>`][Bound].
#[repr(transparent)]
pub struct PySequence(PyAny);
pyobject_native_type_named!(PySequence);
@ -547,33 +553,6 @@ impl PyTypeCheck for PySequence {
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
impl<'v> crate::PyTryFrom<'v> for PySequence {
/// Downcasting to `PySequence` requires the concrete class to be a subclass (or registered
/// subclass) of `collections.abc.Sequence` (from the Python standard library) - i.e.
/// `isinstance(<class>, collections.abc.Sequence) == True`.
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> {
let value = value.into();
if PySequence::type_check(&value.as_borrowed()) {
unsafe { return Ok(value.downcast_unchecked::<PySequence>()) }
}
Err(PyDowncastError::new(value, "Sequence"))
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> {
value.into().downcast()
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v PySequence {
let ptr = value.into() as *const _ as *const PySequence;
&*ptr
}
}
#[cfg(test)]
mod tests {
use crate::types::{PyAnyMethods, PyList, PySequence, PySequenceMethods, PyTuple};
@ -1101,16 +1080,4 @@ mod tests {
assert!(seq_from.to_list().is_ok());
});
}
#[test]
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
fn test_seq_try_from() {
use crate::PyTryFrom;
Python::with_gil(|py| {
let list = PyList::empty(py);
let _ = <PySequence as PyTryFrom>::try_from(list).unwrap();
let _ = PySequence::try_from_exact(list).unwrap();
});
}
}

View File

@ -11,7 +11,13 @@ use crate::{
use crate::{ffi, PyAny, PyObject, Python, ToPyObject};
use std::ptr;
/// Represents a Python `set`
/// Represents a Python `set`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PySet>`][crate::Py] or [`Bound<'py, PySet>`][Bound].
///
/// For APIs available on `set` objects, see the [`PySetMethods`] trait which is implemented for
/// [`Bound<'py, PySet>`][Bound].
#[repr(transparent)]
pub struct PySet(PyAny);

View File

@ -8,6 +8,12 @@ use crate::{Bound, PyAny, PyObject, Python, ToPyObject};
/// Represents a Python `slice`.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PySlice>`][crate::Py] or [`Bound<'py, PySlice>`][Bound].
///
/// For APIs available on `slice` objects, see the [`PySliceMethods`] trait which is implemented for
/// [`Bound<'py, PySlice>`][Bound].
///
/// Only `isize` indices supported at the moment by the `PySlice` object.
#[repr(transparent)]
pub struct PySlice(PyAny);

View File

@ -123,10 +123,11 @@ impl<'a> PyStringData<'a> {
/// Represents a Python `string` (a Unicode string object).
///
/// This type is only seen inside PyO3's smart pointers as [`Py<PyString>`], [`Bound<'py, PyString>`],
/// and [`Borrowed<'a, 'py, PyString>`].
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyString>`][crate::Py] or [`Bound<'py, PyString>`][Bound].
///
/// All functionality on this type is implemented through the [`PyStringMethods`] trait.
/// For APIs available on `str` objects, see the [`PyStringMethods`] trait which is implemented for
/// [`Bound<'py, PyString>`][Bound].
///
/// # Equality
///

View File

@ -5,6 +5,12 @@ use crate::PyNativeType;
use crate::{ffi, Bound, PyAny};
/// Represents a Python traceback.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyTraceback>`][crate::Py] or [`Bound<'py, PyTraceback>`][Bound].
///
/// For APIs available on traceback objects, see the [`PyTracebackMethods`] trait which is implemented for
/// [`Bound<'py, PyTraceback>`][Bound].
#[repr(transparent)]
pub struct PyTraceback(PyAny);

View File

@ -52,7 +52,11 @@ fn new_from_iter<'py>(
/// Represents a Python `tuple` object.
///
/// This type is immutable.
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyTuple>`][crate::Py] or [`Bound<'py, PyTuple>`][Bound].
///
/// For APIs available on `tuple` objects, see the [`PyTupleMethods`] trait which is implemented for
/// [`Bound<'py, PyTuple>`][Bound].
#[repr(transparent)]
pub struct PyTuple(PyAny);

View File

@ -9,7 +9,14 @@ use crate::PyNativeType;
use crate::{ffi, Bound, PyAny, PyTypeInfo, Python};
use super::PyString;
/// Represents a reference to a Python `type object`.
/// Represents a reference to a Python `type` object.
///
/// Values of this type are accessed via PyO3's smart pointers, e.g. as
/// [`Py<PyType>`][crate::Py] or [`Bound<'py, PyType>`][Bound].
///
/// For APIs available on `type` objects, see the [`PyTypeMethods`] trait which is implemented for
/// [`Bound<'py, PyType>`][Bound].
#[repr(transparent)]
pub struct PyType(PyAny);

View File

@ -20,7 +20,6 @@ fn test_compile_errors() {
t.compile_fail("tests/ui/invalid_pymethod_names.rs");
t.compile_fail("tests/ui/invalid_pymodule_args.rs");
t.compile_fail("tests/ui/reject_generics.rs");
#[cfg(feature = "gil-refs")]
t.compile_fail("tests/ui/deprecations.rs");
t.compile_fail("tests/ui/invalid_closure.rs");
t.compile_fail("tests/ui/pyclass_send.rs");
@ -67,4 +66,5 @@ fn test_compile_errors() {
t.compile_fail("tests/ui/abi3_weakref.rs");
#[cfg(all(Py_LIMITED_API, not(Py_3_9)))]
t.compile_fail("tests/ui/abi3_dict.rs");
t.compile_fail("tests/ui/duplicate_pymodule_submodule.rs");
}

View File

@ -49,8 +49,7 @@ create_exception!(
"Some description."
);
#[pymodule]
#[pyo3(submodule)]
#[pymodule(submodule)]
mod external_submodule {}
/// A module written using declarative syntax.
@ -115,7 +114,7 @@ mod declarative_module {
}
}
#[pymodule(submodule)]
#[pymodule]
#[pyo3(module = "custom_root")]
mod inner_custom_root {
use super::*;
@ -144,8 +143,7 @@ mod declarative_submodule {
use super::{double, double_value};
}
#[pymodule]
#[pyo3(name = "declarative_module_renamed")]
#[pymodule(name = "declarative_module_renamed")]
mod declarative_module2 {
#[pymodule_export]
use super::double;

View File

@ -138,8 +138,7 @@ fn test_module_with_explicit_py_arg() {
});
}
#[pymodule]
#[pyo3(name = "other_name")]
#[pymodule(name = "other_name")]
fn some_name(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add("other_name", "other_name")?;
Ok(())

View File

@ -2,125 +2,11 @@
#![allow(dead_code)]
use pyo3::prelude::*;
use pyo3::types::{PyString, PyType};
#[pyclass]
struct MyClass;
#[pymethods]
impl MyClass {
#[__new__]
fn new() -> Self {
Self
}
#[classmethod]
fn cls_method_gil_ref(_cls: &PyType) {}
#[classmethod]
fn cls_method_bound(_cls: &Bound<'_, PyType>) {}
fn method_gil_ref(_slf: &PyCell<Self>) {}
fn method_bound(_slf: &Bound<'_, Self>) {}
#[staticmethod]
fn static_method_gil_ref(_any: &PyAny) {}
#[setter]
fn set_foo_gil_ref(&self, #[pyo3(from_py_with = "extract_gil_ref")] _value: i32) {}
#[setter]
fn set_foo_bound(&self, #[pyo3(from_py_with = "extract_bound")] _value: i32) {}
#[setter]
fn set_bar_gil_ref(&self, _value: &PyAny) {}
#[setter]
fn set_bar_bound(&self, _value: &Bound<'_, PyAny>) {}
fn __eq__(&self, #[pyo3(from_py_with = "extract_gil_ref")] _other: i32) -> bool {
true
}
fn __contains__(&self, #[pyo3(from_py_with = "extract_bound")] _value: i32) -> bool {
true
}
}
fn main() {}
#[pyfunction]
#[pyo3(pass_module)]
fn pyfunction_with_module<'py>(module: &Bound<'py, PyModule>) -> PyResult<Bound<'py, PyString>> {
module.name()
}
#[pyfunction]
#[pyo3(pass_module)]
fn pyfunction_with_module_gil_ref(_module: &PyModule) -> PyResult<&str> {
todo!()
}
#[pyfunction]
fn double(x: usize) -> usize {
x * 2
}
#[pymodule]
fn module_gil_ref(_m: &PyModule) -> PyResult<()> {
Ok(())
}
#[pymodule]
fn module_gil_ref_with_explicit_py_arg(_py: Python<'_>, _m: &PyModule) -> PyResult<()> {
Ok(())
}
#[pymodule]
fn module_bound(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, m)?)?;
Ok(())
}
#[pymodule]
fn module_bound_with_explicit_py_arg(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, m)?)?;
Ok(())
}
#[pymodule]
fn module_bound_by_value(m: Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(double, &m)?)?;
Ok(())
}
fn extract_gil_ref(obj: &PyAny) -> PyResult<i32> {
obj.extract()
}
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<i32> {
obj.extract()
}
fn extract_options(obj: &Bound<'_, PyAny>) -> PyResult<Option<i32>> {
obj.extract()
}
#[pyfunction]
fn pyfunction_from_py_with(
#[pyo3(from_py_with = "extract_gil_ref")] _gil_ref: i32,
#[pyo3(from_py_with = "extract_bound")] _bound: i32,
) {
}
#[pyfunction]
fn pyfunction_gil_ref(_any: &PyAny) {}
#[pyfunction]
#[pyo3(signature = (_any))]
fn pyfunction_option_gil_ref(_any: Option<&PyAny>) {}
#[pyfunction]
#[pyo3(signature = (_i, _any=None))]
fn pyfunction_option_1(_i: u32, _any: Option<i32>) {}
@ -139,63 +25,10 @@ fn pyfunction_option_4(
) {
}
#[derive(Debug, FromPyObject)]
pub struct Zap {
#[pyo3(item)]
name: String,
#[pyo3(from_py_with = "PyAny::len", item("my_object"))]
some_object_length: usize,
#[pyo3(from_py_with = "extract_bound")]
some_number: i32,
}
#[derive(Debug, FromPyObject)]
pub struct ZapTuple(
String,
#[pyo3(from_py_with = "PyAny::len")] usize,
#[pyo3(from_py_with = "extract_bound")] i32,
);
#[derive(Debug, FromPyObject, PartialEq, Eq)]
pub enum ZapEnum {
Zip(#[pyo3(from_py_with = "extract_gil_ref")] i32),
Zap(String, #[pyo3(from_py_with = "extract_bound")] i32),
}
#[derive(Debug, FromPyObject, PartialEq, Eq)]
#[pyo3(transparent)]
pub struct TransparentFromPyWithGilRef {
#[pyo3(from_py_with = "extract_gil_ref")]
len: i32,
}
#[derive(Debug, FromPyObject, PartialEq, Eq)]
#[pyo3(transparent)]
pub struct TransparentFromPyWithBound {
#[pyo3(from_py_with = "extract_bound")]
len: i32,
}
fn test_wrap_pyfunction(py: Python<'_>, m: &Bound<'_, PyModule>) {
// should lint
let _ = wrap_pyfunction!(double, py);
// should lint but currently does not
let _ = wrap_pyfunction!(double)(py);
// should not lint
let _ = wrap_pyfunction!(double, m);
let _ = wrap_pyfunction!(double)(m);
let _ = wrap_pyfunction!(double, m.as_gil_ref());
let _ = wrap_pyfunction!(double)(m.as_gil_ref());
let _ = wrap_pyfunction_bound!(double, py);
let _ = wrap_pyfunction_bound!(double)(py);
}
#[pyclass]
pub enum SimpleEnumWithoutEq {
VariamtA,
VariantB,
}
fn main() {}

View File

@ -1,8 +1,10 @@
error: use of deprecated constant `pyo3::deprecations::PYMETHODS_NEW_DEPRECATED_FORM`: use `#[new]` instead of `#[__new__]`
--> tests/ui/deprecations.rs:12:7
error: use of deprecated constant `__pyfunction_pyfunction_option_2::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
= note: these implicit defaults are being phased out
= help: add `#[pyo3(signature = (_i, _any=None))]` to this function to silence this warning and keep the current behavior
--> tests/ui/deprecations.rs:15:4
|
12 | #[__new__]
| ^^^^^^^
15 | fn pyfunction_option_2(_i: u32, _any: Option<i32>) {}
| ^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> tests/ui/deprecations.rs:1:9
@ -10,144 +12,26 @@ note: the lint level is defined here
1 | #![deny(deprecated)]
| ^^^^^^^^^^
error: use of deprecated constant `__pyfunction_pyfunction_option_2::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
= note: these implicit defaults are being phased out
= help: add `#[pyo3(signature = (_i, _any=None))]` to this function to silence this warning and keep the current behavior
--> tests/ui/deprecations.rs:129:4
|
129 | fn pyfunction_option_2(_i: u32, _any: Option<i32>) {}
| ^^^^^^^^^^^^^^^^^^^
error: use of deprecated constant `__pyfunction_pyfunction_option_3::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
= note: these implicit defaults are being phased out
= help: add `#[pyo3(signature = (_i, _any=None, _foo=None))]` to this function to silence this warning and keep the current behavior
--> tests/ui/deprecations.rs:132:4
|
132 | fn pyfunction_option_3(_i: u32, _any: Option<i32>, _foo: Option<String>) {}
| ^^^^^^^^^^^^^^^^^^^
--> tests/ui/deprecations.rs:18:4
|
18 | fn pyfunction_option_3(_i: u32, _any: Option<i32>, _foo: Option<String>) {}
| ^^^^^^^^^^^^^^^^^^^
error: use of deprecated constant `__pyfunction_pyfunction_option_4::SIGNATURE`: this function has implicit defaults for the trailing `Option<T>` arguments
= note: these implicit defaults are being phased out
= help: add `#[pyo3(signature = (_i, _any=None, _foo=None))]` to this function to silence this warning and keep the current behavior
--> tests/ui/deprecations.rs:135:4
|
135 | fn pyfunction_option_4(
| ^^^^^^^^^^^^^^^^^^^
--> tests/ui/deprecations.rs:21:4
|
21 | fn pyfunction_option_4(
| ^^^^^^^^^^^^^^^^^^^
error: use of deprecated constant `SimpleEnumWithoutEq::__pyo3__generated____richcmp__::DEPRECATION`: Implicit equality for simple enums is deprecated. Use `#[pyclass(eq, eq_int)` to keep the current behavior.
--> tests/ui/deprecations.rs:197:1
|
197 | #[pyclass]
| ^^^^^^^^^^
|
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
error: use of deprecated struct `pyo3::PyCell`: `PyCell` was merged into `Bound`, use that instead; see the migration guide for more info
--> tests/ui/deprecations.rs:23:30
--> tests/ui/deprecations.rs:28:1
|
23 | fn method_gil_ref(_slf: &PyCell<Self>) {}
| ^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
--> tests/ui/deprecations.rs:42:44
28 | #[pyclass]
| ^^^^^^^^^^
|
42 | fn __eq__(&self, #[pyo3(from_py_with = "extract_gil_ref")] _other: i32) -> bool {
| ^^^^^^^^^^^^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
--> tests/ui/deprecations.rs:18:33
|
18 | fn cls_method_gil_ref(_cls: &PyType) {}
| ^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
--> tests/ui/deprecations.rs:23:29
|
23 | fn method_gil_ref(_slf: &PyCell<Self>) {}
| ^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
--> tests/ui/deprecations.rs:28:36
|
28 | fn static_method_gil_ref(_any: &PyAny) {}
| ^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
--> tests/ui/deprecations.rs:31:53
|
31 | fn set_foo_gil_ref(&self, #[pyo3(from_py_with = "extract_gil_ref")] _value: i32) {}
| ^^^^^^^^^^^^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
--> tests/ui/deprecations.rs:37:39
|
37 | fn set_bar_gil_ref(&self, _value: &PyAny) {}
| ^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
--> tests/ui/deprecations.rs:61:44
|
61 | fn pyfunction_with_module_gil_ref(_module: &PyModule) -> PyResult<&str> {
| ^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
--> tests/ui/deprecations.rs:71:19
|
71 | fn module_gil_ref(_m: &PyModule) -> PyResult<()> {
| ^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
--> tests/ui/deprecations.rs:76:57
|
76 | fn module_gil_ref_with_explicit_py_arg(_py: Python<'_>, _m: &PyModule) -> PyResult<()> {
| ^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
--> tests/ui/deprecations.rs:112:27
|
112 | #[pyo3(from_py_with = "extract_gil_ref")] _gil_ref: i32,
| ^^^^^^^^^^^^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::function_arg`: use `&Bound<'_, T>` instead for this function argument
--> tests/ui/deprecations.rs:118:29
|
118 | fn pyfunction_gil_ref(_any: &PyAny) {}
| ^
error: use of deprecated method `pyo3::deprecations::OptionGilRefs::<std::option::Option<T>>::function_arg`: use `Option<&Bound<'_, T>>` instead for this function argument
--> tests/ui/deprecations.rs:122:36
|
122 | fn pyfunction_option_gil_ref(_any: Option<&PyAny>) {}
| ^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
--> tests/ui/deprecations.rs:147:27
|
147 | #[pyo3(from_py_with = "PyAny::len", item("my_object"))]
| ^^^^^^^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
--> tests/ui/deprecations.rs:157:27
|
157 | #[pyo3(from_py_with = "PyAny::len")] usize,
| ^^^^^^^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
--> tests/ui/deprecations.rs:163:31
|
163 | Zip(#[pyo3(from_py_with = "extract_gil_ref")] i32),
| ^^^^^^^^^^^^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<T>::from_py_with_arg`: use `&Bound<'_, PyAny>` as the argument for this `from_py_with` extractor
--> tests/ui/deprecations.rs:170:27
|
170 | #[pyo3(from_py_with = "extract_gil_ref")]
| ^^^^^^^^^^^^^^^^^
error: use of deprecated method `pyo3::deprecations::GilRefs::<pyo3::Python<'_>>::is_python`: use `wrap_pyfunction_bound!` instead
--> tests/ui/deprecations.rs:183:13
|
183 | let _ = wrap_pyfunction!(double, py);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `wrap_pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@ -0,0 +1,7 @@
#[pyo3::pymodule]
mod mymodule {
#[pyo3::pymodule(submodule)]
mod submod {}
}
fn main() {}

View File

@ -0,0 +1,17 @@
error: `submodule` may only be specified once (it is implicitly always specified for nested modules)
--> tests/ui/duplicate_pymodule_submodule.rs:4:2
|
4 | mod submod {}
| ^^^
error[E0425]: cannot find value `_PYO3_DEF` in module `submod`
--> tests/ui/duplicate_pymodule_submodule.rs:1:1
|
1 | #[pyo3::pymodule]
| ^^^^^^^^^^^^^^^^^ not found in `submod`
|
= note: this error originates in the attribute macro `pyo3::pymodule` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider importing this static
|
3 + use crate::mymodule::_PYO3_DEF;
|

1
tests/ui/empty.rs Normal file
View File

@ -0,0 +1 @@
// see invalid_pymodule_in_root.rs

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