diff --git a/.github/workflows/benches.yml b/.github/workflows/benches.yml index 19dd9d145c4..6b049ea6431 100644 --- a/.github/workflows/benches.yml +++ b/.github/workflows/benches.yml @@ -17,7 +17,7 @@ jobs: benchmarks: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f8e6229cc00..2b3ff0e8cc9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: runs-on: ${{ inputs.os }} if: ${{ !(startsWith(inputs.python-version, 'graalpy') && startsWith(inputs.os, 'windows')) }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 with: # For PRs, we need to run on the real PR head, not the resultant merge of the PR into the target branch. # diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 760918ce40a..1aeb00083d5 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -9,7 +9,7 @@ jobs: name: Check changelog entry runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: '3.13' diff --git a/.github/workflows/ci-cache-warmup.yml b/.github/workflows/ci-cache-warmup.yml index 2b05a1e98c0..d6c3df897f0 100644 --- a/.github/workflows/ci-cache-warmup.yml +++ b/.github/workflows/ci-cache-warmup.yml @@ -9,7 +9,7 @@ jobs: cross-compilation-windows: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5487052412d..9de0b92283c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -38,7 +38,7 @@ jobs: MSRV: ${{ steps.resolve-msrv.outputs.MSRV }} verbose: ${{ runner.debug == '1' }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -51,7 +51,7 @@ jobs: needs: [fmt] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -69,7 +69,7 @@ jobs: needs: [fmt, resolve] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ needs.resolve.outputs.MSRV }} @@ -117,7 +117,7 @@ jobs: name: clippy/${{ matrix.target }}/${{ matrix.rust }} continue-on-error: ${{ matrix.rust != 'stable' }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} @@ -390,7 +390,7 @@ jobs: needs: [fmt] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -411,7 +411,7 @@ jobs: needs: [fmt] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -433,7 +433,7 @@ jobs: needs: [fmt] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -451,7 +451,7 @@ jobs: needs: [fmt] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: # TODO bump emscripten builds to test on 3.13 @@ -462,7 +462,7 @@ jobs: with: targets: wasm32-unknown-emscripten components: rust-src - - uses: actions/setup-node@v5 + - uses: actions/setup-node@v6 with: node-version: 18 - run: python -m pip install --upgrade pip && pip install nox[uv] @@ -492,7 +492,7 @@ jobs: needs: [fmt] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: Swatinem/rust-cache@v2 with: save-if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }} @@ -536,7 +536,7 @@ jobs: if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -560,7 +560,7 @@ jobs: include: - rust: ${{ needs.resolve.outputs.MSRV }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -605,7 +605,7 @@ jobs: target: "aarch64-pc-windows-msvc" flags: "-i python3.13 --features generate-import-lib" steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -635,7 +635,7 @@ jobs: if: ${{ contains(github.event.pull_request.labels.*.name, 'CI-build-full') || github.event_name != 'pull_request' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -685,7 +685,7 @@ jobs: ] runs-on: ${{ matrix.platform.os }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.platform.rust-target }} @@ -707,7 +707,7 @@ jobs: if: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-build-full') && github.event_name == 'pull_request' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: dtolnay/rust-toolchain@stable with: components: rust-src diff --git a/.github/workflows/coverage-pr-base.yml b/.github/workflows/coverage-pr-base.yml index 7b21b399266..427553a5fce 100644 --- a/.github/workflows/coverage-pr-base.yml +++ b/.github/workflows/coverage-pr-base.yml @@ -12,7 +12,7 @@ jobs: coverage-pr-base: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: '3.13' diff --git a/.github/workflows/netlify-build.yml b/.github/workflows/netlify-build.yml index 6150af3db24..8817724220a 100644 --- a/.github/workflows/netlify-build.yml +++ b/.github/workflows/netlify-build.yml @@ -21,7 +21,7 @@ jobs: outputs: tag_name: ${{ steps.prepare_tag.outputs.tag_name }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v5.0.1 - uses: actions/setup-python@v6 with: python-version: "3.13" @@ -31,7 +31,7 @@ jobs: - name: Setup mdBook uses: taiki-e/install-action@v2 with: - tool: mdbook, mdbook-linkcheck, lychee + tool: mdbook@0.4, mdbook-tabs@0.2, lychee - name: Prepare tag id: prepare_tag @@ -39,6 +39,13 @@ jobs: TAG_NAME="${GITHUB_REF##*/}" echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT + - name: Restore lychee cache + id: restore-cache + uses: actions/cache/restore@v4 + with: + path: .lycheecache + key: lychee + # This builds the book in target/guide/. - name: Build the guide run: | @@ -49,6 +56,13 @@ jobs: # allows lychee to get better rate limits from github GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Save lychee cache + uses: actions/cache/save@v4 + if: ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'CI-save-pr-cache') }} + with: + path: .lycheecache + key: ${{ steps.restore-cache.outputs.cache-primary-key }} + # We store the versioned guides on GitHub's gh-pages branch for convenience # (the full gh-pages branch is pulled in the build-netlify-site step) - name: Deploy the guide @@ -72,7 +86,7 @@ jobs: # Upload the built site as an artifact for deploy workflow to consume - name: Upload Build Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: site path: ./netlify_build diff --git a/.github/workflows/netlify-deploy.yml b/.github/workflows/netlify-deploy.yml index 0985e3a6584..862d94478c5 100644 --- a/.github/workflows/netlify-deploy.yml +++ b/.github/workflows/netlify-deploy.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Download Build Artifact - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: site github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d7642ab10b5..0cb3569d9ef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest environment: release steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: # The tag to build or the tag received by the tag event ref: ${{ github.event.inputs.version || github.ref }} diff --git a/.gitignore b/.gitignore index da64603db5f..0cd8815e11d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ coverage.json netlify_build/ .nox/ .vscode/ +.lycheecache diff --git a/.netlify/build.sh b/.netlify/build.sh deleted file mode 100755 index dedacb28fb3..00000000000 --- a/.netlify/build.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env bash - -set -uex - -rustup update nightly -rustup default nightly - -PYO3_VERSION=$(cargo search pyo3 --limit 1 | head -1 | tr -s ' ' | cut -d ' ' -f 3 | tr -d '"') - -## Start from the existing gh-pages content. -## By serving it over netlify, we can have better UX for users because -## netlify can then redirect e.g. /v0.17.0 to /v0.17.0/ -## which leads to better loading of CSS assets. - -wget -qc https://github.com/PyO3/pyo3/archive/gh-pages.tar.gz -O - | tar -xz -mv pyo3-gh-pages netlify_build - -## Configure netlify _redirects file - -# Add redirect for each documented version -set +x # these loops get very spammy and fill the deploy log - -for d in netlify_build/v*; do - version="${d/netlify_build\/v/}" - echo "/v$version/doc/* https://docs.rs/pyo3/$version/:splat" >> netlify_build/_redirects - if [ $version != $PYO3_VERSION ]; then - # for old versions, mark the files in the latest version as the canonical URL - for file in $(find $d -type f); do - file_path="${file/$d\//}" - # remove index.html and/or .html suffix to match the page URL on the - # final netlfiy site - url_path="$file_path" - if [[ $file_path == index.html ]]; then - url_path="" - elif [[ $file_path == *.html ]]; then - url_path="${file_path%.html}" - fi - echo "/v$version/$url_path" >> netlify_build/_headers - if test -f "netlify_build/v$PYO3_VERSION/$file_path"; then - echo " Link: ; rel=\"canonical\"" >> netlify_build/_headers - else - # this file doesn't exist in the latest guide, don't index it - echo " X-Robots-Tag: noindex" >> netlify_build/_headers - fi - done - fi -done - -# Add latest redirect -echo "/latest/* /v${PYO3_VERSION}/:splat 302" >> netlify_build/_redirects - -# some backwards compatbiility redirects -echo "/latest/building_and_distribution/* /latest/building-and-distribution/:splat 302" >> netlify_build/_redirects -echo "/latest/building-and-distribution/multiple_python_versions/* /latest/building-and-distribution/multiple-python-versions:splat 302" >> netlify_build/_redirects -echo "/latest/function/error_handling/* /latest/function/error-handling/:splat 302" >> netlify_build/_redirects -echo "/latest/getting_started/* /latest/getting-started/:splat 302" >> netlify_build/_redirects -echo "/latest/python_from_rust/* /latest/python-from-rust/:splat 302" >> netlify_build/_redirects -echo "/latest/python_typing_hints/* /latest/python-typing-hints/:splat 302" >> netlify_build/_redirects -echo "/latest/trait_bounds/* /latest/trait-bounds/:splat 302" >> netlify_build/_redirects - -## Add landing page redirect -if [ "${CONTEXT}" == "deploy-preview" ]; then - echo "/ /main/" >> netlify_build/_redirects -else - echo "/ /v${PYO3_VERSION}/ 302" >> netlify_build/_redirects -fi - -set -x -## Generate towncrier release notes - -pip install towncrier -towncrier build --yes --version Unreleased --date TBC - -## Build guide - -# Install latest mdbook. Netlify will cache the cargo bin dir, so this will -# only build mdbook if needed. -MDBOOK_VERSION=$(cargo search mdbook --limit 1 | head -1 | tr -s ' ' | cut -d ' ' -f 3 | tr -d '"') -INSTALLED_MDBOOK_VERSION=$(mdbook --version || echo "none") -if [ "${INSTALLED_MDBOOK_VERSION}" != "mdbook v${MDBOOK_VERSION}" ]; then - cargo install mdbook@${MDBOOK_VERSION} --force -fi - -# Install latest mdbook-linkcheck. Netlify will cache the cargo bin dir, so this will -# only build mdbook-linkcheck if needed. -MDBOOK_LINKCHECK_VERSION=$(cargo search mdbook-linkcheck --limit 1 | head -1 | tr -s ' ' | cut -d ' ' -f 3 | tr -d '"') -INSTALLED_MDBOOK_LINKCHECK_VERSION=$(mdbook-linkcheck --version || echo "none") -if [ "${INSTALLED_MDBOOK_LINKCHECK_VERSION}" != "mdbook v${MDBOOK_LINKCHECK_VERSION}" ]; then - cargo install mdbook-linkcheck@${MDBOOK_LINKCHECK_VERSION} --force -fi - -# Install latest mdbook-tabs. Netlify will cache the cargo bin dir, so this will -# only build mdbook-tabs if needed. -MDBOOK_TABS_VERSION=$(cargo search mdbook-tabs --limit 1 | head -1 | tr -s ' ' | cut -d ' ' -f 3 | tr -d '"') -INSTALLED_MDBOOK_TABS_VERSION=$(mdbook-tabs --version || echo "none") -if [ "${INSTALLED_MDBOOK_TABS_VERSION}" != "mdbook-tabs v${MDBOOK_TABS_VERSION}" ]; then - cargo install mdbook-tabs@${MDBOOK_TABS_VERSION} --force -fi - -pip install nox[uv] -nox -s build-guide -mv target/guide/ netlify_build/main/ - -## Build public docs - -nox -s docs -mv target/doc netlify_build/main/doc/ - -echo "" > netlify_build/main/doc/index.html - -## Build internal docs - -nox -s docs -- nightly internal - -mkdir -p netlify_build/internal -mv target/doc netlify_build/internal/ - -ls -l netlify_build/ diff --git a/CHANGELOG.md b/CHANGELOG.md index f588deb7dfa..29dcd538763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,17 @@ To see unreleased changes, please see the [CHANGELOG on the main branch guide](h +## [0.27.2] - 2025-11-30 + +### Changed + +- Disable subclassing `PyDict` on GraalPy (unsupported for now, may crash at runtime). [#5653](https://github.com/PyO3/pyo3/pull/5653) + +### Fixed + +- Fix crash when compiling on Rust 1.92+ with both debug assertions and optimizations enabled. [#5638](https://github.com/PyO3/pyo3/pull/5638) +- Fix FFI definition of `PyDictObject` on PyPy. [#5653](https://github.com/PyO3/pyo3/pull/5653) + ## [0.27.1] - 2025-10-21 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index f37bfa9c6ff..317f80d32da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3" -version = "0.27.1" +version = "0.27.2" description = "Bindings to Python interpreter" authors = ["PyO3 Project and Contributors "] readme = "README.md" @@ -30,10 +30,10 @@ memoffset = "0.9" once_cell = "1.21" # ffi bindings to the python interpreter, split into a separate crate so they can be used independently -pyo3-ffi = { path = "pyo3-ffi", version = "=0.27.1" } +pyo3-ffi = { path = "pyo3-ffi", version = "=0.27.2" } # support crates for macros feature -pyo3-macros = { path = "pyo3-macros", version = "=0.27.1", optional = true } +pyo3-macros = { path = "pyo3-macros", version = "=0.27.2", optional = true } indoc = { version = "2.0.1", optional = true } unindent = { version = "0.2.1", optional = true } @@ -86,7 +86,7 @@ uuid = { version = "1.10.0", features = ["v4"] } parking_lot = { version = "0.12.3", features = ["arc_lock"] } [build-dependencies] -pyo3-build-config = { path = "pyo3-build-config", version = "=0.27.1", features = ["resolve-config"] } +pyo3-build-config = { path = "pyo3-build-config", version = "=0.27.2", features = ["resolve-config"] } [features] default = ["macros"] diff --git a/Contributing.md b/Contributing.md index 2257829a06e..97dda500998 100644 --- a/Contributing.md +++ b/Contributing.md @@ -18,11 +18,43 @@ The following sections also contain specific ideas on where to start contributin ## Setting up a development environment To work and develop PyO3, you need Python & Rust installed on your system. + * We encourage the use of [rustup](https://rustup.rs/) to be able to select and choose specific toolchains based on the project. * [Pyenv](https://github.com/pyenv/pyenv) is also highly recommended for being able to choose a specific Python version. * [virtualenv](https://virtualenv.pypa.io/en/latest/) can also be used with or without Pyenv to use specific installed Python versions. * [`nox`][nox] is used to automate many of our CI tasks. +### Testing, linting, etc. with nox + +[`Nox`][nox] is used to automate many of our CI tasks and can be used locally to handle verfication tasks as you code. We recommend running these actions via nox to make use of our prefered configuration options. You can install nox into your global python with pip: `pip install nox` or (recommended) with [`pipx`][pipx] `pip install pipx`, `pipx install nox` + +The main nox commands we have implemented are: + +* `nox -s test` will run the full suite of recommended rust and python tests (>10 minutes) +* `nox -s test-rust -- skip-full` will run a short suite of rust tests (2-3 minutes) +* `nox -s ruff` will check python linting and apply standard formatting rules +* `nox -s rustfmt` will check basic rust linting and apply standard formatting rules +* `nox -s rumdl` will check the markdown in the guide +* `nox -s clippy` will run clippy to make recommendations on rust style +* `nox -s bench` will benchmark your rust code +* `nox -s codspeed` will run our suite of rust and python performance tests +* `nox -s coverage` will analyse test coverage and output `coverage.json` (alternatively: `nox -s coverage lcov` outputs `lcov.info`) +* `nox -s check-guide` will use [`lychee`][lychee] to check all the links in the guide and doc comments. + +Use `nox -l` to list the full set of subcommands you can run. + +#### UI Tests + +PyO3 uses [`trybuild`][trybuild] to develop UI tests to capture error messages from the Rust compiler for some of the macro functionality. + +Because there are several feature combinations for these UI tests, when updating them all (e.g. for a new Rust compiler version) it may be helpful to use the `update-ui-tests` nox session: + +```bash +nox -s update-ui-tests +``` + +## Ways to help + ### Help users identify bugs The [PyO3 Discord server](https://discord.gg/33kcChzH7f) is very active with users who are new to PyO3, and often completely new to Rust. Helping them debug is a great way to get experience with the PyO3 codebase. @@ -88,44 +120,16 @@ Here are a few things to note when you are writing PRs. ### Testing and Continuous Integration -The PyO3 repo uses GitHub Actions. PRs are blocked from merging if CI is not successful. Formatting, linting and tests are checked for all Rust and Python code. In addition, all warnings in Rust code are disallowed (using `RUSTFLAGS="-D warnings"`). +The PyO3 repo uses GitHub Actions. +PRs are blocked from merging if CI is not successful. +Formatting, linting and tests are checked for all Rust and Python code (the pipeline will abort early if formatting fails to save resources). +In addition, all warnings in Rust code are disallowed (using `RUSTFLAGS="-D warnings"`). Tests run with all supported Python versions with the latest stable Rust compiler, as well as for Python 3.9 with the minimum supported Rust version. If you are adding a new feature, you should add it to the `full` feature in our *Cargo.toml** so that it is tested in CI. -You can run these checks yourself with `nox`. Use `nox -l` to list the full set of subcommands you can run. - -#### Linting Python code -`nox -s ruff` - -#### Linting Rust code -`nox -s rustfmt` - -#### Linting Markdown documentation -`nox -s rumdl` - -#### Semver checks -`cargo semver-checks check-release` - -#### Clippy -`nox -s clippy-all` - -#### Tests -`nox -s test` or `cargo test` for Rust tests only, `nox -f pytests/noxfile.py -s test` for Python tests only - -#### Check all conditional compilation -`nox -s check-feature-powerset` - -#### UI Tests - -PyO3 uses [`trybuild`](https://github.com/dtolnay/trybuild) to develop UI tests to capture error messages from the Rust compiler for some of the macro functionality. - -Because there are several feature combinations for these UI tests, when updating them all (e.g. for a new Rust compiler version) it may be helpful to use the `update-ui-tests` nox session: - -```bash -nox -s update-ui-tests -``` +You can run the CI pipeline components yourself with `nox`, see [the testing section above](#testing-linting-etc-with-nox). ### Documenting changes @@ -255,3 +259,5 @@ In the meanwhile, some of our maintainers have personal GitHub sponsorship pages [mdbook-tabs]: https://mdbook-plugins.rustforweb.org/tabs.html [lychee]: https://github.com/lycheeverse/lychee [nox]: https://github.com/theacodes/nox +[pipx]: https://pipx.pypa.io/stable/ +[trybuild]: https://github.com/dtolnay/trybuild diff --git a/README.md b/README.md index 1502d04bcc5..4a7c3497513 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ name = "string_sum" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.27.1", features = ["extension-module"] } +pyo3 = { version = "0.27.2", features = ["extension-module"] } ``` **`src/lib.rs`** @@ -137,7 +137,7 @@ Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like th ```toml [dependencies.pyo3] -version = "0.27.1" +version = "0.27.2" features = ["auto-initialize"] ``` @@ -220,6 +220,7 @@ about this topic. - [pycrdt](https://github.com/jupyter-server/pycrdt) _Python bindings for the Rust CRDT implementation [Yrs](https://github.com/y-crdt/y-crdt)._ - [pydantic-core](https://github.com/pydantic/pydantic-core) _Core validation logic for pydantic written in Rust._ - [primp](https://github.com/deedy5/primp) _The fastest python HTTP client that can impersonate web browsers by mimicking their headers and TLS/JA3/JA4/HTTP2 fingerprints._ +- [radiate](https://github.com/pkalivas/radiate): _A high-performance evolution engine for genetic programming and evolutionary algorithms._ - [rateslib](https://github.com/attack68/rateslib) _A fixed income library for Python using Rust extensions._ - [river](https://github.com/online-ml/river) _Online machine learning in python, the computationally heavy statistics algorithms are implemented in Rust._ - [robyn](https://github.com/sparckles/Robyn) A Super Fast Async Python Web Framework with a Rust runtime. diff --git a/examples/decorator/.template/pre-script.rhai b/examples/decorator/.template/pre-script.rhai index 50613e36d3a..f2008dd49fd 100644 --- a/examples/decorator/.template/pre-script.rhai +++ b/examples/decorator/.template/pre-script.rhai @@ -1,4 +1,4 @@ -variable::set("PYO3_VERSION", "0.27.1"); +variable::set("PYO3_VERSION", "0.27.2"); file::rename(".template/Cargo.toml", "Cargo.toml"); file::rename(".template/pyproject.toml", "pyproject.toml"); file::delete(".template"); diff --git a/examples/maturin-starter/.template/pre-script.rhai b/examples/maturin-starter/.template/pre-script.rhai index 50613e36d3a..f2008dd49fd 100644 --- a/examples/maturin-starter/.template/pre-script.rhai +++ b/examples/maturin-starter/.template/pre-script.rhai @@ -1,4 +1,4 @@ -variable::set("PYO3_VERSION", "0.27.1"); +variable::set("PYO3_VERSION", "0.27.2"); file::rename(".template/Cargo.toml", "Cargo.toml"); file::rename(".template/pyproject.toml", "pyproject.toml"); file::delete(".template"); diff --git a/examples/plugin/.template/pre-script.rhai b/examples/plugin/.template/pre-script.rhai index 0d716a4a44a..1f66441c330 100644 --- a/examples/plugin/.template/pre-script.rhai +++ b/examples/plugin/.template/pre-script.rhai @@ -1,4 +1,4 @@ -variable::set("PYO3_VERSION", "0.27.1"); +variable::set("PYO3_VERSION", "0.27.2"); file::rename(".template/Cargo.toml", "Cargo.toml"); file::rename(".template/plugin_api/Cargo.toml", "plugin_api/Cargo.toml"); file::delete(".template"); diff --git a/examples/setuptools-rust-starter/.template/pre-script.rhai b/examples/setuptools-rust-starter/.template/pre-script.rhai index aa511b5091b..0bfa261b1a4 100644 --- a/examples/setuptools-rust-starter/.template/pre-script.rhai +++ b/examples/setuptools-rust-starter/.template/pre-script.rhai @@ -1,4 +1,4 @@ -variable::set("PYO3_VERSION", "0.27.1"); +variable::set("PYO3_VERSION", "0.27.2"); file::rename(".template/Cargo.toml", "Cargo.toml"); file::rename(".template/setup.cfg", "setup.cfg"); file::delete(".template"); diff --git a/examples/word-count/.template/pre-script.rhai b/examples/word-count/.template/pre-script.rhai index 50613e36d3a..f2008dd49fd 100644 --- a/examples/word-count/.template/pre-script.rhai +++ b/examples/word-count/.template/pre-script.rhai @@ -1,4 +1,4 @@ -variable::set("PYO3_VERSION", "0.27.1"); +variable::set("PYO3_VERSION", "0.27.2"); file::rename(".template/Cargo.toml", "Cargo.toml"); file::rename(".template/pyproject.toml", "pyproject.toml"); file::delete(".template"); diff --git a/guide/src/class.md b/guide/src/class.md index a2f9b0de3fc..0527e9b297b 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -1312,7 +1312,7 @@ Python::attach(|py| { # .unwrap(); ``` -WARNING: `Py::new` and `.into_pyobject` are currently inconsistent. Note how the constructed value is _not_ an instance of the specific variant. For this reason, constructing values is only recommended using `.into_pyobject`. +WARNING: `Py::new` and `.into_pyobject` are currently inconsistent. Note how the constructed value is *not* an instance of the specific variant. For this reason, constructing values is only recommended using `.into_pyobject`. ```rust # use pyo3::prelude::*; diff --git a/guide/src/class/protocols.md b/guide/src/class/protocols.md index 36cd1d37e39..70c6d4bad1f 100644 --- a/guide/src/class/protocols.md +++ b/guide/src/class/protocols.md @@ -78,7 +78,7 @@ given signatures should be interpreted as follows: The implementations of Python's "rich comparison" operators `<`, `<=`, `==`, `!=`, `>` and `>=` respectively. - _Note that implementing any of these methods will cause Python not to generate a default `__hash__` implementation, so consider also implementing `__hash__`._ + *Note that implementing any of these methods will cause Python not to generate a default XXXXXXXXXX implementation, so consider also implementing XXXXXXXXXX.*
Return type The return type will normally be `bool` or `PyResult`, however any Python object can be returned. @@ -90,9 +90,9 @@ given signatures should be interpreted as follows: The `CompareOp` argument indicates the comparison operation being performed. You can use [`CompareOp::matches`] to adapt a Rust `std::cmp::Ordering` result to the requested comparison. - _This method cannot be implemented in combination with any of `__lt__`, `__le__`, `__eq__`, `__ne__`, `__gt__`, or `__ge__`._ + *This method cannot be implemented in combination with any of XXXXXXXX, XXXXXXXX, XXXXXXXX, XXXXXXXX, XXXXXXXX, or XXXXXXXX.* - _Note that implementing `__richcmp__` will cause Python not to generate a default `__hash__` implementation, so consider implementing `__hash__` when implementing `__richcmp__`._ + *Note that implementing XXXXXXXXXXXXX will cause Python not to generate a default XXXXXXXXXX implementation, so consider implementing XXXXXXXXXX when implementing XXXXXXXXXXXXX.*
Return type The return type will normally be `PyResult`, but any Python object can be returned. diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index b0e8bd83eb7..00000000000 --- a/netlify.toml +++ /dev/null @@ -1,6 +0,0 @@ -[build] -publish = "netlify_build/" -command = ".netlify/build.sh" - -[build.environment] -PYTHON_VERSION = "3.8" diff --git a/noxfile.py b/noxfile.py index df74f8c06b4..c3b0089725c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -214,7 +214,7 @@ def rumdl(session: nox.Session): @nox.session(name="clippy", venv_backend="none") def clippy(session: nox.Session) -> bool: - if not _clippy(session) and _clippy_additional_workspaces(session): + if not (_clippy(session) and _clippy_additional_workspaces(session)): session.error("one or more jobs failed") @@ -737,6 +737,8 @@ def check_guide(session: nox.Session): str(PYO3_GUIDE_SRC), *remap_args, "--accept=200,429", + "--cache", + "--max-cache-age=7d", *session.posargs, external=True, ) @@ -754,6 +756,8 @@ def check_guide(session: nox.Session): "--accept=200,429", # reduce the concurrency to avoid rate-limit from `pyo3.rs` "--max-concurrency=32", + "--cache", + "--max-cache-age=7d", *session.posargs, external=True, ) diff --git a/pyo3-build-config/Cargo.toml b/pyo3-build-config/Cargo.toml index 4f8973f8002..81275d0c048 100644 --- a/pyo3-build-config/Cargo.toml +++ b/pyo3-build-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3-build-config" -version = "0.27.1" +version = "0.27.2" description = "Build configuration for the PyO3 ecosystem" authors = ["PyO3 Project and Contributors "] keywords = ["pyo3", "python", "cpython", "ffi"] diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index ea92cd89d40..7dc96d57622 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -1998,7 +1998,7 @@ fn escape(bytes: &[u8]) -> String { } fn unescape(escaped: &str) -> Vec { - assert!(escaped.len() % 2 == 0, "invalid hex encoding"); + assert_eq!(escaped.len() % 2, 0, "invalid hex encoding"); let mut bytes = Vec::with_capacity(escaped.len() / 2); diff --git a/pyo3-ffi/Cargo.toml b/pyo3-ffi/Cargo.toml index b5e17fdfd71..0bba4337129 100644 --- a/pyo3-ffi/Cargo.toml +++ b/pyo3-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3-ffi" -version = "0.27.1" +version = "0.27.2" description = "Python-API bindings for the PyO3 ecosystem" authors = ["PyO3 Project and Contributors "] keywords = ["pyo3", "python", "cpython", "ffi"] @@ -44,7 +44,7 @@ generate-import-lib = ["pyo3-build-config/generate-import-lib"] paste = "1" [build-dependencies] -pyo3-build-config = { path = "../pyo3-build-config", version = "=0.27.1", features = ["resolve-config"] } +pyo3-build-config = { path = "../pyo3-build-config", version = "=0.27.2", features = ["resolve-config"] } [lints] workspace = true diff --git a/pyo3-ffi/README.md b/pyo3-ffi/README.md index 1c0d071c21a..17cace5d1d4 100644 --- a/pyo3-ffi/README.md +++ b/pyo3-ffi/README.md @@ -41,13 +41,13 @@ name = "string_sum" crate-type = ["cdylib"] [dependencies.pyo3-ffi] -version = "0.27.1" +version = "0.27.2" features = ["extension-module"] [build-dependencies] # This is only necessary if you need to configure your build based on # the Python version or the compile-time configuration for the interpreter. -pyo3_build_config = "0.27.1" +pyo3_build_config = "0.27.2" ``` If you need to use conditional compilation based on Python version or how diff --git a/pyo3-ffi/src/cpython/dictobject.rs b/pyo3-ffi/src/cpython/dictobject.rs index 0ee90b53fc6..1510371f368 100644 --- a/pyo3-ffi/src/cpython/dictobject.rs +++ b/pyo3-ffi/src/cpython/dictobject.rs @@ -1,13 +1,17 @@ use crate::object::*; +#[cfg(not(PyPy))] use crate::pyport::Py_ssize_t; +#[cfg(not(PyPy))] use std::ffi::c_int; +#[cfg(not(PyPy))] opaque_struct!(pub PyDictKeysObject); #[cfg(Py_3_11)] +#[cfg(not(PyPy))] opaque_struct!(pub PyDictValues); -#[cfg(not(GraalPy))] +#[cfg(not(any(GraalPy, PyPy)))] #[repr(C)] #[derive(Debug)] pub struct PyDictObject { @@ -28,11 +32,20 @@ pub struct PyDictObject { pub ma_values: *mut PyDictValues, } +#[cfg(PyPy)] +#[repr(C)] +#[derive(Debug)] +pub struct PyDictObject { + pub ob_base: PyObject, + _tmpkeys: *mut PyObject, +} + extern "C" { // skipped _PyDict_GetItem_KnownHash // skipped _PyDict_GetItemIdWithError // skipped _PyDict_GetItemStringWithError // skipped PyDict_SetDefault + #[cfg(not(PyPy))] pub fn _PyDict_SetItem_KnownHash( mp: *mut PyObject, key: *mut PyObject, @@ -42,6 +55,7 @@ extern "C" { // skipped _PyDict_DelItem_KnownHash // skipped _PyDict_DelItemIf // skipped _PyDict_NewKeysForClass + #[cfg(not(PyPy))] pub fn _PyDict_Next( mp: *mut PyObject, pos: *mut Py_ssize_t, @@ -51,6 +65,7 @@ extern "C" { ) -> c_int; // skipped PyDict_GET_SIZE // skipped _PyDict_ContainsId + #[cfg(not(PyPy))] pub fn _PyDict_NewPresized(minused: Py_ssize_t) -> *mut PyObject; // skipped _PyDict_MaybeUntrack // skipped _PyDict_HasOnlyStringKeys @@ -72,6 +87,7 @@ extern "C" { // skipped _PyDictView_Intersect #[cfg(Py_3_10)] + #[cfg(not(PyPy))] pub fn _PyDict_Contains_KnownHash( op: *mut PyObject, key: *mut PyObject, @@ -79,5 +95,6 @@ extern "C" { ) -> c_int; #[cfg(not(Py_3_10))] + #[cfg(not(PyPy))] pub fn _PyDict_Contains(mp: *mut PyObject, key: *mut PyObject, hash: Py_ssize_t) -> c_int; } diff --git a/pyo3-ffi/src/cpython/mod.rs b/pyo3-ffi/src/cpython/mod.rs index d2b2274628b..cde84b06bd3 100644 --- a/pyo3-ffi/src/cpython/mod.rs +++ b/pyo3-ffi/src/cpython/mod.rs @@ -9,7 +9,6 @@ pub(crate) mod complexobject; #[cfg(Py_3_13)] pub(crate) mod critical_section; pub(crate) mod descrobject; -#[cfg(not(PyPy))] pub(crate) mod dictobject; // skipped fileobject.h // skipped fileutils.h @@ -53,7 +52,6 @@ pub use self::complexobject::*; #[cfg(Py_3_13)] pub use self::critical_section::*; pub use self::descrobject::*; -#[cfg(not(PyPy))] pub use self::dictobject::*; pub use self::floatobject::*; pub use self::frameobject::*; diff --git a/pyo3-ffi/src/dictobject.rs b/pyo3-ffi/src/dictobject.rs index d8419ba4317..d1209da6e5d 100644 --- a/pyo3-ffi/src/dictobject.rs +++ b/pyo3-ffi/src/dictobject.rs @@ -116,6 +116,6 @@ extern "C" { pub static mut PyDictRevIterItem_Type: PyTypeObject; } -#[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] +#[cfg(any(GraalPy, Py_LIMITED_API))] // TODO: remove (see https://github.com/PyO3/pyo3/pull/1341#issuecomment-751515985) opaque_struct!(pub PyDictObject); diff --git a/pyo3-introspection/Cargo.toml b/pyo3-introspection/Cargo.toml index 54295c5f64d..02444e38898 100644 --- a/pyo3-introspection/Cargo.toml +++ b/pyo3-introspection/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3-introspection" -version = "0.27.1" +version = "0.27.2" description = "Introspect dynamic libraries built with PyO3 to get metadata about the exported Python types" authors = ["PyO3 Project and Contributors "] homepage = "https://github.com/pyo3/pyo3" diff --git a/pyo3-macros-backend/Cargo.toml b/pyo3-macros-backend/Cargo.toml index e76faeb69b4..28b4dd24b6e 100644 --- a/pyo3-macros-backend/Cargo.toml +++ b/pyo3-macros-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3-macros-backend" -version = "0.27.1" +version = "0.27.2" description = "Code generation for PyO3 package" authors = ["PyO3 Project and Contributors "] keywords = ["pyo3", "python", "cpython", "ffi"] @@ -17,7 +17,7 @@ rust-version.workspace = true [dependencies] heck = "0.5" proc-macro2 = { version = "1.0.60", default-features = false } -pyo3-build-config = { path = "../pyo3-build-config", version = "=0.27.1", features = ["resolve-config"] } +pyo3-build-config = { path = "../pyo3-build-config", version = "=0.27.2", features = ["resolve-config"] } quote = { version = "1", default-features = false } [dependencies.syn] @@ -27,7 +27,7 @@ default-features = false features = ["derive", "parsing", "printing", "clone-impls", "full", "extra-traits", "visit-mut"] [build-dependencies] -pyo3-build-config = { path = "../pyo3-build-config", version = "=0.27.1" } +pyo3-build-config = { path = "../pyo3-build-config", version = "=0.27.2" } [lints] workspace = true diff --git a/pyo3-macros/Cargo.toml b/pyo3-macros/Cargo.toml index bff62645960..1eb4793bae2 100644 --- a/pyo3-macros/Cargo.toml +++ b/pyo3-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3-macros" -version = "0.27.1" +version = "0.27.2" description = "Proc macros for PyO3 package" authors = ["PyO3 Project and Contributors "] keywords = ["pyo3", "python", "cpython", "ffi"] @@ -23,7 +23,7 @@ experimental-inspect = ["pyo3-macros-backend/experimental-inspect"] 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.27.1" } +pyo3-macros-backend = { path = "../pyo3-macros-backend", version = "=0.27.2" } [lints] workspace = true diff --git a/pyproject.toml b/pyproject.toml index f2fa6cb52bd..7139001ebff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ [tool.towncrier] filename = "CHANGELOG.md" -version = "0.27.1" +version = "0.27.2" start_string = "\n" template = ".towncrier.template.md" title_format = "## [{version}] - {project_date}" @@ -33,7 +33,9 @@ disable = [ # TODO: what to do about inline HTML, probably allow? "MD033", # TODO: {{#PYO3_DOCS_URL}} placeholder confuses rumdl, change syntax perhaps? - "MD051" + "MD051", + # Disabled on release-0.27 branch + "MD059", ] exclude = [ diff --git a/src/conversions/hashbrown.rs b/src/conversions/hashbrown.rs index 85bc350999b..48735a9734c 100644 --- a/src/conversions/hashbrown.rs +++ b/src/conversions/hashbrown.rs @@ -158,7 +158,7 @@ mod tests { let py_map = (&map).into_pyobject(py).unwrap(); - assert!(py_map.len() == 1); + assert_eq!(py_map.len(), 1); assert!( py_map .get_item(1) diff --git a/src/conversions/indexmap.rs b/src/conversions/indexmap.rs index 89ef8172961..cccd3c7a49a 100644 --- a/src/conversions/indexmap.rs +++ b/src/conversions/indexmap.rs @@ -165,7 +165,7 @@ mod test_indexmap { let py_map = (&map).into_pyobject(py).unwrap(); - assert!(py_map.len() == 1); + assert_eq!(py_map.len(), 1); assert!( py_map .get_item(1) diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index b66968cf305..5df09664df8 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -183,7 +183,7 @@ mod tests { .unwrap() .extract() .unwrap(); - assert!(&v == b"abcabcabcabcabcabcabcabcabcabcabc"); + assert_eq!(&v, b"abcabcabcabcabcabcabcabcabcabcabc"); }) } @@ -213,7 +213,7 @@ mod tests { .unwrap() .extract() .unwrap(); - assert!(&v == b"abcabcabcabcabcabcabcabcabcabcabc"); + assert_eq!(&v, b"abcabcabcabcabcabcabcabcabcabcabc"); }) } @@ -225,7 +225,7 @@ mod tests { .unwrap() .extract() .unwrap(); - assert!(&v == b"abc"); + assert_eq!(&v, b"abc"); }); } #[test] diff --git a/src/conversions/std/map.rs b/src/conversions/std/map.rs index 676351abf4a..ce6c50adb73 100644 --- a/src/conversions/std/map.rs +++ b/src/conversions/std/map.rs @@ -171,7 +171,7 @@ mod tests { let py_map = (&map).into_pyobject(py).unwrap(); - assert!(py_map.len() == 1); + assert_eq!(py_map.len(), 1); assert!( py_map .get_item(1) @@ -193,7 +193,7 @@ mod tests { let py_map = (&map).into_pyobject(py).unwrap(); - assert!(py_map.len() == 1); + assert_eq!(py_map.len(), 1); assert!( py_map .get_item(1) @@ -215,7 +215,7 @@ mod tests { let py_map = map.into_pyobject(py).unwrap(); - assert!(py_map.len() == 1); + assert_eq!(py_map.len(), 1); assert!( py_map .get_item(1) @@ -236,7 +236,7 @@ mod tests { let py_map = map.into_pyobject(py).unwrap(); - assert!(py_map.len() == 1); + assert_eq!(py_map.len(), 1); assert!( py_map .get_item(1) diff --git a/src/conversions/std/vec.rs b/src/conversions/std/vec.rs index f17a512290e..bd3a0e65714 100644 --- a/src/conversions/std/vec.rs +++ b/src/conversions/std/vec.rs @@ -165,7 +165,7 @@ mod tests { .unwrap() .extract() .unwrap(); - assert!(v == [1, 2]); + assert_eq!(v, [1, 2]); }); } @@ -177,7 +177,7 @@ mod tests { .unwrap() .extract() .unwrap(); - assert!(v == [1, 2, 3, 4]); + assert_eq!(v, [1, 2, 3, 4]); }); } @@ -189,7 +189,7 @@ mod tests { .unwrap() .extract() .unwrap(); - assert!(v == b"abc"); + assert_eq!(v, b"abc"); }); } } diff --git a/src/internal/state.rs b/src/internal/state.rs index 899c37bd13e..ba92e512800 100644 --- a/src/internal/state.rs +++ b/src/internal/state.rs @@ -504,7 +504,7 @@ mod tests { Python::attach(|py| { // Make a simple object with 1 reference let obj = get_object(py); - assert!(obj.get_refcnt(py) == 1); + assert_eq!(obj.get_refcnt(py), 1); // Cloning the object when detached should panic py.detach(|| obj.clone()); }); diff --git a/src/pycell.rs b/src/pycell.rs index 2a230b05833..8944fc2bf90 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -313,10 +313,10 @@ impl<'py, T: PyClass> PyRef<'py, T> { } } -impl<'p, T, U> PyRef<'p, T> +impl<'p, T> PyRef<'p, T> where - T: PyClass, - U: PyClass, + T: PyClass, + T::BaseType: PyClass, { /// Gets a `PyRef`. /// @@ -363,10 +363,10 @@ where /// # pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'") /// # }); /// ``` - pub fn into_super(self) -> PyRef<'p, U> { + pub fn into_super(self) -> PyRef<'p, T::BaseType> { let py = self.py(); let t_not_frozen = !::VALUE; - let u_frozen = ::VALUE; + let u_frozen = <::Frozen as crate::pyclass::boolean_struct::private::Boolean>::VALUE; if t_not_frozen && u_frozen { // If `T` is mutable subclass of `U` differ, then it is possible that we need to // release the borrow count now. (e.g. `U` may have a noop borrow checker so @@ -443,7 +443,7 @@ where /// # pyo3::py_run!(py, sub, "assert sub.format_name_lengths() == '9 8'") /// # }); /// ``` - pub fn as_super(&self) -> &PyRef<'p, U> { + pub fn as_super(&self) -> &PyRef<'p, T::BaseType> { let ptr = NonNull::from(&self.inner) // `Bound` has the same layout as `Bound` .cast::>() @@ -521,20 +521,20 @@ impl<'p, T: PyClass> PyRefMut<'p, T> { } } -impl AsRef for PyRefMut<'_, T> +impl AsRef for PyRefMut<'_, T> where - T: PyClass, - U: PyClass, + T: PyClass, + T::BaseType: PyClass, { fn as_ref(&self) -> &T::BaseType { PyRefMut::downgrade(self).as_super() } } -impl AsMut for PyRefMut<'_, T> +impl AsMut for PyRefMut<'_, T> where - T: PyClass, - U: PyClass, + T: PyClass, + T::BaseType: PyClass, { fn as_mut(&mut self) -> &mut T::BaseType { self.as_super() @@ -587,15 +587,15 @@ impl<'py, T: PyClass> PyRefMut<'py, T> { } } -impl<'p, T, U> PyRefMut<'p, T> +impl<'p, T> PyRefMut<'p, T> where - T: PyClass, - U: PyClass, + T: PyClass, + T::BaseType: PyClass, { /// Gets a `PyRef`. /// /// See [`PyRef::into_super`] for more. - pub fn into_super(self) -> PyRefMut<'p, U> { + pub fn into_super(self) -> PyRefMut<'p, T::BaseType> { let py = self.py(); PyRefMut { inner: unsafe { @@ -614,7 +614,7 @@ where /// can also be chained to access the super-superclass (and so on). /// /// See [`PyRef::as_super`] for more. - pub fn as_super(&mut self) -> &mut PyRefMut<'p, U> { + pub fn as_super(&mut self) -> &mut PyRefMut<'p, T::BaseType> { let mut ptr = NonNull::from(&mut self.inner) // `Bound` has the same layout as `Bound` .cast::>() diff --git a/src/pyclass/guard.rs b/src/pyclass/guard.rs index f2fd3d59b78..24144daeb2f 100644 --- a/src/pyclass/guard.rs +++ b/src/pyclass/guard.rs @@ -140,10 +140,10 @@ impl<'a, T: PyClass> PyClassGuard<'a, T> { } } -impl<'a, T, U> PyClassGuard<'a, T> +impl<'a, T> PyClassGuard<'a, T> where - T: PyClass, - U: PyClass, + T: PyClass, + T::BaseType: PyClass, { /// Borrows a shared reference to `PyClassGuard`. /// @@ -189,7 +189,7 @@ where /// # pyo3::py_run!(py, sub, "assert sub.format_name_lengths() == '9 8'") /// # }); /// ``` - pub fn as_super(&self) -> &PyClassGuard<'a, U> { + pub fn as_super(&self) -> &PyClassGuard<'a, T::BaseType> { // SAFETY: `PyClassGuard` and `PyClassGuard` have the same layout unsafe { NonNull::from(self).cast().as_ref() } } @@ -237,9 +237,10 @@ where /// # pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'") /// # }); /// ``` - pub fn into_super(self) -> PyClassGuard<'a, U> { + pub fn into_super(self) -> PyClassGuard<'a, T::BaseType> { let t_not_frozen = !::VALUE; - let u_frozen = ::VALUE; + let u_frozen = + <::Frozen as crate::pyclass::boolean_struct::private::Boolean>::VALUE; if t_not_frozen && u_frozen { // If `T` is a mutable subclass of a frozen `U` base, then it is possible that we need // to release the borrow count now. (e.g. `U` may have a noop borrow checker so dropping @@ -630,10 +631,10 @@ impl<'a, T: PyClass> PyClassGuardMut<'a, T> { } } -impl<'a, T, U> PyClassGuardMut<'a, T> +impl<'a, T> PyClassGuardMut<'a, T> where - T: PyClass, - U: PyClass, + T: PyClass, + T::BaseType: PyClass, { /// Borrows a mutable reference to `PyClassGuardMut`. /// @@ -643,7 +644,7 @@ where /// super-superclass (and so on). /// /// See [`PyClassGuard::as_super`] for more. - pub fn as_super(&mut self) -> &mut PyClassGuardMut<'a, U> { + pub fn as_super(&mut self) -> &mut PyClassGuardMut<'a, T::BaseType> { // SAFETY: `PyClassGuardMut` and `PyClassGuardMut` have the same layout unsafe { NonNull::from(self).cast().as_mut() } } @@ -651,7 +652,7 @@ where /// Gets a `PyClassGuardMut`. /// /// See [`PyClassGuard::into_super`] for more. - pub fn into_super(self) -> PyClassGuardMut<'a, U> { + pub fn into_super(self) -> PyClassGuardMut<'a, T::BaseType> { // `PyClassGuardMut` is only available for non-frozen classes, so there // is no possibility of leaking borrows like `PyClassGuard` PyClassGuardMut { diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index 507fa24c2be..7455597b270 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -138,8 +138,8 @@ impl PyClassInitializer { #[inline] pub fn add_subclass(self, subclass_value: S) -> PyClassInitializer where + T: PyClassBaseType, S: PyClass, - S::BaseType: PyClassBaseType, { PyClassInitializer::new(subclass_value, self) } diff --git a/src/sync.rs b/src/sync.rs index a3c527aa62f..40d77d712a9 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1467,7 +1467,7 @@ mod tests { Err(poisoned) => poisoned.into_inner(), } }); - assert!(*guard == 42); + assert_eq!(*guard, 42); } #[cfg(feature = "macros")] @@ -1699,7 +1699,7 @@ mod tests { Python::attach(|py| { // recover from the poisoning let guard = rwlock.write_py_attached(py).unwrap_err().into_inner(); - assert!(*guard == 42); + assert_eq!(*guard, 42); }); } } diff --git a/src/types/dict.rs b/src/types/dict.rs index 926354cc684..ae8e6cc171f 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -16,6 +16,7 @@ use crate::{ffi, BoundObject, IntoPyObject, IntoPyObjectExt, Python}; #[repr(transparent)] pub struct PyDict(PyAny); +#[cfg(not(GraalPy))] pyobject_subclassable_native_type!(PyDict, crate::ffi::PyDictObject); pyobject_native_type!( diff --git a/src/types/iterator.rs b/src/types/iterator.rs index c41d2fb85eb..860132fef53 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -70,10 +70,12 @@ pub enum PySendResult<'py> { #[cfg(all(not(PyPy), Py_3_10))] impl<'py> Bound<'py, PyIterator> { - /// Sends a value into a python generator. This is the equivalent of calling `generator.send(value)` in Python. - /// This resumes the generator and continues its execution until the next `yield` or `return` statement. - /// If the generator exits without returning a value, this function returns a `StopException`. - /// The first call to `send` must be made with `None` as the argument to start the generator, failing to do so will raise a `TypeError`. + /// Sends a value into a python generator. This is the equivalent of calling + /// `generator.send(value)` in Python. This resumes the generator and continues its execution + /// until the next `yield` or `return` statement. When the generator completes, the (optional) + /// return value will be returned as `PySendResult::Return`. All subsequent calls will return + /// `PySendResult::Return(None)`. The first call to `send` must be made with `None` as the + /// argument to start the generator, failing to do so will raise a `TypeError`. #[inline] pub fn send(&self, value: &Bound<'py, PyAny>) -> PyResult> { let py = self.py(); diff --git a/src/types/list.rs b/src/types/list.rs index ad3e0741c88..55aa50d39e4 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -1222,7 +1222,7 @@ mod tests { -5 }); assert_eq!(sum, -5); - assert!(list.len() == 0); + assert_eq!(list.len(), 0); }); } diff --git a/src/types/mutex.rs b/src/types/mutex.rs index d6b49be5038..fe9f9e436f3 100644 --- a/src/types/mutex.rs +++ b/src/types/mutex.rs @@ -452,7 +452,7 @@ mod tests { .join() }); mutex.clear_poison(); - assert!(*mutex.lock().unwrap() == 0); + assert_eq!(*mutex.lock().unwrap(), 0); } #[test] diff --git a/src/version.rs b/src/version.rs index 6760bd8d337..48d8b5d36a8 100644 --- a/src/version.rs +++ b/src/version.rs @@ -128,12 +128,12 @@ mod test { fn test_python_version_info_parse() { assert!(PythonVersionInfo::from_str("3.5.0a1").unwrap() >= (3, 5, 0)); assert!(PythonVersionInfo::from_str("3.5+").unwrap() >= (3, 5, 0)); - assert!(PythonVersionInfo::from_str("3.5+").unwrap() == (3, 5, 0)); - assert!(PythonVersionInfo::from_str("3.5+").unwrap() != (3, 5, 1)); + assert_eq!(PythonVersionInfo::from_str("3.5+").unwrap(), (3, 5, 0)); + assert_ne!(PythonVersionInfo::from_str("3.5+").unwrap(), (3, 5, 1)); assert!(PythonVersionInfo::from_str("3.5.2a1+").unwrap() < (3, 5, 3)); - assert!(PythonVersionInfo::from_str("3.5.2a1+").unwrap() == (3, 5, 2)); - assert!(PythonVersionInfo::from_str("3.5.2a1+").unwrap() == (3, 5)); - assert!(PythonVersionInfo::from_str("3.5+").unwrap() == (3, 5)); + assert_eq!(PythonVersionInfo::from_str("3.5.2a1+").unwrap(), (3, 5, 2)); + assert_eq!(PythonVersionInfo::from_str("3.5.2a1+").unwrap(), (3, 5)); + assert_eq!(PythonVersionInfo::from_str("3.5+").unwrap(), (3, 5)); assert!(PythonVersionInfo::from_str("3.5.2a1+").unwrap() < (3, 6)); assert!(PythonVersionInfo::from_str("3.5.2a1+").unwrap() > (3, 4)); assert!(PythonVersionInfo::from_str("3.11.3+chromium.29").unwrap() >= (3, 11, 3)); diff --git a/tests/test_class_new.rs b/tests/test_class_new.rs index 7f56ebabc23..6a35e681500 100644 --- a/tests/test_class_new.rs +++ b/tests/test_class_new.rs @@ -244,12 +244,12 @@ fn test_new_existing() { let obj5 = typeobj.call1((2,)).unwrap(); let obj6 = typeobj.call1((2,)).unwrap(); - assert!(obj1.getattr("num").unwrap().extract::().unwrap() == 0); - assert!(obj2.getattr("num").unwrap().extract::().unwrap() == 0); - assert!(obj3.getattr("num").unwrap().extract::().unwrap() == 1); - assert!(obj4.getattr("num").unwrap().extract::().unwrap() == 1); - assert!(obj5.getattr("num").unwrap().extract::().unwrap() == 2); - assert!(obj6.getattr("num").unwrap().extract::().unwrap() == 2); + assert_eq!(obj1.getattr("num").unwrap().extract::().unwrap(), 0); + assert_eq!(obj2.getattr("num").unwrap().extract::().unwrap(), 0); + assert_eq!(obj3.getattr("num").unwrap().extract::().unwrap(), 1); + assert_eq!(obj4.getattr("num").unwrap().extract::().unwrap(), 1); + assert_eq!(obj5.getattr("num").unwrap().extract::().unwrap(), 2); + assert_eq!(obj6.getattr("num").unwrap().extract::().unwrap(), 2); assert!(obj1.is(&obj2)); assert!(obj3.is(&obj4)); diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index b24b94397d6..d045d43dfeb 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -52,8 +52,8 @@ macro_rules! assert_check_exact { ($check_func:ident, $check_func_exact:ident, $obj: expr) => { unsafe { use pyo3::ffi::*; - assert!($check_func(($obj).as_ptr()) != 0); - assert!($check_func_exact(($obj).as_ptr()) != 0); + assert_ne!($check_func(($obj).as_ptr()), 0); + assert_ne!($check_func_exact(($obj).as_ptr()), 0); } }; } @@ -62,8 +62,8 @@ macro_rules! assert_check_only { ($check_func:ident, $check_func_exact:ident, $obj: expr) => { unsafe { use pyo3::ffi::*; - assert!($check_func(($obj).as_ptr()) != 0); - assert!($check_func_exact(($obj).as_ptr()) == 0); + assert_ne!($check_func(($obj).as_ptr()), 0); + assert_eq!($check_func_exact(($obj).as_ptr()), 0); } }; } diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index 99c26ae1041..f03e0c47a99 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -179,6 +179,7 @@ except Exception as e: mod inheriting_native_type { use super::*; use pyo3::exceptions::PyException; + #[cfg(not(GraalPy))] use pyo3::types::PyDict; #[cfg(not(any(PyPy, GraalPy)))] @@ -211,6 +212,7 @@ mod inheriting_native_type { }); } + #[cfg(not(GraalPy))] #[pyclass(extends=PyDict)] #[derive(Debug)] struct DictWithName { @@ -218,6 +220,7 @@ mod inheriting_native_type { _name: &'static str, } + #[cfg(not(GraalPy))] #[pymethods] impl DictWithName { #[new] @@ -226,6 +229,7 @@ mod inheriting_native_type { } } + #[cfg(not(GraalPy))] #[test] fn inherit_dict() { Python::attach(|py| { @@ -238,6 +242,7 @@ mod inheriting_native_type { }); } + #[cfg(not(GraalPy))] #[test] fn inherit_dict_drop() { Python::attach(|py| { diff --git a/tests/ui/invalid_pyfunction_argument.stderr b/tests/ui/invalid_pyfunction_argument.stderr index fe867f6a797..a741f10f9e6 100644 --- a/tests/ui/invalid_pyfunction_argument.stderr +++ b/tests/ui/invalid_pyfunction_argument.stderr @@ -70,8 +70,13 @@ error[E0277]: `Foo` cannot be used as a Python function argument --> tests/ui/invalid_pyfunction_argument.rs:14:59 | 14 | fn skip_from_py_object_without_custom_from_py_object(arg: Foo) { - | ^^^ the trait `ExtractPyClassWithClone` is not implemented for `Foo` + | ^^^ unsatisfied trait bound | +help: the trait `ExtractPyClassWithClone` is not implemented for `Foo` + --> tests/ui/invalid_pyfunction_argument.rs:11:1 + | +11 | struct Foo; + | ^^^^^^^^^^ = note: implement `FromPyObject` to enable using `Foo` as a function argument = note: `Python<'py>` is also a valid argument type to pass the Python token into `#[pyfunction]`s and `#[pymethods]` = help: the following other types implement trait `PyFunctionArgument<'a, 'holder, 'py, IMPLEMENTS_FROMPYOBJECT>`: diff --git a/tests/ui/invalid_pymethods.stderr b/tests/ui/invalid_pymethods.stderr index 66acc374f63..94c3604e7fe 100644 --- a/tests/ui/invalid_pymethods.stderr +++ b/tests/ui/invalid_pymethods.stderr @@ -201,6 +201,11 @@ error[E0277]: the trait bound `NotATypeObject: From>` i --> tests/ui/invalid_pymethods.rs:33:45 | 33 | fn classmethod_wrong_first_argument(_t: NotATypeObject) -> Self { - | ^^^^^^^^^^^^^^ the trait `From>` is not implemented for `NotATypeObject` + | ^^^^^^^^^^^^^^ unsatisfied trait bound | +help: the trait `From>` is not implemented for `NotATypeObject` + --> tests/ui/invalid_pymethods.rs:28:1 + | +28 | struct NotATypeObject; + | ^^^^^^^^^^^^^^^^^^^^^ = note: required for `BoundRef<'_, '_, PyType>` to implement `Into` diff --git a/tests/ui/missing_intopy.stderr b/tests/ui/missing_intopy.stderr index afa8d9e48a4..2d3e39dadea 100644 --- a/tests/ui/missing_intopy.stderr +++ b/tests/ui/missing_intopy.stderr @@ -2,8 +2,13 @@ error[E0277]: `Blah` cannot be converted to a Python object --> tests/ui/missing_intopy.rs:4:14 | 4 | fn blah() -> Blah { - | ^^^^ the trait `IntoPyObject<'_>` is not implemented for `Blah` + | ^^^^ unsatisfied trait bound | +help: the trait `IntoPyObject<'_>` is not implemented for `Blah` + --> tests/ui/missing_intopy.rs:1:1 + | +1 | struct Blah; + | ^^^^^^^^^^^ = note: `IntoPyObject` is automatically implemented by the `#[pyclass]` macro = note: if you do not wish to have a corresponding Python type, implement it manually = note: if you do not own `Blah` you can perform a manual conversion to one of the types in `pyo3::types::*` diff --git a/tests/ui/reject_generics.stderr b/tests/ui/reject_generics.stderr index 406ac2832c7..6d9f66f5ee1 100644 --- a/tests/ui/reject_generics.stderr +++ b/tests/ui/reject_generics.stderr @@ -1,10 +1,10 @@ -error: #[pyclass] cannot have generic parameters. For an explanation, see https://pyo3.rs/v0.27.1/class.html#no-generic-parameters +error: #[pyclass] cannot have generic parameters. For an explanation, see https://pyo3.rs/v0.27.2/class.html#no-generic-parameters --> tests/ui/reject_generics.rs:4:25 | 4 | struct ClassWithGenerics { | ^ -error: #[pyclass] cannot have lifetime parameters. For an explanation, see https://pyo3.rs/v0.27.1/class.html#no-lifetime-parameters +error: #[pyclass] cannot have lifetime parameters. For an explanation, see https://pyo3.rs/v0.27.2/class.html#no-lifetime-parameters --> tests/ui/reject_generics.rs:9:27 | 9 | struct ClassWithLifetimes<'a> {