Skip to content

Commit

Permalink
Merge branch 'main' into experimental-async
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Mar 5, 2024
2 parents 37b6412 + b08ee4b commit 7aa18fa
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 32 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ nightly = []
# This is mostly intended for testing purposes - activating *all* of these isn't particularly useful.
full = [
"macros",
"gil-refs",
# "multiple-pymethods", # TODO re-add this when MSRV is greater than 1.62
"anyhow",
"chrono",
Expand Down Expand Up @@ -144,7 +143,7 @@ members = [

[package.metadata.docs.rs]
no-default-features = true
features = ["full"]
features = ["full", "gil-refs"]
rustdoc-args = ["--cfg", "docsrs"]

[workspace.lints.clippy]
Expand Down
18 changes: 13 additions & 5 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def test_rust(session: nox.Session):
_run_cargo_test(session, features="abi3")
if "skip-full" not in session.posargs:
_run_cargo_test(session, features="full")
_run_cargo_test(session, features="full gil-refs")
_run_cargo_test(session, features="abi3 full")


Expand Down Expand Up @@ -617,6 +618,7 @@ def check_feature_powerset(session: nox.Session):

EXCLUDED_FROM_FULL = {
"nightly",
"gil-refs",
"extension-module",
"full",
"default",
Expand Down Expand Up @@ -658,10 +660,15 @@ def check_feature_powerset(session: nox.Session):
session.error("no experimental features exist; please simplify the noxfile")

features_to_skip = [
*EXCLUDED_FROM_FULL,
*(EXCLUDED_FROM_FULL - {"gil-refs"}),
*abi3_version_features,
]

# deny warnings
env = os.environ.copy()
rust_flags = env.get("RUSTFLAGS", "")
env["RUSTFLAGS"] = f"{rust_flags} -Dwarnings"

comma_join = ",".join
_run_cargo(
session,
Expand All @@ -672,6 +679,7 @@ def check_feature_powerset(session: nox.Session):
*(f"--group-features={comma_join(group)}" for group in features_to_group),
"check",
"--all-targets",
env=env,
)


Expand Down Expand Up @@ -715,8 +723,8 @@ def _get_feature_sets() -> Tuple[Tuple[str, ...], ...]:
"--no-default-features",
"--features=abi3",
),
("--features=full multiple-pymethods",),
("--features=abi3 full multiple-pymethods",),
("--features=full gil-refs multiple-pymethods",),
("--features=abi3 full gil-refs multiple-pymethods",),
)
else:
return (
Expand All @@ -725,8 +733,8 @@ def _get_feature_sets() -> Tuple[Tuple[str, ...], ...]:
"--no-default-features",
"--features=abi3",
),
("--features=full",),
("--features=abi3 full",),
("--features=full gil-refs",),
("--features=abi3 full gil-refs",),
)


Expand Down
2 changes: 1 addition & 1 deletion pyo3-benches/benches/bench_dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn iter_dict(b: &mut Bencher<'_>) {
let dict = (0..LEN as u64).map(|i| (i, i * 2)).into_py_dict_bound(py);
let mut sum = 0;
b.iter(|| {
for (k, _v) in dict.iter() {
for (k, _v) in &dict {
let i: u64 = k.extract().unwrap();
sum += i;
}
Expand Down
2 changes: 1 addition & 1 deletion pyo3-benches/benches/bench_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn iter_list(b: &mut Bencher<'_>) {
let list = PyList::new_bound(py, 0..LEN);
let mut sum = 0;
b.iter(|| {
for x in list.iter() {
for x in &list {
let i: u64 = x.extract().unwrap();
sum += i;
}
Expand Down
2 changes: 1 addition & 1 deletion pyo3-benches/benches/bench_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn iter_set(b: &mut Bencher<'_>) {
let set = PySet::new_bound(py, &(0..LEN).collect::<Vec<_>>()).unwrap();
let mut sum = 0;
b.iter(|| {
for x in set.iter() {
for x in &set {
let i: u64 = x.extract().unwrap();
sum += i;
}
Expand Down
4 changes: 2 additions & 2 deletions src/conversions/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -980,13 +980,13 @@ mod tests {
let td = new_py_datetime_ob(py, "timedelta", (0, 3600, 0));
let py_timedelta = new_py_datetime_ob(py, "timezone", (td,));
// Should be equal
assert!(offset.as_ref(py).eq(py_timedelta).unwrap());
assert!(offset.bind(py).eq(py_timedelta).unwrap());

// Same but with negative values
let offset = FixedOffset::east_opt(-3600).unwrap().to_object(py);
let td = new_py_datetime_ob(py, "timedelta", (0, -3600, 0));
let py_timedelta = new_py_datetime_ob(py, "timezone", (td,));
assert!(offset.as_ref(py).eq(py_timedelta).unwrap());
assert!(offset.bind(py).eq(py_timedelta).unwrap());
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/conversions/hashbrown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ where
fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
let dict = ob.downcast::<PyDict>()?;
let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default());
for (k, v) in dict.iter() {
for (k, v) in dict {
ret.insert(k.extract()?, v.extract()?);
}
Ok(ret)
Expand Down
2 changes: 1 addition & 1 deletion src/conversions/indexmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ where
fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
let dict = ob.downcast::<PyDict>()?;
let mut ret = indexmap::IndexMap::with_capacity_and_hasher(dict.len(), S::default());
for (k, v) in dict.iter() {
for (k, v) in dict {
ret.insert(k.extract()?, v.extract()?);
}
Ok(ret)
Expand Down
8 changes: 4 additions & 4 deletions src/conversions/num_bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ mod tests {
let mut f0 = 1.to_object(py);
let mut f1 = 1.to_object(py);
std::iter::from_fn(move || {
let f2 = f0.call_method1(py, "__add__", (f1.as_ref(py),)).unwrap();
let f2 = f0.call_method1(py, "__add__", (f1.bind(py),)).unwrap();
Some(std::mem::replace(&mut f0, std::mem::replace(&mut f1, f2)))
})
}
Expand All @@ -295,7 +295,7 @@ mod tests {
// Python -> Rust
assert_eq!(py_result.extract::<BigUint>(py).unwrap(), rs_result);
// Rust -> Python
assert!(py_result.as_ref(py).eq(rs_result).unwrap());
assert!(py_result.bind(py).eq(rs_result).unwrap());
}
});
}
Expand All @@ -308,7 +308,7 @@ mod tests {
// Python -> Rust
assert_eq!(py_result.extract::<BigInt>(py).unwrap(), rs_result);
// Rust -> Python
assert!(py_result.as_ref(py).eq(&rs_result).unwrap());
assert!(py_result.bind(py).eq(&rs_result).unwrap());

// negate

Expand All @@ -318,7 +318,7 @@ mod tests {
// Python -> Rust
assert_eq!(py_result.extract::<BigInt>(py).unwrap(), rs_result);
// Rust -> Python
assert!(py_result.as_ref(py).eq(rs_result).unwrap());
assert!(py_result.bind(py).eq(rs_result).unwrap());
}
});
}
Expand Down
14 changes: 3 additions & 11 deletions src/conversions/rust_decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ use crate::sync::GILOnceCell;
use crate::types::any::PyAnyMethods;
use crate::types::string::PyStringMethods;
use crate::types::PyType;
use crate::{
intern, Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
};
use crate::{Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
use rust_decimal::Decimal;
use std::str::FromStr;

Expand All @@ -74,14 +72,8 @@ impl FromPyObject<'_> for Decimal {

static DECIMAL_CLS: GILOnceCell<Py<PyType>> = GILOnceCell::new();

fn get_decimal_cls(py: Python<'_>) -> PyResult<&PyType> {
DECIMAL_CLS
.get_or_try_init(py, || {
py.import_bound(intern!(py, "decimal"))?
.getattr(intern!(py, "Decimal"))?
.extract()
})
.map(|ty| ty.as_ref(py))
fn get_decimal_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> {
DECIMAL_CLS.get_or_try_init_type_ref(py, "decimal", "Decimal")
}

impl ToPyObject for Decimal {
Expand Down
4 changes: 2 additions & 2 deletions src/conversions/std/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ where
fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
let dict = ob.downcast::<PyDict>()?;
let mut ret = collections::HashMap::with_capacity_and_hasher(dict.len(), S::default());
for (k, v) in dict.iter() {
for (k, v) in dict {
ret.insert(k.extract()?, v.extract()?);
}
Ok(ret)
Expand All @@ -96,7 +96,7 @@ where
fn extract_bound(ob: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
let dict = ob.downcast::<PyDict>()?;
let mut ret = collections::BTreeMap::new();
for (k, v) in dict.iter() {
for (k, v) in dict {
ret.insert(k.extract()?, v.extract()?);
}
Ok(ret)
Expand Down
29 changes: 29 additions & 0 deletions src/types/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,15 @@ impl<'py> IntoIterator for Bound<'py, PyDict> {
}
}

impl<'py> IntoIterator for &Bound<'py, PyDict> {
type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
type IntoIter = BoundDictIterator<'py>;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

mod borrowed_iter {
use super::*;

Expand Down Expand Up @@ -1123,6 +1132,26 @@ mod tests {
});
}

#[test]
fn test_iter_bound() {
Python::with_gil(|py| {
let mut v = HashMap::new();
v.insert(7, 32);
v.insert(8, 42);
v.insert(9, 123);
let ob = v.to_object(py);
let dict: &Bound<'_, PyDict> = ob.downcast_bound(py).unwrap();
let mut key_sum = 0;
let mut value_sum = 0;
for (key, value) in dict {
key_sum += key.extract::<i32>().unwrap();
value_sum += value.extract::<i32>().unwrap();
}
assert_eq!(7 + 8 + 9, key_sum);
assert_eq!(32 + 42 + 123, value_sum);
});
}

#[test]
fn test_iter_value_mutated() {
Python::with_gil(|py| {
Expand Down
21 changes: 21 additions & 0 deletions src/types/frozenset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,16 @@ impl<'py> IntoIterator for Bound<'py, PyFrozenSet> {
}
}

impl<'py> IntoIterator for &Bound<'py, PyFrozenSet> {
type Item = Bound<'py, PyAny>;
type IntoIter = BoundFrozenSetIterator<'py>;

/// Returns an iterator of values in this set.
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

/// PyO3 implementation of an iterator for a Python `frozenset` object.
pub struct BoundFrozenSetIterator<'p> {
it: Bound<'p, PyIterator>,
Expand Down Expand Up @@ -357,6 +367,17 @@ mod tests {
});
}

#[test]
fn test_frozenset_iter_bound() {
Python::with_gil(|py| {
let set = PyFrozenSet::new_bound(py, &[1]).unwrap();

for el in &set {
assert_eq!(1i32, el.extract::<i32>().unwrap());
}
});
}

#[test]
fn test_frozenset_iter_size_hint() {
Python::with_gil(|py| {
Expand Down
42 changes: 42 additions & 0 deletions src/types/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ impl<'py> Borrowed<'_, 'py, PyIterator> {
}
}

impl<'py> IntoIterator for &Bound<'py, PyIterator> {
type Item = PyResult<Bound<'py, PyAny>>;
type IntoIter = Bound<'py, PyIterator>;

fn into_iter(self) -> Self::IntoIter {
self.clone()
}
}

impl PyTypeCheck for PyIterator {
const NAME: &'static str = "Iterator";

Expand Down Expand Up @@ -246,6 +255,39 @@ def fibonacci(target):
});
}

#[test]
fn fibonacci_generator_bound() {
use crate::types::any::PyAnyMethods;
use crate::Bound;

let fibonacci_generator = r#"
def fibonacci(target):
a = 1
b = 1
for _ in range(target):
yield a
a, b = b, a + b
"#;

Python::with_gil(|py| {
let context = PyDict::new_bound(py);
py.run_bound(fibonacci_generator, None, Some(&context))
.unwrap();

let generator: Bound<'_, PyIterator> = py
.eval_bound("fibonacci(5)", None, Some(&context))
.unwrap()
.downcast_into()
.unwrap();
let mut items = vec![];
for actual in &generator {
let actual = actual.unwrap().extract::<usize>().unwrap();
items.push(actual);
}
assert_eq!(items, [1, 1, 2, 3, 5]);
});
}

#[test]
fn int_not_iterable() {
Python::with_gil(|py| {
Expand Down
23 changes: 23 additions & 0 deletions src/types/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,15 @@ impl<'py> IntoIterator for Bound<'py, PyList> {
}
}

impl<'py> IntoIterator for &Bound<'py, PyList> {
type Item = Bound<'py, PyAny>;
type IntoIter = BoundListIterator<'py>;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

#[cfg(test)]
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
mod tests {
Expand Down Expand Up @@ -911,6 +920,20 @@ mod tests {
});
}

#[test]
fn test_into_iter_bound() {
use crate::types::any::PyAnyMethods;

Python::with_gil(|py| {
let list = PyList::new_bound(py, [1, 2, 3, 4]);
let mut items = vec![];
for item in &list {
items.push(item.extract::<i32>().unwrap());
}
assert_eq!(items, vec![1, 2, 3, 4]);
});
}

#[test]
fn test_extract() {
Python::with_gil(|py| {
Expand Down
Loading

0 comments on commit 7aa18fa

Please sign in to comment.