From 3180e5cc06f87066d158f31a70b04909899250d2 Mon Sep 17 00:00:00 2001 From: konstin Date: Wed, 17 Jun 2020 10:10:00 +0200 Subject: [PATCH 01/10] POC stable rust (2 tests failing) --- .github/workflows/test.yml | 2 +- .travis.yml | 2 +- CHANGELOG.md | 1 + Cargo.toml | 4 +--- README.md | 6 +----- build.rs | 34 ---------------------------------- guide/src/rust_cpython.md | 2 +- src/conversion.rs | 20 ++++++++++++++++++++ src/lib.rs | 2 +- src/types/sequence.rs | 27 +++++++++++++++++++++++++++ tests/test_datetime.rs | 9 ++++++++- 11 files changed, 62 insertions(+), 47 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c0ccb60001a..cae815a3b3b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: nightly + toolchain: stable default: true - run: rustup set default-host ${{ matrix.platform.rust-target }} - name: Build without default features diff --git a/.travis.yml b/.travis.yml index 5ab7b52724e..8f9d450ddcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ jobs: env: global: - - TRAVIS_RUST_VERSION=nightly + - TRAVIS_RUST_VERSION=stable - RUST_BACKTRACE=1 before_install: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fe74cb09ac..883fcde29ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added +- stable rust support - Add FFI definition `PyObject_AsFileDescriptor` [#938](https://github.com/PyO3/pyo3/pull/938) - Add `PyByteArray::data`, `PyByteArray::as_bytes`, and `PyByteArray::as_bytes_mut`. [#967](https://github.com/PyO3/pyo3/pull/967) - Add `Py::borrow`, `Py::borrow_mut`, `Py::try_borrow`, and `Py::try_borrow_mut` for accessing `#[pyclass]` values. [#976](https://github.com/PyO3/pyo3/pull/976) diff --git a/Cargo.toml b/Cargo.toml index d18cafd6ee4..7fa6398f54a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,12 +34,10 @@ unindent = { version = "0.1.4", optional = true } assert_approx_eq = "1.1.0" trybuild = "1.0.23" -[build-dependencies] -version_check = "0.9.1" - [features] default = ["macros"] macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"] +nightly = [] # this is no longer needed internally, but setuptools-rust assumes this feature python3 = [] diff --git a/README.md b/README.md index 37e1b79e3d5..305b02f8d1d 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste ## Usage -PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.42.0-nightly 2020-01-21. - -If you have never used nightly Rust, the official guide has -[a great section](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#rustup-and-the-role-of-rust-nightly) -about installing it. +PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.44. PyPy is also supported (via cpyext) for Python 3.5 only, targeted PyPy version is 7.0.0. Please refer to the [pypy section in the guide](https://pyo3.rs/master/pypy.html). diff --git a/build.rs b/build.rs index 4459041d557..15621c35042 100644 --- a/build.rs +++ b/build.rs @@ -8,13 +8,6 @@ use std::{ process::{Command, Stdio}, str::FromStr, }; -use version_check::{Channel, Date, Version}; - -/// Specifies the minimum nightly version needed to compile pyo3. -/// Keep this synced up with the travis ci config, -/// But note that this is the rustc version which can be lower than the nightly version -const MIN_DATE: &str = "2020-01-20"; -const MIN_VERSION: &str = "1.42.0-nightly"; const PY3_MIN_MINOR: u8 = 5; const CFG_KEY: &str = "py_sys_config"; @@ -567,34 +560,7 @@ fn check_target_architecture(interpreter_config: &InterpreterConfig) -> Result<( Ok(()) } -fn check_rustc_version() -> Result<()> { - let channel = Channel::read().ok_or("Failed to determine rustc channel")?; - if !channel.supports_features() { - bail!("PyO3 requires a nightly or dev version of Rust."); - } - - let actual_version = Version::read().ok_or("Failed to determine the rustc version")?; - if !actual_version.at_least(MIN_VERSION) { - bail!( - "PyO3 requires at least rustc {}, while the current version is {}", - MIN_VERSION, - actual_version - ) - } - - let actual_date = Date::read().ok_or("Failed to determine the rustc date")?; - if !actual_date.at_least(MIN_DATE) { - bail!( - "PyO3 requires at least rustc {}, while the current rustc date is {}", - MIN_DATE, - actual_date - ) - } - Ok(()) -} - fn main() -> Result<()> { - check_rustc_version()?; // 1. Setup cfg variables so we can do conditional compilation in this library based on the // python interpeter's compilation flags. This is necessary for e.g. matching the right unicode // and threading interfaces. First check if we're cross compiling, if so, we cannot run the diff --git a/guide/src/rust_cpython.md b/guide/src/rust_cpython.md index e5912465014..4eb670d2a48 100644 --- a/guide/src/rust_cpython.md +++ b/guide/src/rust_cpython.md @@ -6,7 +6,7 @@ This chapter is based on the discussion in [PyO3/pyo3#55](https://github.com/PyO ## Macros -While rust-cpython has a macro based dsl for declaring modules and classes, PyO3 uses proc macros and specialization. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions. The disadvantage is that specialization currently only works on nightly. +While rust-cpython has a macro based dsl for declaring modules and classes, PyO3 uses proc macros and specialization. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions. **rust-cpython** diff --git a/src/conversion.rs b/src/conversion.rs index 52e684394ad..8ec196f8bbc 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -98,6 +98,7 @@ pub trait ToBorrowedObject: ToPyObject { F: FnOnce(*mut ffi::PyObject) -> R; } +#[cfg(feature = "nightly")] impl ToBorrowedObject for T where T: ToPyObject, @@ -115,6 +116,25 @@ where } } +#[cfg(not(feature = "nightly"))] +impl ToBorrowedObject for T +where + T: ToPyObject, +{ + fn with_borrowed_ptr(&self, py: Python, f: F) -> R + where + F: FnOnce(*mut ffi::PyObject) -> R, + { + let ptr = self.to_object(py).into_ptr(); + let result = f(ptr); + unsafe { + ffi::Py_XDECREF(ptr); + } + result + } +} + +#[cfg(feature = "nightly")] impl ToBorrowedObject for T where T: ToPyObject + AsPyPointer, diff --git a/src/lib.rs b/src/lib.rs index 92204145722..948af9d6b55 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![cfg_attr(nightly, feature(specialization))] #![allow(clippy::missing_safety_doc)] // FIXME (#698) //! Rust bindings to the Python interpreter. diff --git a/src/types/sequence.rs b/src/types/sequence.rs index 33a84a923ba..b8aedb3cad8 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -1,5 +1,6 @@ // Copyright (c) 2017-present PyO3 Project and Contributors +#[cfg(feature = "nightly")] use crate::buffer; use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::exceptions; @@ -261,6 +262,7 @@ impl PySequence { macro_rules! array_impls { ($($N:expr),+) => { $( + #[cfg(feature = "nightly")] impl<'a, T> FromPyObject<'a> for [T; $N] where T: Copy + Default + FromPyObject<'a>, @@ -272,6 +274,19 @@ macro_rules! array_impls { } } + #[cfg(not(feature = "nightly"))] + impl<'a, T> FromPyObject<'a> for [T; $N] + where + T: Copy + Default + FromPyObject<'a>, + { + fn extract(obj: &'a PyAny) -> PyResult { + let mut array = [T::default(); $N]; + extract_sequence_into_slice(obj, &mut array)?; + Ok(array) + } + } + + #[cfg(feature = "nightly")] impl<'source, T> FromPyObject<'source> for [T; $N] where for<'a> T: Copy + Default + FromPyObject<'a> + buffer::Element, @@ -300,6 +315,17 @@ array_impls!( 26, 27, 28, 29, 30, 31, 32 ); +#[cfg(not(feature = "nightly"))] +impl<'a, T> FromPyObject<'a> for Vec +where + T: FromPyObject<'a>, +{ + fn extract(obj: &'a PyAny) -> PyResult { + extract_sequence(obj) + } +} + +#[cfg(feature = "nightly")] impl<'a, T> FromPyObject<'a> for Vec where T: FromPyObject<'a>, @@ -309,6 +335,7 @@ where } } +#[cfg(feature = "nightly")] impl<'source, T> FromPyObject<'source> for Vec where for<'a> T: FromPyObject<'a> + buffer::Element + Copy, diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index b5db6d08f5a..81e2010f230 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -1,5 +1,6 @@ -#![feature(concat_idents)] +#![cfg_attr(nightly, feature(concat_idents))] +#[cfg(feature = "nightly")] use pyo3::ffi::*; use pyo3::prelude::*; use pyo3::types::IntoPyDict; @@ -34,6 +35,7 @@ fn _get_subclasses<'p>( Ok((obj, sub_obj, sub_sub_obj)) } +#[cfg(feature = "nightly")] macro_rules! assert_check_exact { ($check_func:ident, $obj: expr) => { unsafe { @@ -44,6 +46,7 @@ macro_rules! assert_check_exact { }; } +#[cfg(feature = "nightly")] macro_rules! assert_check_only { ($check_func:ident, $obj: expr) => { unsafe { @@ -54,6 +57,7 @@ macro_rules! assert_check_only { }; } +#[cfg(feature = "nightly")] #[test] fn test_date_check() { let gil = Python::acquire_gil(); @@ -65,6 +69,7 @@ fn test_date_check() { assert_check_only!(PyDate_Check, sub_sub_obj); } +#[cfg(feature = "nightly")] #[test] fn test_time_check() { let gil = Python::acquire_gil(); @@ -76,6 +81,7 @@ fn test_time_check() { assert_check_only!(PyTime_Check, sub_sub_obj); } +#[cfg(feature = "nightly")] #[test] fn test_datetime_check() { let gil = Python::acquire_gil(); @@ -90,6 +96,7 @@ fn test_datetime_check() { assert_check_only!(PyDateTime_Check, sub_sub_obj); } +#[cfg(feature = "nightly")] #[test] fn test_delta_check() { let gil = Python::acquire_gil(); From 6798fe9da74ca986e9cf2687cc5db2afa5157aac Mon Sep 17 00:00:00 2001 From: konstin Date: Wed, 17 Jun 2020 10:41:51 +0200 Subject: [PATCH 02/10] Fix tests on stable --- tests/test_compile_error.rs | 2 +- tests/ui/invalid_pymethod_names.stderr | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index a683f5f58f2..6820d3ee139 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -4,7 +4,6 @@ fn test_compile_errors() { t.compile_fail("tests/ui/invalid_macro_args.rs"); t.compile_fail("tests/ui/invalid_property_args.rs"); t.compile_fail("tests/ui/invalid_pyclass_args.rs"); - t.compile_fail("tests/ui/invalid_pymethod_names.rs"); t.compile_fail("tests/ui/missing_clone.rs"); t.compile_fail("tests/ui/reject_generics.rs"); t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs"); @@ -12,6 +11,7 @@ fn test_compile_errors() { // we skip this test. // TODO(kngwyu): Remove this `if` when we update minimum nightly. if option_env!("TRAVIS_JOB_NAME") != Some("Minimum nightly") { + t.compile_fail("tests/ui/invalid_pymethod_names.rs"); t.compile_fail("tests/ui/static_ref.rs"); } } diff --git a/tests/ui/invalid_pymethod_names.stderr b/tests/ui/invalid_pymethod_names.stderr index fc1911ac191..17f3595b0b1 100644 --- a/tests/ui/invalid_pymethod_names.stderr +++ b/tests/ui/invalid_pymethod_names.stderr @@ -2,16 +2,16 @@ error: name not allowed with this attribute --> $DIR/invalid_pymethod_names.rs:10:5 | 10 | #[name = "num"] - | ^^^^^^^^^^^^^^^ + | ^ error: #[name] can not be specified multiple times --> $DIR/invalid_pymethod_names.rs:17:5 | 17 | #[name = "foo"] - | ^^^^^^^^^^^^^^^ + | ^ error: name not allowed with this attribute --> $DIR/invalid_pymethod_names.rs:24:5 | 24 | #[name = "makenew"] - | ^^^^^^^^^^^^^^^^^^^ + | ^ From 0f709d6edc0337e5bd42a042199947f316dc076d Mon Sep 17 00:00:00 2001 From: kngwyu Date: Thu, 18 Jun 2020 18:01:57 +0900 Subject: [PATCH 03/10] Use paste instead of nightly concat_idents in datetime tests --- tests/test_datetime.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index 81e2010f230..f31148385ed 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -1,7 +1,3 @@ -#![cfg_attr(nightly, feature(concat_idents))] - -#[cfg(feature = "nightly")] -use pyo3::ffi::*; use pyo3::prelude::*; use pyo3::types::IntoPyDict; @@ -35,29 +31,26 @@ fn _get_subclasses<'p>( Ok((obj, sub_obj, sub_sub_obj)) } -#[cfg(feature = "nightly")] macro_rules! assert_check_exact { ($check_func:ident, $obj: expr) => { unsafe { - use pyo3::AsPyPointer; + use pyo3::{AsPyPointer, ffi::*}; assert!($check_func(($obj).as_ptr()) != 0); - assert!(concat_idents!($check_func, Exact)(($obj).as_ptr()) != 0); + assert!(pyo3::paste::expr!([<$check_func Exact>])(($obj).as_ptr()) != 0); } }; } -#[cfg(feature = "nightly")] macro_rules! assert_check_only { ($check_func:ident, $obj: expr) => { unsafe { - use pyo3::AsPyPointer; + use pyo3::{AsPyPointer, ffi::*}; assert!($check_func(($obj).as_ptr()) != 0); - assert!(concat_idents!($check_func, Exact)(($obj).as_ptr()) == 0); + assert!(pyo3::paste::expr!([<$check_func Exact>])(($obj).as_ptr()) == 0); } }; } -#[cfg(feature = "nightly")] #[test] fn test_date_check() { let gil = Python::acquire_gil(); @@ -69,7 +62,6 @@ fn test_date_check() { assert_check_only!(PyDate_Check, sub_sub_obj); } -#[cfg(feature = "nightly")] #[test] fn test_time_check() { let gil = Python::acquire_gil(); @@ -81,7 +73,6 @@ fn test_time_check() { assert_check_only!(PyTime_Check, sub_sub_obj); } -#[cfg(feature = "nightly")] #[test] fn test_datetime_check() { let gil = Python::acquire_gil(); @@ -96,7 +87,6 @@ fn test_datetime_check() { assert_check_only!(PyDateTime_Check, sub_sub_obj); } -#[cfg(feature = "nightly")] #[test] fn test_delta_check() { let gil = Python::acquire_gil(); From 654ab4c819285c3e4201f29eb447519f32bf6ea7 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Thu, 18 Jun 2020 18:16:01 +0900 Subject: [PATCH 04/10] Enable tests for --features nighly on travis --- .travis.yml | 10 ++++++---- ci/travis/setup.sh | 2 +- ci/travis/test.sh | 2 +- src/lib.rs | 2 +- tests/test_compile_error.rs | 10 +++------- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8f9d450ddcf..b1f946d8bbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,23 +16,25 @@ jobs: python: "3.7" - name: Python 3.8 python: "3.8" + # Run clippy and rustfmt + env: RUN_LINT=1 - name: Python 3.9-dev python: "3.9-dev" - - name: Minimum nightly + - name: Nightly python: "3.7" - # Keep this synced up with build.rs and ensure that the nightly version does have clippy available - # https://static.rust-lang.org/dist/YYYY-MM-DD/clippy-nightly-x86_64-unknown-linux-gnu.tar.gz exists - env: TRAVIS_RUST_VERSION=nightly-2020-01-21 + env: TRAVIS_RUST_VERSION=nightly FEATURES="nightly" - name: PyPy3.5 7.0 # Tested via anaconda PyPy (since travis's PyPy version is too old) python: "3.7" env: FEATURES="pypy" PATH="$PATH:/opt/anaconda/envs/pypy3/bin" allow_failures: + - name: Nightly - python: 3.9-dev env: global: - TRAVIS_RUST_VERSION=stable - RUST_BACKTRACE=1 + - RUN_LINT=0 before_install: - source ./ci/travis/setup.sh diff --git a/ci/travis/setup.sh b/ci/travis/setup.sh index d9c6f66e35f..c436691afc4 100755 --- a/ci/travis/setup.sh +++ b/ci/travis/setup.sh @@ -7,7 +7,7 @@ set -e # Use profile=minimal here to skip installing clippy curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain=$TRAVIS_RUST_VERSION --profile=minimal -y export PATH=$PATH:$HOME/.cargo/bin -if [ "$TRAVIS_JOB_NAME" = "Minimum nightly" ]; then +if [[ $RUN_LINT == 1 ]]; then rustup component add clippy rustup component add rustfmt fi diff --git a/ci/travis/test.sh b/ci/travis/test.sh index 975e05bc42a..eb85e926aca 100755 --- a/ci/travis/test.sh +++ b/ci/travis/test.sh @@ -10,7 +10,7 @@ else PYTHON_SYS_EXECUTABLE="/opt/anaconda/envs/pypy3/bin/pypy3" cargo build; fi -if [ "$TRAVIS_JOB_NAME" = "Minimum nightly" ]; then +if [[ $RUN_LINT == 1 ]]; then pip install --pre black==19.3b0 make lint fi diff --git a/src/lib.rs b/src/lib.rs index 948af9d6b55..f55b1a07102 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(nightly, feature(specialization))] +#![cfg_attr(feature = "nightly", feature(specialization))] #![allow(clippy::missing_safety_doc)] // FIXME (#698) //! Rust bindings to the Python interpreter. diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index 6820d3ee139..67ab76980b4 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -7,11 +7,7 @@ fn test_compile_errors() { t.compile_fail("tests/ui/missing_clone.rs"); t.compile_fail("tests/ui/reject_generics.rs"); t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs"); - // Since the current minimum nightly(2020-01-20) has a different error message, - // we skip this test. - // TODO(kngwyu): Remove this `if` when we update minimum nightly. - if option_env!("TRAVIS_JOB_NAME") != Some("Minimum nightly") { - t.compile_fail("tests/ui/invalid_pymethod_names.rs"); - t.compile_fail("tests/ui/static_ref.rs"); - } + t.compile_fail("tests/ui/static_ref.rs"); + #[cfg(not(feature = "nightly"))] + t.compile_fail("tests/ui/invalid_pymethod_names.rs"); } From 64408142f5d92395e7f05e92a6eeb1e08521a093 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Thu, 18 Jun 2020 18:43:04 +0900 Subject: [PATCH 05/10] Use all-stable feature in CI instead of --all-features --- Cargo.toml | 3 +++ Makefile | 2 +- ci/travis/guide.sh | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7fa6398f54a..35fa56cdda6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,9 @@ trybuild = "1.0.23" [features] default = ["macros"] macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"] +# For CI +all-stable = ["default", "num-bigint", "num-complex"] +# Optimizes PyObject to Vec conversion and so on. nightly = [] # this is no longer needed internally, but setuptools-rust assumes this feature diff --git a/Makefile b/Makefile index 897208f9d9b..4a41a384943 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ fmt: clippy: @touch src/lib.rs # Touching file to ensure that cargo clippy will re-check the project - cargo clippy --all-features --all-targets -- \ + cargo clippy --features=all-stable --all-targets -- \ $(addprefix -D ,${CLIPPY_LINTS_TO_DENY}) for example in examples/*; do (cd $$example/; cargo clippy) || exit 1; done diff --git a/ci/travis/guide.sh b/ci/travis/guide.sh index 898f0889ec5..7a963074140 100755 --- a/ci/travis/guide.sh +++ b/ci/travis/guide.sh @@ -21,7 +21,7 @@ mdbook build -d ../target/guide guide # Build the doc # This builds the book in target/doc -cargo doc --all-features --no-deps +cargo doc --features=all-stable --no-deps echo "" > target/doc/index.html # Get the lastest tag across all branches From a840ebbb5a82bde33fa1b0c1de1d059c6530bde5 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Thu, 18 Jun 2020 18:49:43 +0900 Subject: [PATCH 06/10] Support Rust 1.39.0 --- .travis.yml | 3 +++ Cargo.toml | 1 + README.md | 3 ++- build.rs | 6 +++--- pyo3-derive-backend/src/pyfunction.rs | 19 ++++++++++-------- pyo3-derive-backend/src/pymethod.rs | 16 ++++++++------- tests/test_compile_error.rs | 28 ++++++++++++++++++--------- 7 files changed, 48 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml index b1f946d8bbe..06b7f48a7ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,9 @@ jobs: - name: Nightly python: "3.7" env: TRAVIS_RUST_VERSION=nightly FEATURES="nightly" + - name: Minimum Stable + python: "3.7" + env: TRAVIS_RUST_VERSION=1.39.0 - name: PyPy3.5 7.0 # Tested via anaconda PyPy (since travis's PyPy version is too old) python: "3.7" env: FEATURES="pypy" PATH="$PATH:/opt/anaconda/envs/pypy3/bin" diff --git a/Cargo.toml b/Cargo.toml index 35fa56cdda6..52980a97331 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ unindent = { version = "0.1.4", optional = true } [dev-dependencies] assert_approx_eq = "1.1.0" trybuild = "1.0.23" +rustversion = "1.0" [features] default = ["macros"] diff --git a/README.md b/README.md index 305b02f8d1d..faba2e50d8c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Actions Status](https://github.com/PyO3/pyo3/workflows/Test/badge.svg)](https://github.com/PyO3/pyo3/actions) [![codecov](https://codecov.io/gh/PyO3/pyo3/branch/master/graph/badge.svg)](https://codecov.io/gh/PyO3/pyo3) [![crates.io](http://meritbadge.herokuapp.com/pyo3)](https://crates.io/crates/pyo3) +[![minimum rustc 1.42](https://img.shields.io/badge/rustc-1.39+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) [![Join the dev chat](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/PyO3/Lobby) [Rust](http://www.rust-lang.org/) bindings for [Python](https://www.python.org/). This includes running and interacting with Python code from a Rust binary, as well as writing native Python modules. @@ -16,7 +17,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste ## Usage -PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.44. +PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.39.0. PyPy is also supported (via cpyext) for Python 3.5 only, targeted PyPy version is 7.0.0. Please refer to the [pypy section in the guide](https://pyo3.rs/master/pypy.html). diff --git a/build.rs b/build.rs index 15621c35042..a091e3e3169 100644 --- a/build.rs +++ b/build.rs @@ -69,8 +69,8 @@ impl FromStr for PythonInterpreterKind { type Err = Box; fn from_str(s: &str) -> Result { match s { - "CPython" => Ok(Self::CPython), - "PyPy" => Ok(Self::PyPy), + "CPython" => Ok(PythonInterpreterKind::CPython), + "PyPy" => Ok(PythonInterpreterKind::PyPy), _ => Err(format!("Invalid interpreter: {}", s).into()), } } @@ -295,7 +295,7 @@ fn run_python_script(interpreter: &Path, script: &str) -> Result { ); } } - Ok(ok) if !ok.status.success() => bail!("Python script failed: {}"), + Ok(ref ok) if !ok.status.success() => bail!("Python script failed: {}"), Ok(ok) => Ok(String::from_utf8(ok.stdout)?), } } diff --git a/pyo3-derive-backend/src/pyfunction.rs b/pyo3-derive-backend/src/pyfunction.rs index 689cd45f516..96ef584a9ca 100644 --- a/pyo3-derive-backend/src/pyfunction.rs +++ b/pyo3-derive-backend/src/pyfunction.rs @@ -179,22 +179,25 @@ pub fn parse_name_attribute(attrs: &mut Vec) -> syn::Result true, }); - match &*name_attrs { - [] => Ok(None), - [(syn::Lit::Str(s), span)] => { + if 1 < name_attrs.len() { + return Err(syn::Error::new( + name_attrs[0].1, + "#[name] can not be specified multiple times", + )); + } + + match name_attrs.get(0) { + Some((syn::Lit::Str(s), span)) => { let mut ident: syn::Ident = s.parse()?; // This span is the whole attribute span, which is nicer for reporting errors. ident.set_span(*span); Ok(Some(ident)) } - [(_, span)] => Err(syn::Error::new( + Some((_, span)) => Err(syn::Error::new( *span, "Expected string literal for #[name] argument", )), - [(_, span), ..] => Err(syn::Error::new( - *span, - "#[name] can not be specified multiple times", - )), + None => Ok(None), } } diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index d1f35df0241..a4ef8c497ac 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -512,10 +512,11 @@ fn impl_arg_param( }; if let syn::Type::Reference(tref) = ty { let (tref, mut_) = tref_preprocess(tref); - let as_deref = if mut_.is_some() { - quote! { as_deref_mut } + // To support Rustc 1.39.0, we don't use as_deref here... + let tmp_as_deref = if mut_.is_some() { + quote! { _tmp.as_mut().map(std::ops::DerefMut::deref_mut) } } else { - quote! { as_deref } + quote! { _tmp.as_ref().map(std::ops::Deref::deref) } }; // Get Option<&T> from Option> quote! { @@ -525,7 +526,7 @@ fn impl_arg_param( }, None => #default, }; - let #arg_name = _tmp.#as_deref(); + let #arg_name = #tmp_as_deref; } } else { quote! { @@ -731,8 +732,9 @@ pub(crate) fn impl_py_getter_def( /// Split an argument of pyo3::Python from the front of the arg list, if present fn split_off_python_arg<'a>(args: &'a [FnArg<'a>]) -> (Option<&FnArg>, &[FnArg]) { - match args { - [py, rest @ ..] if utils::if_type_is_python(&py.ty) => (Some(py), rest), - rest => (None, rest), + if args.get(0).map(|py| utils::if_type_is_python(&py.ty)) == Some(true) { + (Some(&args[0]), &args[1..]) + } else { + (None, args) } } diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index 67ab76980b4..468fac9037d 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -1,13 +1,23 @@ #[test] fn test_compile_errors() { let t = trybuild::TestCases::new(); - t.compile_fail("tests/ui/invalid_macro_args.rs"); - t.compile_fail("tests/ui/invalid_property_args.rs"); - t.compile_fail("tests/ui/invalid_pyclass_args.rs"); - t.compile_fail("tests/ui/missing_clone.rs"); - t.compile_fail("tests/ui/reject_generics.rs"); - t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs"); - t.compile_fail("tests/ui/static_ref.rs"); - #[cfg(not(feature = "nightly"))] - t.compile_fail("tests/ui/invalid_pymethod_names.rs"); + testcase_common(&t); + testcase_latest_stable(&t); + + fn testcase_common(t: &trybuild::TestCases) { + t.compile_fail("tests/ui/invalid_macro_args.rs"); + t.compile_fail("tests/ui/invalid_property_args.rs"); + t.compile_fail("tests/ui/invalid_pyclass_args.rs"); + t.compile_fail("tests/ui/missing_clone.rs"); + t.compile_fail("tests/ui/reject_generics.rs"); + t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs"); + t.compile_fail("tests/ui/invalid_pymethod_names.rs"); + } + + #[rustversion::all(since(1.43))] + fn testcase_latest_stable(t: &trybuild::TestCases) { + t.compile_fail("tests/ui/static_ref.rs"); + } + #[rustversion::before(1.43)] + fn testcase_latest_stable(_t: &trybuild::TestCases) {} } From ac922e575666478b2113315f96f582e4597b31df Mon Sep 17 00:00:00 2001 From: kngwyu Date: Thu, 18 Jun 2020 20:32:31 +0900 Subject: [PATCH 07/10] Exclude benches from 'make lint' clippy run --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4a41a384943..8986d05f923 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ fmt: clippy: @touch src/lib.rs # Touching file to ensure that cargo clippy will re-check the project - cargo clippy --features=all-stable --all-targets -- \ + cargo clippy --features=all-stable --tests -- \ $(addprefix -D ,${CLIPPY_LINTS_TO_DENY}) for example in examples/*; do (cd $$example/; cargo clippy) || exit 1; done From e27c4d75c938430ee7eda115fd1c358c6a30513c Mon Sep 17 00:00:00 2001 From: kngwyu Date: Fri, 19 Jun 2020 14:50:48 +0900 Subject: [PATCH 08/10] Add a document about nightly feature --- guide/src/advanced.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/guide/src/advanced.md b/guide/src/advanced.md index 7507771d351..00ec6006169 100644 --- a/guide/src/advanced.md +++ b/guide/src/advanced.md @@ -28,3 +28,9 @@ version = "0.8.1" extension-module = ["pyo3/extension-module"] default = ["extension-module"] ``` + +## `nightly` flag + +`pyo3/nightly` feature needs nightly compiler, but enables some optimizations as follows: +- `FromPyObject` for `Vec` and array is optimized when the object can be `PyBuffer` +- `ToBorrowedObject`, used by `PyDict::set_item` or so, is optimized when the taken object is a Python native type. From a63e426f6fb3aba4b9590b8c3ca1cd2c5d7c9ff7 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Fri, 19 Jun 2020 14:55:06 +0900 Subject: [PATCH 09/10] Some doc and test cleanups mainly around specialization --- CHANGELOG.md | 4 ++-- README.md | 2 +- guide/src/class.md | 8 ++++---- guide/src/rust_cpython.md | 2 +- tests/test_compile_error.rs | 3 +-- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 883fcde29ce..74f496eb4b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added -- stable rust support -- Add FFI definition `PyObject_AsFileDescriptor` [#938](https://github.com/PyO3/pyo3/pull/938) +- Support stable versions of Rust (>=1.39). [#969](https://github.com/PyO3/pyo3/pull/969) +- Add FFI definition `PyObject_AsFileDescriptor`. [#938](https://github.com/PyO3/pyo3/pull/938) - Add `PyByteArray::data`, `PyByteArray::as_bytes`, and `PyByteArray::as_bytes_mut`. [#967](https://github.com/PyO3/pyo3/pull/967) - Add `Py::borrow`, `Py::borrow_mut`, `Py::try_borrow`, and `Py::try_borrow_mut` for accessing `#[pyclass]` values. [#976](https://github.com/PyO3/pyo3/pull/976) diff --git a/README.md b/README.md index faba2e50d8c..8c3e60b81d5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Actions Status](https://github.com/PyO3/pyo3/workflows/Test/badge.svg)](https://github.com/PyO3/pyo3/actions) [![codecov](https://codecov.io/gh/PyO3/pyo3/branch/master/graph/badge.svg)](https://codecov.io/gh/PyO3/pyo3) [![crates.io](http://meritbadge.herokuapp.com/pyo3)](https://crates.io/crates/pyo3) -[![minimum rustc 1.42](https://img.shields.io/badge/rustc-1.39+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) +[![minimum rustc 1.39](https://img.shields.io/badge/rustc-1.39+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) [![Join the dev chat](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/PyO3/Lobby) [Rust](http://www.rust-lang.org/) bindings for [Python](https://www.python.org/). This includes running and interacting with Python code from a Rust binary, as well as writing native Python modules. diff --git a/guide/src/class.md b/guide/src/class.md index 391e6e40e4d..a351edfb2a0 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -888,12 +888,12 @@ documentation](https://docs.python.org/3/library/stdtypes.html#iterator-types). Users should be able to define a `#[pyclass]` with or without `#[pymethods]`, while PyO3 needs a trait with a function that returns all methods. Since it's impossible to make the code generation in pyclass dependent on whether there is an impl block, we'd need to implement the trait on -`#[pyclass]` and override the implementation in `#[pymethods]`, which is to the best of my knowledge -only possible with the specialization feature, which can't be used on stable. - -To escape this we use [inventory](https://github.com/dtolnay/inventory), +`#[pyclass]` and override the implementation in `#[pymethods]`. +To enable this, we use a static registry type provided by [inventory](https://github.com/dtolnay/inventory), which allows us to collect `impl`s from arbitrary source code by exploiting some binary trick. See [inventory: how it works](https://github.com/dtolnay/inventory#how-it-works) and `pyo3_derive_backend::py_class` for more details. +Also for `#[pyproto]`, we use a similar, but more task-specific registry and +initialize it by [ctor](https://github.com/mmastrac/rust-ctor) crate. Specifically, the following implementation is generated: diff --git a/guide/src/rust_cpython.md b/guide/src/rust_cpython.md index 4eb670d2a48..6d14e6c95c5 100644 --- a/guide/src/rust_cpython.md +++ b/guide/src/rust_cpython.md @@ -6,7 +6,7 @@ This chapter is based on the discussion in [PyO3/pyo3#55](https://github.com/PyO ## Macros -While rust-cpython has a macro based dsl for declaring modules and classes, PyO3 uses proc macros and specialization. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions. +While rust-cpython has a macro based dsl for declaring modules and classes, PyO3 uses proc macros. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions. **rust-cpython** diff --git a/tests/test_compile_error.rs b/tests/test_compile_error.rs index 468fac9037d..465922b59a8 100644 --- a/tests/test_compile_error.rs +++ b/tests/test_compile_error.rs @@ -13,8 +13,7 @@ fn test_compile_errors() { t.compile_fail("tests/ui/wrong_aspyref_lifetimes.rs"); t.compile_fail("tests/ui/invalid_pymethod_names.rs"); } - - #[rustversion::all(since(1.43))] + #[rustversion::since(1.43)] fn testcase_latest_stable(t: &trybuild::TestCases) { t.compile_fail("tests/ui/static_ref.rs"); } From 404f3981e53792dcbc1906848568a681bdeeff27 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 21 Jun 2020 16:09:50 +0900 Subject: [PATCH 10/10] Improve docs and Remove all-stable feature --- Cargo.toml | 2 -- Makefile | 2 +- ci/travis/guide.sh | 2 +- guide/src/advanced.md | 8 ++++---- guide/src/class.md | 2 +- guide/src/rust_cpython.md | 2 +- guide/src/trait_bounds.md | 1 + guide/src/types.md | 4 ++-- 8 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 52980a97331..3073775e2ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,8 +38,6 @@ rustversion = "1.0" [features] default = ["macros"] macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"] -# For CI -all-stable = ["default", "num-bigint", "num-complex"] # Optimizes PyObject to Vec conversion and so on. nightly = [] diff --git a/Makefile b/Makefile index 8986d05f923..5bfb5755650 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ fmt: clippy: @touch src/lib.rs # Touching file to ensure that cargo clippy will re-check the project - cargo clippy --features=all-stable --tests -- \ + cargo clippy --features="default num-bigint num-complex" --tests -- \ $(addprefix -D ,${CLIPPY_LINTS_TO_DENY}) for example in examples/*; do (cd $$example/; cargo clippy) || exit 1; done diff --git a/ci/travis/guide.sh b/ci/travis/guide.sh index 7a963074140..158497c900e 100755 --- a/ci/travis/guide.sh +++ b/ci/travis/guide.sh @@ -21,7 +21,7 @@ mdbook build -d ../target/guide guide # Build the doc # This builds the book in target/doc -cargo doc --features=all-stable --no-deps +cargo doc --features="default num-bigint num-complex" --no-deps echo "" > target/doc/index.html # Get the lastest tag across all branches diff --git a/guide/src/advanced.md b/guide/src/advanced.md index 00ec6006169..dc04d5d019a 100644 --- a/guide/src/advanced.md +++ b/guide/src/advanced.md @@ -29,8 +29,8 @@ extension-module = ["pyo3/extension-module"] default = ["extension-module"] ``` -## `nightly` flag +## The `nightly` feature -`pyo3/nightly` feature needs nightly compiler, but enables some optimizations as follows: -- `FromPyObject` for `Vec` and array is optimized when the object can be `PyBuffer` -- `ToBorrowedObject`, used by `PyDict::set_item` or so, is optimized when the taken object is a Python native type. +The `pyo3/nightly` feature needs the nightly Rust compiler. This allows PyO3 to use Rust's unstable specialization feature to apply the following optimizations: +- `FromPyObject` for `Vec` and `[T;N]` can perform a `memcpy` when the object is a `PyBuffer` +- `ToBorrowedObject` can skip a reference count increase when the provided object is a Python native type. diff --git a/guide/src/class.md b/guide/src/class.md index a351edfb2a0..47976993959 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -893,7 +893,7 @@ To enable this, we use a static registry type provided by [inventory](https://gi which allows us to collect `impl`s from arbitrary source code by exploiting some binary trick. See [inventory: how it works](https://github.com/dtolnay/inventory#how-it-works) and `pyo3_derive_backend::py_class` for more details. Also for `#[pyproto]`, we use a similar, but more task-specific registry and -initialize it by [ctor](https://github.com/mmastrac/rust-ctor) crate. +initialize it using the [ctor](https://github.com/mmastrac/rust-ctor) crate. Specifically, the following implementation is generated: diff --git a/guide/src/rust_cpython.md b/guide/src/rust_cpython.md index 6d14e6c95c5..3017b9571d4 100644 --- a/guide/src/rust_cpython.md +++ b/guide/src/rust_cpython.md @@ -6,7 +6,7 @@ This chapter is based on the discussion in [PyO3/pyo3#55](https://github.com/PyO ## Macros -While rust-cpython has a macro based dsl for declaring modules and classes, PyO3 uses proc macros. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions. +While rust-cpython has a `macro_rules!` based dsl for declaring modules and classes, PyO3 uses proc macros. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions. **rust-cpython** diff --git a/guide/src/trait_bounds.md b/guide/src/trait_bounds.md index 08e212b2219..6b4ef9d675e 100644 --- a/guide/src/trait_bounds.md +++ b/guide/src/trait_bounds.md @@ -153,6 +153,7 @@ Now we add the PyO3 annotations to the trait implementation: impl Model for UserModel { // the previous trait implementation } +``` However, the previous code will not compile. The compilation error is the following one: `error: #[pymethods] cannot be used on trait impl blocks` diff --git a/guide/src/types.md b/guide/src/types.md index cb36c31aad5..cff7da22bba 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -46,7 +46,7 @@ references is done at runtime using `PyCell`, a scheme very similar to ## Object types -### [`PyAny`] +### [`PyAny`][PyAny] **Represents:** a Python object of unspecified type, restricted to a GIL lifetime. Currently, `PyAny` can only ever occur as a reference, `&PyAny`. @@ -95,7 +95,7 @@ These types all implement `Deref`, so they all expose the same methods which can be found on `PyAny`. To see all Python types exposed by `PyO3` you should consult the -[`pyo3::types`] module. +[`pyo3::types`][pyo3::types] module. **Conversions:**