Skip to content

Commit

Permalink
Restore compatibility with Rust 1.41(.1).
Browse files Browse the repository at this point in the history
This version is currently supported by Debian stable and Alpine Linux.

Fixes PyO3#1420
  • Loading branch information
birkenfeld committed Feb 10, 2021
1 parent 479d67a commit 30e5f89
Show file tree
Hide file tree
Showing 14 changed files with 68 additions and 56 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ edition = "2018"
[dependencies]
cfg-if = { version = "1.0" }
ctor = { version = "0.1", optional = true }
indoc = { version = "1.0.3", optional = true }
# must stay at 0.3.x for Rust 1.41 compatibility
indoc = { version = "0.3.6", optional = true }
inventory = { version = "0.1.4", optional = true }
libc = "0.2.62"
parking_lot = "0.11.0"
num-bigint = { version = "0.3", optional = true }
num-complex = { version = "0.3", optional = true }
paste = { version = "1.0.3", optional = true }
# must stay at 0.1.x for Rust 1.41 compatibility
paste = { version = "0.1.18", optional = true }
pyo3-macros = { path = "pyo3-macros", version = "=0.13.1", optional = true }
unindent = { version = "0.1.4", optional = true }
hashbrown = { version = "0.9", optional = true }
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,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.45](https://img.shields.io/badge/rustc-1.45+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
[![minimum rustc 1.41](https://img.shields.io/badge/rustc-1.41+-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.
Expand All @@ -18,7 +18,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste

## Usage

PyO3 supports Python 3.6 and up. The minimum required Rust version is 1.45.0.
PyO3 supports Python 3.6 and up. The minimum required Rust version is 1.41.1.

Building with PyPy is also possible (via cpyext) for Python 3.6, targeted PyPy version is 7.3+.
Please refer to the [pypy section in the guide](https://pyo3.rs/master/pypy.html).
Expand Down
10 changes: 6 additions & 4 deletions pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,12 @@ impl<'a> FnSpec<'a> {

// strip get_ or set_
let strip_fn_name = |prefix: &'static str| {
name.unraw()
.to_string()
.strip_prefix(prefix)
.map(|rest| syn::Ident::new(rest, name.span()))
let ident = name.unraw().to_string();
if ident.starts_with(prefix) {
Some(syn::Ident::new(&ident[prefix.len()..], ident.span()))
} else {
None
}
};

// Parse receiver & function type for various method types
Expand Down
4 changes: 2 additions & 2 deletions pyo3-macros-backend/src/pyfunction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ pub fn parse_name_attribute(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Opti
Ok(Some(ident))
}
[(_, span)] => bail_spanned!(*span => "expected string literal for #[name] argument"),
[_first_attr, second_attr, ..] => bail_spanned!(
second_attr.1 => "#[name] can not be specified multiple times"
slice => bail_spanned!(
slice[1].1 => "#[name] can not be specified multiple times"
),
}
}
Expand Down
11 changes: 8 additions & 3 deletions pyo3-macros-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,8 +703,13 @@ 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, args @ ..] if utils::is_python(&py.ty) => (Some(py), args),
args => (None, args),
if args
.get(0)
.map(|py| utils::is_python(&py.ty))
.unwrap_or(false)
{
(Some(&args[0]), &args[1..])
} else {
(None, args)
}
}
2 changes: 2 additions & 0 deletions pyo3-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//! This crate declares only the proc macro attributes, as a crate defining proc macro attributes
//! must not contain any other public items.

extern crate proc_macro;

use proc_macro::TokenStream;
use pyo3_macros_backend::{
build_derive_from_pyobject, build_py_class, build_py_function, build_py_methods,
Expand Down
2 changes: 1 addition & 1 deletion src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl ElementType {
pub fn from_format(format: &CStr) -> ElementType {
match format.to_bytes() {
[char] | [b'@', char] => native_element_type_from_type_char(*char),
[modifier, char] if matches!(modifier, b'=' | b'<' | b'>' | b'!') => {
[modifier, char] if (*modifier == b'=' || *modifier == b'<' || *modifier == b'>' || *modifier == b'!') => {
standard_element_type_from_type_char(*char)
}
_ => ElementType::Unknown,
Expand Down
7 changes: 2 additions & 5 deletions src/err/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,11 +594,8 @@ mod tests {
assert!(debug_str.starts_with("PyErr { "));
assert!(debug_str.ends_with(" }"));

let mut fields = debug_str
.strip_prefix("PyErr { ")
.unwrap()
.strip_suffix(" }")
.unwrap()
// strip "PyErr { " and " }"
let mut fields = debug_str["PyErr { ".len()..debug_str.len()-2]
.split(", ");

assert_eq!(fields.next().unwrap(), "type: <class 'Exception'>");
Expand Down
7 changes: 4 additions & 3 deletions src/freelist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ where
let free = get_type_free(ty).unwrap_or_else(|| tp_free_fallback(ty));
free(obj as *mut c_void);

#[cfg(Py_3_8)]
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
ffi::Py_DECREF(ty as *mut ffi::PyObject);
if cfg!(Py_3_8) {
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
ffi::Py_DECREF(ty as *mut ffi::PyObject);
}
}
}
}
Expand Down
14 changes: 8 additions & 6 deletions src/gil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ pub fn prepare_freethreaded_python() {

// Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t
// have to call it yourself anymore.
#[cfg(not(Py_3_7))]
if ffi::PyEval_ThreadsInitialized() == 0 {
ffi::PyEval_InitThreads();
if cfg!(not(Py_3_7)) {
if ffi::PyEval_ThreadsInitialized() == 0 {
ffi::PyEval_InitThreads();
}
}

// Release the GIL.
Expand Down Expand Up @@ -150,9 +151,10 @@ where

// Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t have to
// call it yourself anymore.
#[cfg(not(Py_3_7))]
if ffi::PyEval_ThreadsInitialized() == 0 {
ffi::PyEval_InitThreads();
if cfg!(not(Py_3_7)) {
if ffi::PyEval_ThreadsInitialized() == 0 {
ffi::PyEval_InitThreads();
}
}

// Safe: the GIL is already held because of the Py_IntializeEx call.
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ pub mod proc_macro {
#[macro_export]
macro_rules! wrap_pyfunction {
($function_name: ident) => {{
&pyo3::paste::paste! { [<__pyo3_get_function_ $function_name>] }
&pyo3::paste::expr! { [<__pyo3_get_function_ $function_name>] }
}};

($function_name: ident, $arg: expr) => {
Expand Down Expand Up @@ -257,7 +257,7 @@ macro_rules! wrap_pyfunction {
#[macro_export]
macro_rules! raw_pycfunction {
($function_name: ident) => {{
pyo3::paste::paste! { [<__pyo3_raw_ $function_name>] }
pyo3::paste::expr! { [<__pyo3_raw_ $function_name>] }
}};
}

Expand All @@ -267,7 +267,7 @@ macro_rules! raw_pycfunction {
#[macro_export]
macro_rules! wrap_pymodule {
($module_name:ident) => {{
pyo3::paste::paste! {
pyo3::paste::expr! {
&|py| unsafe { pyo3::PyObject::from_owned_ptr(py, [<PyInit_ $module_name>]()) }
}
}};
Expand Down
45 changes: 23 additions & 22 deletions src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ pub trait PyClassAlloc: PyTypeInfo + Sized {
let free = get_type_free(ty).unwrap_or_else(|| tp_free_fallback(ty));
free(obj as *mut c_void);

#[cfg(Py_3_8)]
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
ffi::Py_DECREF(ty as *mut ffi::PyObject);
if cfg!(Py_3_8) {
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
ffi::Py_DECREF(ty as *mut ffi::PyObject);
}
}
}
}
Expand Down Expand Up @@ -194,8 +195,7 @@ where
slots.maybe_push(ffi::Py_tp_new, new.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_call, call.map(|v| v as _));

#[cfg(Py_3_9)]
{
if cfg!(Py_3_9) {
let members = py_class_members::<T>();
if !members.is_empty() {
slots.push(ffi::Py_tp_members, into_raw(members))
Expand Down Expand Up @@ -265,18 +265,18 @@ fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {

// Setting buffer protocols via slots doesn't work until Python 3.9, so on older versions we
// must manually fixup the type object.
#[cfg(not(Py_3_9))]
if let Some(buffer) = T::get_buffer() {
unsafe {
(*(*type_object).tp_as_buffer).bf_getbuffer = buffer.bf_getbuffer;
(*(*type_object).tp_as_buffer).bf_releasebuffer = buffer.bf_releasebuffer;
if cfg!(not(Py_3_9)) {
if let Some(buffer) = T::get_buffer() {
unsafe {
(*(*type_object).tp_as_buffer).bf_getbuffer = buffer.bf_getbuffer;
(*(*type_object).tp_as_buffer).bf_releasebuffer = buffer.bf_releasebuffer;
}
}
}

// Setting tp_dictoffset and tp_weaklistoffset via slots doesn't work until Python 3.9, so on
// older versions again we must fixup the type object.
#[cfg(not(Py_3_9))]
{
if cfg!(not(Py_3_9)) {
// __dict__ support
if let Some(dict_offset) = PyCell::<T>::dict_offset() {
unsafe {
Expand Down Expand Up @@ -368,7 +368,7 @@ fn py_class_method_defs<T: PyMethods>() -> (
/// tp_weaklistoffset.
///
/// Only works on Python 3.9 and up.
#[cfg(Py_3_9)]
// #[cfg(Py_3_9)] -- we need it for code currently using if cfg!(Py_3_9)
fn py_class_members<T: PyClass>() -> Vec<ffi::structmember::PyMemberDef> {
#[inline(always)]
fn offset_def(name: &'static str, offset: usize) -> ffi::structmember::PyMemberDef {
Expand Down Expand Up @@ -429,15 +429,16 @@ fn py_class_properties<T: PyClass>() -> Vec<ffi::PyGetSetDef> {

// PyPy doesn't automatically adds __dict__ getter / setter.
// PyObject_GenericGetDict not in the limited API until Python 3.10.
#[cfg(not(any(PyPy, all(Py_LIMITED_API, not(Py_3_10)))))]
if !T::Dict::IS_DUMMY {
props.push(ffi::PyGetSetDef {
name: "__dict__\0".as_ptr() as *mut c_char,
get: Some(ffi::PyObject_GenericGetDict),
set: Some(ffi::PyObject_GenericSetDict),
doc: ptr::null_mut(),
closure: ptr::null_mut(),
});
if cfg!(not(any(PyPy, all(Py_LIMITED_API, not(Py_3_10))))) {
if !T::Dict::IS_DUMMY {
props.push(ffi::PyGetSetDef {
name: "__dict__\0".as_ptr() as *mut c_char,
get: Some(ffi::PyObject_GenericGetDict),
set: Some(ffi::PyObject_GenericSetDict),
doc: ptr::null_mut(),
closure: ptr::null_mut(),
});
}
}
if !props.is_empty() {
props.push(unsafe { std::mem::zeroed() });
Expand Down
4 changes: 2 additions & 2 deletions tests/test_datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ macro_rules! assert_check_exact {
unsafe {
use pyo3::{AsPyPointer, ffi::*};
assert!($check_func(($obj).as_ptr()) != 0);
assert!(pyo3::paste::paste!([<$check_func Exact>])(($obj).as_ptr()) != 0);
assert!(pyo3::paste::expr!([<$check_func Exact>])(($obj).as_ptr()) != 0);
}
};
}
Expand All @@ -48,7 +48,7 @@ macro_rules! assert_check_only {
unsafe {
use pyo3::{AsPyPointer, ffi::*};
assert!($check_func(($obj).as_ptr()) != 0);
assert!(pyo3::paste::paste!([<$check_func Exact>])(($obj).as_ptr()) == 0);
assert!(pyo3::paste::expr!([<$check_func Exact>])(($obj).as_ptr()) == 0);
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/static_ref.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ error[E0597]: `pool` does not live long enough
| `pool` dropped here while still borrowed
| cast requires that `pool` is borrowed for `'static`
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

0 comments on commit 30e5f89

Please sign in to comment.