Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relax extract_sequence to the status quo before 0.17 was released #2631

Merged
merged 4 commits into from Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Expand Up @@ -185,12 +185,14 @@ jobs:
PROJECTS=("." "examples/decorator" "examples/maturin-starter" "examples/setuptools-rust-starter" "examples/word-count")
for PROJ in ${PROJECTS[@]}; do
cargo update --manifest-path "$PROJ/Cargo.toml" -p parking_lot --precise 0.11.0
cargo update --manifest-path "$PROJ/Cargo.toml" -p once_cell --precise 1.14.0
done
cargo update -p plotters --precise 0.3.1
cargo update -p plotters-svg --precise 0.3.1
cargo update -p plotters-backend --precise 0.3.2
cargo update -p bumpalo --precise 3.10.0
cargo update -p itertools --precise 0.10.3
cargo update -p once_cell --precise 1.14.0

- name: Build docs
run: cargo doc --no-deps --no-default-features --features "full ${{ matrix.extra_features }}"
Expand Down
1 change: 1 addition & 0 deletions newsfragments/2631.fixed.md
@@ -0,0 +1 @@
`impl FromPyObject for Vec<T>` will accept anything passing `PySequence_Check`, e.g. NumPy arrays, as it was before [#2477](https://github.com/PyO3/pyo3/pull/2477). Note that this does not change what `impl PyTryFrom for PySequence` does accept.
3 changes: 3 additions & 0 deletions pytests/noxfile.py
@@ -1,11 +1,14 @@
import nox
import platform

nox.options.sessions = ["test"]


@nox.session
def test(session):
session.install("-rrequirements-dev.txt")
if platform.system() == "Linux" and platform.python_implementation() == "CPython":
session.install("numpy>=1.16")
session.install("maturin")
session.run_always("maturin", "develop")
session.run("pytest")
Expand Down
3 changes: 3 additions & 0 deletions pytests/src/lib.rs
Expand Up @@ -11,6 +11,7 @@ pub mod othermod;
pub mod path;
pub mod pyclasses;
pub mod pyfunctions;
pub mod sequence;
pub mod subclassing;

#[pymodule]
Expand All @@ -26,6 +27,7 @@ fn pyo3_pytests(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pymodule!(path::path))?;
m.add_wrapped(wrap_pymodule!(pyclasses::pyclasses))?;
m.add_wrapped(wrap_pymodule!(pyfunctions::pyfunctions))?;
m.add_wrapped(wrap_pymodule!(sequence::sequence))?;
m.add_wrapped(wrap_pymodule!(subclassing::subclassing))?;

// Inserting to sys.modules allows importing submodules nicely from Python
Expand All @@ -42,6 +44,7 @@ fn pyo3_pytests(py: Python<'_>, m: &PyModule) -> PyResult<()> {
sys_modules.set_item("pyo3_pytests.path", m.getattr("path")?)?;
sys_modules.set_item("pyo3_pytests.pyclasses", m.getattr("pyclasses")?)?;
sys_modules.set_item("pyo3_pytests.pyfunctions", m.getattr("pyfunctions")?)?;
sys_modules.set_item("pyo3_pytests.sequence", m.getattr("sequence")?)?;
sys_modules.set_item("pyo3_pytests.subclassing", m.getattr("subclassing")?)?;

Ok(())
Expand Down
19 changes: 19 additions & 0 deletions pytests/src/sequence.rs
@@ -0,0 +1,19 @@
use pyo3::prelude::*;
use pyo3::types::PyString;

#[pyfunction]
fn vec_to_vec_i32(vec: Vec<i32>) -> Vec<i32> {
vec
}

#[pyfunction]
fn vec_to_vec_pystring(vec: Vec<&PyString>) -> Vec<&PyString> {
vec
}

#[pymodule]
pub fn sequence(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(vec_to_vec_i32, m)?)?;
m.add_function(wrap_pyfunction!(vec_to_vec_pystring, m)?)?;
Ok(())
}
31 changes: 31 additions & 0 deletions pytests/tests/test_sequence.py
@@ -0,0 +1,31 @@
import pytest
import platform

from pyo3_pytests import sequence


def test_vec_from_list_i32():
assert sequence.vec_to_vec_i32([1, 2, 3]) == [1, 2, 3]


def test_vec_from_list_pystring():
assert sequence.vec_to_vec_pystring(["1", "2", "3"]) == ["1", "2", "3"]


def test_vec_from_bytes():
assert sequence.vec_to_vec_i32(b"123") == [49, 50, 51]


def test_vec_from_str():
with pytest.raises(ValueError):
sequence.vec_to_vec_pystring("123")


@pytest.mark.skipif(
platform.system() != "Linux" or platform.python_implementation() != "CPython",
reason="Binary NumPy wheels are not available for all platforms and Python implementations",
)
def test_vec_from_array():
import numpy

assert sequence.vec_to_vec_i32(numpy.array([1, 2, 3])) == [1, 2, 3]
11 changes: 10 additions & 1 deletion src/types/sequence.rs
Expand Up @@ -299,7 +299,16 @@ fn extract_sequence<'s, T>(obj: &'s PyAny) -> PyResult<Vec<T>>
where
T: FromPyObject<'s>,
{
let seq = <PySequence as PyTryFrom>::try_from(obj)?;
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
// to support this function and if not, we will only fail extraction safely.
let seq = unsafe {
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
<PySequence as PyTryFrom>::try_from_unchecked(obj)
} else {
return Err(PyDowncastError::new(obj, "Sequence").into());
}
};

let mut v = Vec::with_capacity(seq.len().unwrap_or(0) as usize);
for item in seq.iter()? {
v.push(item?.extract::<T>()?);
Expand Down