Skip to content

Commit 7075827

Browse files
authored
Merge pull request #969 from PyO3/poc-stable-rust
Stable Rust
2 parents 3cd55ee + 404f398 commit 7075827

23 files changed

+130
-100
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
- name: Install Rust
2626
uses: actions-rs/toolchain@v1
2727
with:
28-
toolchain: nightly
28+
toolchain: stable
2929
default: true
3030
- run: rustup set default-host ${{ matrix.platform.rust-target }}
3131
- name: Build without default features

.travis.yml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,28 @@ jobs:
1616
python: "3.7"
1717
- name: Python 3.8
1818
python: "3.8"
19+
# Run clippy and rustfmt
20+
env: RUN_LINT=1
1921
- name: Python 3.9-dev
2022
python: "3.9-dev"
21-
- name: Minimum nightly
23+
- name: Nightly
2224
python: "3.7"
23-
# Keep this synced up with build.rs and ensure that the nightly version does have clippy available
24-
# https://static.rust-lang.org/dist/YYYY-MM-DD/clippy-nightly-x86_64-unknown-linux-gnu.tar.gz exists
25-
env: TRAVIS_RUST_VERSION=nightly-2020-01-21
25+
env: TRAVIS_RUST_VERSION=nightly FEATURES="nightly"
26+
- name: Minimum Stable
27+
python: "3.7"
28+
env: TRAVIS_RUST_VERSION=1.39.0
2629
- name: PyPy3.5 7.0 # Tested via anaconda PyPy (since travis's PyPy version is too old)
2730
python: "3.7"
2831
env: FEATURES="pypy" PATH="$PATH:/opt/anaconda/envs/pypy3/bin"
2932
allow_failures:
33+
- name: Nightly
3034
- python: 3.9-dev
3135

3236
env:
3337
global:
34-
- TRAVIS_RUST_VERSION=nightly
38+
- TRAVIS_RUST_VERSION=stable
3539
- RUST_BACKTRACE=1
40+
- RUN_LINT=0
3641

3742
before_install:
3843
- source ./ci/travis/setup.sh

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
66

77
## [Unreleased]
88
### Added
9-
- Add FFI definition `PyObject_AsFileDescriptor` [#938](https://github.com/PyO3/pyo3/pull/938)
9+
- Support stable versions of Rust (>=1.39). [#969](https://github.com/PyO3/pyo3/pull/969)
10+
- Add FFI definition `PyObject_AsFileDescriptor`. [#938](https://github.com/PyO3/pyo3/pull/938)
1011
- Add `PyByteArray::data`, `PyByteArray::as_bytes`, and `PyByteArray::as_bytes_mut`. [#967](https://github.com/PyO3/pyo3/pull/967)
1112
- Add `GILOnceCell` to use in situations where `lazy_static` or `once_cell` can deadlock. [#975](https://github.com/PyO3/pyo3/pull/975)
1213
- 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)

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ unindent = { version = "0.1.4", optional = true }
3333
[dev-dependencies]
3434
assert_approx_eq = "1.1.0"
3535
trybuild = "1.0.23"
36-
37-
[build-dependencies]
38-
version_check = "0.9.1"
36+
rustversion = "1.0"
3937

4038
[features]
4139
default = ["macros"]
4240
macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"]
41+
# Optimizes PyObject to Vec conversion and so on.
42+
nightly = []
4343

4444
# this is no longer needed internally, but setuptools-rust assumes this feature
4545
python3 = []

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fmt:
1616

1717
clippy:
1818
@touch src/lib.rs # Touching file to ensure that cargo clippy will re-check the project
19-
cargo clippy --all-features --all-targets -- \
19+
cargo clippy --features="default num-bigint num-complex" --tests -- \
2020
$(addprefix -D ,${CLIPPY_LINTS_TO_DENY})
2121
for example in examples/*; do (cd $$example/; cargo clippy) || exit 1; done
2222

README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
[![Actions Status](https://github.com/PyO3/pyo3/workflows/Test/badge.svg)](https://github.com/PyO3/pyo3/actions)
55
[![codecov](https://codecov.io/gh/PyO3/pyo3/branch/master/graph/badge.svg)](https://codecov.io/gh/PyO3/pyo3)
66
[![crates.io](http://meritbadge.herokuapp.com/pyo3)](https://crates.io/crates/pyo3)
7+
[![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)
78
[![Join the dev chat](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/PyO3/Lobby)
89

910
[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,11 +17,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste
1617

1718
## Usage
1819

19-
PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.42.0-nightly 2020-01-21.
20-
21-
If you have never used nightly Rust, the official guide has
22-
[a great section](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#rustup-and-the-role-of-rust-nightly)
23-
about installing it.
20+
PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.39.0.
2421

2522
PyPy is also supported (via cpyext) for Python 3.5 only, targeted PyPy version is 7.0.0.
2623
Please refer to the [pypy section in the guide](https://pyo3.rs/master/pypy.html).

build.rs

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,6 @@ use std::{
88
process::{Command, Stdio},
99
str::FromStr,
1010
};
11-
use version_check::{Channel, Date, Version};
12-
13-
/// Specifies the minimum nightly version needed to compile pyo3.
14-
/// Keep this synced up with the travis ci config,
15-
/// But note that this is the rustc version which can be lower than the nightly version
16-
const MIN_DATE: &str = "2020-01-20";
17-
const MIN_VERSION: &str = "1.42.0-nightly";
1811

1912
const PY3_MIN_MINOR: u8 = 5;
2013
const CFG_KEY: &str = "py_sys_config";
@@ -76,8 +69,8 @@ impl FromStr for PythonInterpreterKind {
7669
type Err = Box<dyn std::error::Error>;
7770
fn from_str(s: &str) -> Result<Self> {
7871
match s {
79-
"CPython" => Ok(Self::CPython),
80-
"PyPy" => Ok(Self::PyPy),
72+
"CPython" => Ok(PythonInterpreterKind::CPython),
73+
"PyPy" => Ok(PythonInterpreterKind::PyPy),
8174
_ => Err(format!("Invalid interpreter: {}", s).into()),
8275
}
8376
}
@@ -302,7 +295,7 @@ fn run_python_script(interpreter: &Path, script: &str) -> Result<String> {
302295
);
303296
}
304297
}
305-
Ok(ok) if !ok.status.success() => bail!("Python script failed: {}"),
298+
Ok(ref ok) if !ok.status.success() => bail!("Python script failed: {}"),
306299
Ok(ok) => Ok(String::from_utf8(ok.stdout)?),
307300
}
308301
}
@@ -567,34 +560,7 @@ fn check_target_architecture(interpreter_config: &InterpreterConfig) -> Result<(
567560
Ok(())
568561
}
569562

570-
fn check_rustc_version() -> Result<()> {
571-
let channel = Channel::read().ok_or("Failed to determine rustc channel")?;
572-
if !channel.supports_features() {
573-
bail!("PyO3 requires a nightly or dev version of Rust.");
574-
}
575-
576-
let actual_version = Version::read().ok_or("Failed to determine the rustc version")?;
577-
if !actual_version.at_least(MIN_VERSION) {
578-
bail!(
579-
"PyO3 requires at least rustc {}, while the current version is {}",
580-
MIN_VERSION,
581-
actual_version
582-
)
583-
}
584-
585-
let actual_date = Date::read().ok_or("Failed to determine the rustc date")?;
586-
if !actual_date.at_least(MIN_DATE) {
587-
bail!(
588-
"PyO3 requires at least rustc {}, while the current rustc date is {}",
589-
MIN_DATE,
590-
actual_date
591-
)
592-
}
593-
Ok(())
594-
}
595-
596563
fn main() -> Result<()> {
597-
check_rustc_version()?;
598564
// 1. Setup cfg variables so we can do conditional compilation in this library based on the
599565
// python interpeter's compilation flags. This is necessary for e.g. matching the right unicode
600566
// and threading interfaces. First check if we're cross compiling, if so, we cannot run the

ci/travis/guide.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ mdbook build -d ../target/guide guide
2121

2222
# Build the doc
2323
# This builds the book in target/doc
24-
cargo doc --all-features --no-deps
24+
cargo doc --features="default num-bigint num-complex" --no-deps
2525
echo "<meta http-equiv=refresh content=0;url=pyo3/index.html>" > target/doc/index.html
2626

2727
# Get the lastest tag across all branches

ci/travis/setup.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ set -e
77
# Use profile=minimal here to skip installing clippy
88
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain=$TRAVIS_RUST_VERSION --profile=minimal -y
99
export PATH=$PATH:$HOME/.cargo/bin
10-
if [ "$TRAVIS_JOB_NAME" = "Minimum nightly" ]; then
10+
if [[ $RUN_LINT == 1 ]]; then
1111
rustup component add clippy
1212
rustup component add rustfmt
1313
fi

ci/travis/test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ else
1010
PYTHON_SYS_EXECUTABLE="/opt/anaconda/envs/pypy3/bin/pypy3" cargo build;
1111
fi
1212

13-
if [ "$TRAVIS_JOB_NAME" = "Minimum nightly" ]; then
13+
if [[ $RUN_LINT == 1 ]]; then
1414
pip install --pre black==19.3b0
1515
make lint
1616
fi

guide/src/advanced.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@ version = "0.8.1"
2828
extension-module = ["pyo3/extension-module"]
2929
default = ["extension-module"]
3030
```
31+
32+
## The `nightly` feature
33+
34+
The `pyo3/nightly` feature needs the nightly Rust compiler. This allows PyO3 to use Rust's unstable specialization feature to apply the following optimizations:
35+
- `FromPyObject` for `Vec` and `[T;N]` can perform a `memcpy` when the object is a `PyBuffer`
36+
- `ToBorrowedObject` can skip a reference count increase when the provided object is a Python native type.

guide/src/class.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -893,12 +893,12 @@ documentation](https://docs.python.org/3/library/stdtypes.html#iterator-types).
893893
Users should be able to define a `#[pyclass]` with or without `#[pymethods]`, while PyO3 needs a
894894
trait with a function that returns all methods. Since it's impossible to make the code generation in
895895
pyclass dependent on whether there is an impl block, we'd need to implement the trait on
896-
`#[pyclass]` and override the implementation in `#[pymethods]`, which is to the best of my knowledge
897-
only possible with the specialization feature, which can't be used on stable.
898-
899-
To escape this we use [inventory](https://github.com/dtolnay/inventory),
896+
`#[pyclass]` and override the implementation in `#[pymethods]`.
897+
To enable this, we use a static registry type provided by [inventory](https://github.com/dtolnay/inventory),
900898
which allows us to collect `impl`s from arbitrary source code by exploiting some binary trick.
901899
See [inventory: how it works](https://github.com/dtolnay/inventory#how-it-works) and `pyo3_derive_backend::py_class` for more details.
900+
Also for `#[pyproto]`, we use a similar, but more task-specific registry and
901+
initialize it using the [ctor](https://github.com/mmastrac/rust-ctor) crate.
902902

903903
Specifically, the following implementation is generated:
904904

guide/src/rust_cpython.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This chapter is based on the discussion in [PyO3/pyo3#55](https://github.com/PyO
66

77
## Macros
88

9-
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.
9+
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.
1010

1111
**rust-cpython**
1212

guide/src/trait_bounds.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ Now we add the PyO3 annotations to the trait implementation:
153153
impl Model for UserModel {
154154
// the previous trait implementation
155155
}
156+
```
156157

157158
However, the previous code will not compile. The compilation error is the following one:
158159
`error: #[pymethods] cannot be used on trait impl blocks`

guide/src/types.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ references is done at runtime using `PyCell`, a scheme very similar to
4646

4747
## Object types
4848

49-
### [`PyAny`]
49+
### [`PyAny`][PyAny]
5050

5151
**Represents:** a Python object of unspecified type, restricted to a GIL
5252
lifetime. Currently, `PyAny` can only ever occur as a reference, `&PyAny`.
@@ -95,7 +95,7 @@ These types all implement `Deref<Target = PyAny>`, so they all expose the same
9595
methods which can be found on `PyAny`.
9696

9797
To see all Python types exposed by `PyO3` you should consult the
98-
[`pyo3::types`] module.
98+
[`pyo3::types`][pyo3::types] module.
9999

100100
**Conversions:**
101101

pyo3-derive-backend/src/pyfunction.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,22 +179,25 @@ pub fn parse_name_attribute(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Opti
179179
_ => true,
180180
});
181181

182-
match &*name_attrs {
183-
[] => Ok(None),
184-
[(syn::Lit::Str(s), span)] => {
182+
if 1 < name_attrs.len() {
183+
return Err(syn::Error::new(
184+
name_attrs[0].1,
185+
"#[name] can not be specified multiple times",
186+
));
187+
}
188+
189+
match name_attrs.get(0) {
190+
Some((syn::Lit::Str(s), span)) => {
185191
let mut ident: syn::Ident = s.parse()?;
186192
// This span is the whole attribute span, which is nicer for reporting errors.
187193
ident.set_span(*span);
188194
Ok(Some(ident))
189195
}
190-
[(_, span)] => Err(syn::Error::new(
196+
Some((_, span)) => Err(syn::Error::new(
191197
*span,
192198
"Expected string literal for #[name] argument",
193199
)),
194-
[(_, span), ..] => Err(syn::Error::new(
195-
*span,
196-
"#[name] can not be specified multiple times",
197-
)),
200+
None => Ok(None),
198201
}
199202
}
200203

pyo3-derive-backend/src/pymethod.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -512,10 +512,11 @@ fn impl_arg_param(
512512
};
513513
if let syn::Type::Reference(tref) = ty {
514514
let (tref, mut_) = tref_preprocess(tref);
515-
let as_deref = if mut_.is_some() {
516-
quote! { as_deref_mut }
515+
// To support Rustc 1.39.0, we don't use as_deref here...
516+
let tmp_as_deref = if mut_.is_some() {
517+
quote! { _tmp.as_mut().map(std::ops::DerefMut::deref_mut) }
517518
} else {
518-
quote! { as_deref }
519+
quote! { _tmp.as_ref().map(std::ops::Deref::deref) }
519520
};
520521
// Get Option<&T> from Option<PyRef<T>>
521522
quote! {
@@ -525,7 +526,7 @@ fn impl_arg_param(
525526
},
526527
None => #default,
527528
};
528-
let #arg_name = _tmp.#as_deref();
529+
let #arg_name = #tmp_as_deref;
529530
}
530531
} else {
531532
quote! {
@@ -731,8 +732,9 @@ pub(crate) fn impl_py_getter_def(
731732

732733
/// Split an argument of pyo3::Python from the front of the arg list, if present
733734
fn split_off_python_arg<'a>(args: &'a [FnArg<'a>]) -> (Option<&FnArg>, &[FnArg]) {
734-
match args {
735-
[py, rest @ ..] if utils::if_type_is_python(&py.ty) => (Some(py), rest),
736-
rest => (None, rest),
735+
if args.get(0).map(|py| utils::if_type_is_python(&py.ty)) == Some(true) {
736+
(Some(&args[0]), &args[1..])
737+
} else {
738+
(None, args)
737739
}
738740
}

src/conversion.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub trait ToBorrowedObject: ToPyObject {
9898
F: FnOnce(*mut ffi::PyObject) -> R;
9999
}
100100

101+
#[cfg(feature = "nightly")]
101102
impl<T> ToBorrowedObject for T
102103
where
103104
T: ToPyObject,
@@ -115,6 +116,25 @@ where
115116
}
116117
}
117118

119+
#[cfg(not(feature = "nightly"))]
120+
impl<T> ToBorrowedObject for T
121+
where
122+
T: ToPyObject,
123+
{
124+
fn with_borrowed_ptr<F, R>(&self, py: Python, f: F) -> R
125+
where
126+
F: FnOnce(*mut ffi::PyObject) -> R,
127+
{
128+
let ptr = self.to_object(py).into_ptr();
129+
let result = f(ptr);
130+
unsafe {
131+
ffi::Py_XDECREF(ptr);
132+
}
133+
result
134+
}
135+
}
136+
137+
#[cfg(feature = "nightly")]
118138
impl<T> ToBorrowedObject for T
119139
where
120140
T: ToPyObject + AsPyPointer,

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(specialization)]
1+
#![cfg_attr(feature = "nightly", feature(specialization))]
22
#![allow(clippy::missing_safety_doc)] // FIXME (#698)
33

44
//! Rust bindings to the Python interpreter.

0 commit comments

Comments
 (0)