Merge pull request #2490 from CLOVIS-AI/type-info
Runtime type information for objects crossing the Rust–Python boundary
This commit is contained in:
commit
ec58ad2f6f
|
@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added `type_input()` and `type_output()` to get the Python type of any Python-compatible object. [#2490](https://github.com/PyO3/pyo3/pull/2490)
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove the deprecated `pyproto` feature, `#[pyproto]` macro, and all accompanying APIs. [#2587](https://github.com/PyO3/pyo3/pull/2587)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
//! Defines conversions between Rust and Python types.
|
||||
use crate::err::{self, PyDowncastError, PyResult};
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::pyclass::boolean_struct::False;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
use crate::types::PyTuple;
|
||||
|
@ -244,6 +245,17 @@ impl<T> ToBorrowedObject for T where T: ToPyObject {}
|
|||
pub trait IntoPy<T>: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_py(self, py: Python<'_>) -> T;
|
||||
|
||||
/// Extracts the type hint information for this type when it appears as a return value.
|
||||
///
|
||||
/// For example, `Vec<u32>` would return `List[int]`.
|
||||
/// The default implementation returns `Any`, which is correct for any type.
|
||||
///
|
||||
/// For most types, the return value for this method will be identical to that of [`FromPyObject::type_input`].
|
||||
/// It may be different for some types, such as `Dict`, to allow duck-typing: functions return `Dict` but take `Mapping` as argument.
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::Any
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a type from a Python object.
|
||||
|
@ -289,6 +301,17 @@ pub trait IntoPy<T>: Sized {
|
|||
pub trait FromPyObject<'source>: Sized {
|
||||
/// Extracts `Self` from the source `PyObject`.
|
||||
fn extract(ob: &'source PyAny) -> PyResult<Self>;
|
||||
|
||||
/// Extracts the type hint information for this type when it appears as an argument.
|
||||
///
|
||||
/// For example, `Vec<u32>` would return `Sequence[int]`.
|
||||
/// The default implementation returns `Any`, which is correct for any type.
|
||||
///
|
||||
/// For most types, the return value for this method will be identical to that of [`IntoPy::type_output`].
|
||||
/// It may be different for some types, such as `Dict`, to allow duck-typing: functions return `Dict` but take `Mapping` as argument.
|
||||
fn type_input() -> TypeInfo {
|
||||
TypeInfo::Any
|
||||
}
|
||||
}
|
||||
|
||||
/// Identity conversion: allows using existing `PyObject` instances where
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
/// Runtime inspection of objects exposed to Python.
|
||||
///
|
||||
/// Tracking issue: <https://github.com/PyO3/pyo3/issues/2454>.
|
||||
pub mod types;
|
|
@ -0,0 +1,481 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// Designation of a Python type.
|
||||
///
|
||||
/// This enum is used to handle advanced types, such as types with generics.
|
||||
/// Its [`Display`] implementation can be used to convert to the type hint notation (e.g. `List[int]`).
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum TypeInfo {
|
||||
/// The type `typing.Any`, which represents any possible value (unknown type).
|
||||
Any,
|
||||
/// The type `typing.None`.
|
||||
None,
|
||||
/// The type `typing.NoReturn`, which represents functions that never return (they can still panic / throw, similar to `never` in Rust).
|
||||
NoReturn,
|
||||
/// The type `typing.Callable`.
|
||||
///
|
||||
/// The first argument represents the parameters of the callable:
|
||||
/// - `Some` of a vector of types to represent the signature,
|
||||
/// - `None` if the signature is unknown (allows any number of arguments with type `Any`).
|
||||
///
|
||||
/// The second argument represents the return type.
|
||||
Callable(Option<Vec<TypeInfo>>, Box<TypeInfo>),
|
||||
/// The type `typing.tuple`.
|
||||
///
|
||||
/// The argument represents the contents of the tuple:
|
||||
/// - `Some` of a vector of types to represent the accepted types,
|
||||
/// - `Some` of an empty vector for the empty tuple,
|
||||
/// - `None` if the number and type of accepted values is unknown.
|
||||
///
|
||||
/// If the number of accepted values is unknown, but their type is, use [`Self::UnsizedTypedTuple`].
|
||||
Tuple(Option<Vec<TypeInfo>>),
|
||||
/// The type `typing.Tuple`.
|
||||
///
|
||||
/// Use this variant to represent a tuple of unknown size but of known types.
|
||||
///
|
||||
/// If the type is unknown, or if the number of elements is known, use [`Self::Tuple`].
|
||||
UnsizedTypedTuple(Box<TypeInfo>),
|
||||
/// A Python class.
|
||||
Class {
|
||||
/// The module this class comes from.
|
||||
module: ModuleName,
|
||||
/// The name of this class, as it appears in a type hint.
|
||||
name: Cow<'static, str>,
|
||||
/// The generics accepted by this class (empty vector if this class is not generic).
|
||||
type_vars: Vec<TypeInfo>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Declares which module a type is a part of.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum ModuleName {
|
||||
/// The type is built-in: it doesn't need to be imported.
|
||||
Builtin,
|
||||
/// The type is in the current module: it doesn't need to be imported in this module, but needs to be imported in others.
|
||||
CurrentModule,
|
||||
/// The type is in the specified module.
|
||||
Module(Cow<'static, str>),
|
||||
}
|
||||
|
||||
impl TypeInfo {
|
||||
/// Returns the module in which a type is declared.
|
||||
///
|
||||
/// Returns `None` if the type is declared in the current module.
|
||||
pub fn module_name(&self) -> Option<&str> {
|
||||
match self {
|
||||
TypeInfo::Any
|
||||
| TypeInfo::None
|
||||
| TypeInfo::NoReturn
|
||||
| TypeInfo::Callable(_, _)
|
||||
| TypeInfo::Tuple(_)
|
||||
| TypeInfo::UnsizedTypedTuple(_) => Some("typing"),
|
||||
TypeInfo::Class { module, .. } => match module {
|
||||
ModuleName::Builtin => Some("builtins"),
|
||||
ModuleName::CurrentModule => None,
|
||||
ModuleName::Module(name) => Some(name),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the name of a type.
|
||||
///
|
||||
/// The name of a type is the part of the hint that is not generic (e.g. `List` instead of `List[int]`).
|
||||
pub fn name(&self) -> Cow<'_, str> {
|
||||
Cow::from(match self {
|
||||
TypeInfo::Any => "Any",
|
||||
TypeInfo::None => "None",
|
||||
TypeInfo::NoReturn => "NoReturn",
|
||||
TypeInfo::Callable(_, _) => "Callable",
|
||||
TypeInfo::Tuple(_) => "Tuple",
|
||||
TypeInfo::UnsizedTypedTuple(_) => "Tuple",
|
||||
TypeInfo::Class { name, .. } => name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Utilities for easily instantiating TypeInfo structures for built-in/common types.
|
||||
impl TypeInfo {
|
||||
/// The Python `Optional` type.
|
||||
pub fn optional_of(t: TypeInfo) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("Optional"),
|
||||
type_vars: vec![t],
|
||||
}
|
||||
}
|
||||
|
||||
/// The Python `Union` type.
|
||||
pub fn union_of(types: &[TypeInfo]) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("Union"),
|
||||
type_vars: types.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The Python `List` type.
|
||||
pub fn list_of(t: TypeInfo) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("List"),
|
||||
type_vars: vec![t],
|
||||
}
|
||||
}
|
||||
|
||||
/// The Python `Sequence` type.
|
||||
pub fn sequence_of(t: TypeInfo) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("Sequence"),
|
||||
type_vars: vec![t],
|
||||
}
|
||||
}
|
||||
|
||||
/// The Python `Set` type.
|
||||
pub fn set_of(t: TypeInfo) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("Set"),
|
||||
type_vars: vec![t],
|
||||
}
|
||||
}
|
||||
|
||||
/// The Python `FrozenSet` type.
|
||||
pub fn frozen_set_of(t: TypeInfo) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("FrozenSet"),
|
||||
type_vars: vec![t],
|
||||
}
|
||||
}
|
||||
|
||||
/// The Python `Iterable` type.
|
||||
pub fn iterable_of(t: TypeInfo) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("Iterable"),
|
||||
type_vars: vec![t],
|
||||
}
|
||||
}
|
||||
|
||||
/// The Python `Iterator` type.
|
||||
pub fn iterator_of(t: TypeInfo) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("Iterator"),
|
||||
type_vars: vec![t],
|
||||
}
|
||||
}
|
||||
|
||||
/// The Python `Dict` type.
|
||||
pub fn dict_of(k: TypeInfo, v: TypeInfo) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("Dict"),
|
||||
type_vars: vec![k, v],
|
||||
}
|
||||
}
|
||||
|
||||
/// The Python `Mapping` type.
|
||||
pub fn mapping_of(k: TypeInfo, v: TypeInfo) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Module(Cow::from("typing")),
|
||||
name: Cow::from("Mapping"),
|
||||
type_vars: vec![k, v],
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience factory for non-generic builtins (e.g. `int`).
|
||||
pub fn builtin(name: &'static str) -> TypeInfo {
|
||||
TypeInfo::Class {
|
||||
module: ModuleName::Builtin,
|
||||
name: Cow::from(name),
|
||||
type_vars: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TypeInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TypeInfo::Any | TypeInfo::None | TypeInfo::NoReturn => write!(f, "{}", self.name()),
|
||||
TypeInfo::Callable(input, output) => {
|
||||
write!(f, "Callable[")?;
|
||||
|
||||
if let Some(input) = input {
|
||||
write!(f, "[")?;
|
||||
let mut comma = false;
|
||||
for arg in input {
|
||||
if comma {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", arg)?;
|
||||
comma = true;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
} else {
|
||||
write!(f, "...")?;
|
||||
}
|
||||
|
||||
write!(f, ", {}]", output)
|
||||
}
|
||||
TypeInfo::Tuple(types) => {
|
||||
write!(f, "Tuple[")?;
|
||||
|
||||
if let Some(types) = types {
|
||||
if types.is_empty() {
|
||||
write!(f, "()")?;
|
||||
} else {
|
||||
let mut comma = false;
|
||||
for t in types {
|
||||
if comma {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", t)?;
|
||||
comma = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
write!(f, "...")?;
|
||||
}
|
||||
|
||||
write!(f, "]")
|
||||
}
|
||||
TypeInfo::UnsizedTypedTuple(t) => write!(f, "Tuple[{}, ...]", t),
|
||||
TypeInfo::Class {
|
||||
name, type_vars, ..
|
||||
} => {
|
||||
write!(f, "{}", name)?;
|
||||
|
||||
if !type_vars.is_empty() {
|
||||
write!(f, "[")?;
|
||||
|
||||
let mut comma = false;
|
||||
for var in type_vars {
|
||||
if comma {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", var)?;
|
||||
comma = true;
|
||||
}
|
||||
|
||||
write!(f, "]")
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::inspect::types::{ModuleName, TypeInfo};
|
||||
|
||||
pub fn assert_display(t: &TypeInfo, expected: &str) {
|
||||
assert_eq!(format!("{}", t), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
assert_display(&TypeInfo::Any, "Any");
|
||||
assert_display(&TypeInfo::None, "None");
|
||||
assert_display(&TypeInfo::NoReturn, "NoReturn");
|
||||
|
||||
assert_display(&TypeInfo::builtin("int"), "int");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callable() {
|
||||
let any_to_int = TypeInfo::Callable(None, Box::new(TypeInfo::builtin("int")));
|
||||
assert_display(&any_to_int, "Callable[..., int]");
|
||||
|
||||
let sum = TypeInfo::Callable(
|
||||
Some(vec![TypeInfo::builtin("int"), TypeInfo::builtin("int")]),
|
||||
Box::new(TypeInfo::builtin("int")),
|
||||
);
|
||||
assert_display(&sum, "Callable[[int, int], int]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple() {
|
||||
let any = TypeInfo::Tuple(None);
|
||||
assert_display(&any, "Tuple[...]");
|
||||
|
||||
let triple = TypeInfo::Tuple(Some(vec![
|
||||
TypeInfo::builtin("int"),
|
||||
TypeInfo::builtin("str"),
|
||||
TypeInfo::builtin("bool"),
|
||||
]));
|
||||
assert_display(&triple, "Tuple[int, str, bool]");
|
||||
|
||||
let empty = TypeInfo::Tuple(Some(vec![]));
|
||||
assert_display(&empty, "Tuple[()]");
|
||||
|
||||
let typed = TypeInfo::UnsizedTypedTuple(Box::new(TypeInfo::builtin("bool")));
|
||||
assert_display(&typed, "Tuple[bool, ...]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn class() {
|
||||
let class1 = TypeInfo::Class {
|
||||
module: ModuleName::CurrentModule,
|
||||
name: Cow::from("MyClass"),
|
||||
type_vars: vec![],
|
||||
};
|
||||
assert_display(&class1, "MyClass");
|
||||
|
||||
let class2 = TypeInfo::Class {
|
||||
module: ModuleName::CurrentModule,
|
||||
name: Cow::from("MyClass"),
|
||||
type_vars: vec![TypeInfo::builtin("int"), TypeInfo::builtin("bool")],
|
||||
};
|
||||
assert_display(&class2, "MyClass[int, bool]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collections() {
|
||||
let int = TypeInfo::builtin("int");
|
||||
let bool = TypeInfo::builtin("bool");
|
||||
let str = TypeInfo::builtin("str");
|
||||
|
||||
let list = TypeInfo::list_of(int.clone());
|
||||
assert_display(&list, "List[int]");
|
||||
|
||||
let sequence = TypeInfo::sequence_of(bool.clone());
|
||||
assert_display(&sequence, "Sequence[bool]");
|
||||
|
||||
let optional = TypeInfo::optional_of(str.clone());
|
||||
assert_display(&optional, "Optional[str]");
|
||||
|
||||
let iterable = TypeInfo::iterable_of(int.clone());
|
||||
assert_display(&iterable, "Iterable[int]");
|
||||
|
||||
let iterator = TypeInfo::iterator_of(bool);
|
||||
assert_display(&iterator, "Iterator[bool]");
|
||||
|
||||
let dict = TypeInfo::dict_of(int.clone(), str.clone());
|
||||
assert_display(&dict, "Dict[int, str]");
|
||||
|
||||
let mapping = TypeInfo::mapping_of(int, str.clone());
|
||||
assert_display(&mapping, "Mapping[int, str]");
|
||||
|
||||
let set = TypeInfo::set_of(str.clone());
|
||||
assert_display(&set, "Set[str]");
|
||||
|
||||
let frozen_set = TypeInfo::frozen_set_of(str);
|
||||
assert_display(&frozen_set, "FrozenSet[str]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complicated() {
|
||||
let int = TypeInfo::builtin("int");
|
||||
assert_display(&int, "int");
|
||||
|
||||
let bool = TypeInfo::builtin("bool");
|
||||
assert_display(&bool, "bool");
|
||||
|
||||
let str = TypeInfo::builtin("str");
|
||||
assert_display(&str, "str");
|
||||
|
||||
let any = TypeInfo::Any;
|
||||
assert_display(&any, "Any");
|
||||
|
||||
let params = TypeInfo::union_of(&[int.clone(), str]);
|
||||
assert_display(¶ms, "Union[int, str]");
|
||||
|
||||
let func = TypeInfo::Callable(Some(vec![params, any]), Box::new(bool));
|
||||
assert_display(&func, "Callable[[Union[int, str], Any], bool]");
|
||||
|
||||
let dict = TypeInfo::mapping_of(int, func);
|
||||
assert_display(
|
||||
&dict,
|
||||
"Mapping[int, Callable[[Union[int, str], Any], bool]]",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod conversion {
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::inspect::types::test::assert_display;
|
||||
use crate::{FromPyObject, IntoPy};
|
||||
|
||||
#[test]
|
||||
fn unsigned_int() {
|
||||
assert_display(&usize::type_output(), "int");
|
||||
assert_display(&usize::type_input(), "int");
|
||||
|
||||
assert_display(&u8::type_output(), "int");
|
||||
assert_display(&u8::type_input(), "int");
|
||||
|
||||
assert_display(&u16::type_output(), "int");
|
||||
assert_display(&u16::type_input(), "int");
|
||||
|
||||
assert_display(&u32::type_output(), "int");
|
||||
assert_display(&u32::type_input(), "int");
|
||||
|
||||
assert_display(&u64::type_output(), "int");
|
||||
assert_display(&u64::type_input(), "int");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_int() {
|
||||
assert_display(&isize::type_output(), "int");
|
||||
assert_display(&isize::type_input(), "int");
|
||||
|
||||
assert_display(&i8::type_output(), "int");
|
||||
assert_display(&i8::type_input(), "int");
|
||||
|
||||
assert_display(&i16::type_output(), "int");
|
||||
assert_display(&i16::type_input(), "int");
|
||||
|
||||
assert_display(&i32::type_output(), "int");
|
||||
assert_display(&i32::type_input(), "int");
|
||||
|
||||
assert_display(&i64::type_output(), "int");
|
||||
assert_display(&i64::type_input(), "int");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float() {
|
||||
assert_display(&f32::type_output(), "float");
|
||||
assert_display(&f32::type_input(), "float");
|
||||
|
||||
assert_display(&f64::type_output(), "float");
|
||||
assert_display(&f64::type_input(), "float");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool() {
|
||||
assert_display(&bool::type_output(), "bool");
|
||||
assert_display(&bool::type_input(), "bool");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text() {
|
||||
assert_display(&String::type_output(), "str");
|
||||
assert_display(&String::type_input(), "str");
|
||||
|
||||
assert_display(&<&[u8]>::type_output(), "bytes");
|
||||
assert_display(&<&[u8]>::type_input(), "bytes");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collections() {
|
||||
assert_display(&<Vec<usize>>::type_output(), "List[int]");
|
||||
assert_display(&<Vec<usize>>::type_input(), "Sequence[int]");
|
||||
|
||||
assert_display(&<HashSet<usize>>::type_output(), "Set[int]");
|
||||
assert_display(&<HashSet<usize>>::type_input(), "Set[int]");
|
||||
|
||||
assert_display(&<HashMap<usize, f32>>::type_output(), "Dict[int, float]");
|
||||
assert_display(&<HashMap<usize, f32>>::type_input(), "Mapping[int, float]");
|
||||
|
||||
assert_display(&<(usize, f32)>::type_input(), "Tuple[int, float]");
|
||||
}
|
||||
}
|
|
@ -407,6 +407,8 @@ mod macros;
|
|||
#[cfg(all(test, feature = "macros"))]
|
||||
mod test_hygiene;
|
||||
|
||||
pub mod inspect;
|
||||
|
||||
/// Test readme and user guide
|
||||
#[cfg(doctest)]
|
||||
pub mod doc_test {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python,
|
||||
ToPyObject,
|
||||
|
@ -46,6 +47,10 @@ impl IntoPy<PyObject> for bool {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
PyBool::new(py, self).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::builtin("bool")
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Python `bool` to a Rust `bool`.
|
||||
|
@ -55,6 +60,10 @@ impl<'source> FromPyObject<'source> for bool {
|
|||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
Ok(<PyBool as PyTryFrom>::try_from(obj)?.is_true())
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
Self::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::inspect::types::TypeInfo;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, PyTryFrom, Python,
|
||||
ToPyObject,
|
||||
|
@ -128,13 +129,22 @@ impl<'a> IntoPy<PyObject> for &'a [u8] {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
PyBytes::new(py, self).to_object(py)
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::builtin("bytes")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromPyObject<'a> for &'a [u8] {
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
Ok(<PyBytes as PyTryFrom>::try_from(obj)?.as_bytes())
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
Self::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PyBytes;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use super::PyMapping;
|
||||
use crate::err::{self, PyErr, PyResult};
|
||||
use crate::ffi::Py_ssize_t;
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::types::{PyAny, PyList};
|
||||
#[cfg(not(PyPy))]
|
||||
use crate::IntoPyPointer;
|
||||
|
@ -382,6 +383,10 @@ where
|
|||
.map(|(k, v)| (k.into_py(py), v.into_py(py)));
|
||||
IntoPyDict::into_py_dict(iter, py).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::dict_of(K::type_output(), V::type_output())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> IntoPy<PyObject> for collections::BTreeMap<K, V>
|
||||
|
@ -395,6 +400,10 @@ where
|
|||
.map(|(k, v)| (k.into_py(py), v.into_py(py)));
|
||||
IntoPyDict::into_py_dict(iter, py).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::dict_of(K::type_output(), V::type_output())
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion trait that allows a sequence of tuples to be converted into `PyDict`
|
||||
|
@ -472,6 +481,10 @@ where
|
|||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
TypeInfo::mapping_of(K::type_input(), V::type_input())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source, K, V> FromPyObject<'source> for BTreeMap<K, V>
|
||||
|
@ -487,6 +500,10 @@ where
|
|||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
TypeInfo::mapping_of(K::type_input(), V::type_input())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
//
|
||||
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
|
@ -44,6 +45,10 @@ impl IntoPy<PyObject> for f64 {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
PyFloat::new(py, self).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::builtin("float")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for f64 {
|
||||
|
@ -60,6 +65,10 @@ impl<'source> FromPyObject<'source> for f64 {
|
|||
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
Self::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToPyObject for f32 {
|
||||
|
@ -72,12 +81,20 @@ impl IntoPy<PyObject> for f32 {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
PyFloat::new(py, f64::from(self)).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::builtin("float")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for f32 {
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
Ok(obj.extract::<f64>()? as f32)
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
Self::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::convert::TryInto;
|
|||
|
||||
use crate::err::{self, PyResult};
|
||||
use crate::ffi::{self, Py_ssize_t};
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::internal_tricks::get_ssize_index;
|
||||
use crate::types::PySequence;
|
||||
use crate::{
|
||||
|
@ -368,6 +369,10 @@ where
|
|||
let list = new_from_iter(py, &mut iter);
|
||||
list.into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::list_of(T::type_output())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//
|
||||
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
|
||||
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::{
|
||||
exceptions, ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python,
|
||||
ToPyObject,
|
||||
|
@ -22,6 +23,10 @@ macro_rules! int_fits_larger_int {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
(self as $larger_type).into_py(py)
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
<$larger_type>::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
|
@ -30,6 +35,10 @@ macro_rules! int_fits_larger_int {
|
|||
<$rust_type>::try_from(val)
|
||||
.map_err(|e| exceptions::PyOverflowError::new_err(e.to_string()))
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
<$larger_type>::type_input()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -56,6 +65,10 @@ macro_rules! int_fits_c_long {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(self as c_long)) }
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::builtin("int")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
|
@ -74,6 +87,10 @@ macro_rules! int_fits_c_long {
|
|||
<$rust_type>::try_from(val)
|
||||
.map_err(|e| exceptions::PyOverflowError::new_err(e.to_string()))
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
Self::type_output()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -91,6 +108,10 @@ macro_rules! int_convert_u64_or_i64 {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(self)) }
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::builtin("int")
|
||||
}
|
||||
}
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
fn extract(ob: &'source PyAny) -> PyResult<$rust_type> {
|
||||
|
@ -106,6 +127,10 @@ macro_rules! int_convert_u64_or_i64 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
Self::type_output()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -170,6 +195,10 @@ mod fast_128bit_int_conversion {
|
|||
PyObject::from_owned_ptr(py, obj)
|
||||
}
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::builtin("int")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
|
@ -192,6 +221,10 @@ mod fast_128bit_int_conversion {
|
|||
Ok(<$rust_type>::from_le_bytes(buffer))
|
||||
}
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
Self::type_output()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -234,6 +267,10 @@ mod slow_128bit_int_conversion {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::builtin("int")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source> FromPyObject<'source> for $rust_type {
|
||||
|
@ -253,6 +290,10 @@ mod slow_128bit_int_conversion {
|
|||
Ok((<$rust_type>::from(upper) << SHIFT) | lower)
|
||||
}
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
Self::type_output()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
use crate::err::{self, PyDowncastError, PyErr, PyResult};
|
||||
use crate::exceptions::PyValueError;
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::internal_tricks::get_ssize_index;
|
||||
use crate::once_cell::GILOnceCell;
|
||||
use crate::type_object::PyTypeInfo;
|
||||
|
@ -288,6 +289,10 @@ where
|
|||
}
|
||||
extract_sequence(obj)
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
TypeInfo::sequence_of(T::type_input())
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_sequence<'s, T>(obj: &'s PyAny) -> PyResult<Vec<T>>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//
|
||||
|
||||
use crate::err::{self, PyErr, PyResult};
|
||||
use crate::inspect::types::TypeInfo;
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
use crate::types::PyIterator;
|
||||
use crate::{ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, Python, ToPyObject};
|
||||
|
@ -271,6 +272,10 @@ where
|
|||
}
|
||||
set.into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::set_of(K::type_output())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source, K, S> FromPyObject<'source> for HashSet<K, S>
|
||||
|
@ -282,6 +287,10 @@ where
|
|||
let set: &PySet = ob.downcast()?;
|
||||
set.iter().map(K::extract).collect()
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
TypeInfo::set_of(K::type_input())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> IntoPy<PyObject> for BTreeSet<K>
|
||||
|
@ -297,6 +306,10 @@ where
|
|||
}
|
||||
set.into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::set_of(K::type_output())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source, K> FromPyObject<'source> for BTreeSet<K>
|
||||
|
@ -307,6 +320,10 @@ where
|
|||
let set: &PySet = ob.downcast()?;
|
||||
set.iter().map(K::extract).collect()
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
TypeInfo::set_of(K::type_input())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#[cfg(all(not(Py_LIMITED_API), target_endian = "little"))]
|
||||
use crate::exceptions::PyUnicodeDecodeError;
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::types::PyBytes;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, PyTryFrom, Python,
|
||||
|
@ -296,6 +297,10 @@ impl<'a> IntoPy<PyObject> for &'a str {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
PyString::new(py, self).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
<String>::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoPy<Py<PyString>> for &'a str {
|
||||
|
@ -303,6 +308,10 @@ impl<'a> IntoPy<Py<PyString>> for &'a str {
|
|||
fn into_py(self, py: Python<'_>) -> Py<PyString> {
|
||||
PyString::new(py, self).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
<String>::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Rust `Cow<'_, str>` to a Python object.
|
||||
|
@ -319,6 +328,10 @@ impl IntoPy<PyObject> for Cow<'_, str> {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
self.to_object(py)
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
<String>::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Rust `String` to a Python object.
|
||||
|
@ -341,12 +354,20 @@ impl IntoPy<PyObject> for char {
|
|||
let mut bytes = [0u8; 4];
|
||||
PyString::new(py, self.encode_utf8(&mut bytes)).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
<String>::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPy<PyObject> for String {
|
||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
PyString::new(py, &self).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::builtin("str")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoPy<PyObject> for &'a String {
|
||||
|
@ -354,6 +375,10 @@ impl<'a> IntoPy<PyObject> for &'a String {
|
|||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
PyString::new(py, self).into()
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
<String>::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows extracting strings from Python objects.
|
||||
|
@ -362,6 +387,10 @@ impl<'source> FromPyObject<'source> for &'source str {
|
|||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||
<PyString as PyTryFrom>::try_from(ob)?.to_str()
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
<String>::type_input()
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows extracting strings from Python objects.
|
||||
|
@ -372,6 +401,10 @@ impl FromPyObject<'_> for String {
|
|||
.to_str()
|
||||
.map(ToOwned::to_owned)
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
Self::type_output()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromPyObject<'_> for char {
|
||||
|
@ -386,6 +419,10 @@ impl FromPyObject<'_> for char {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
<String>::type_input()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use std::convert::TryInto;
|
||||
|
||||
use crate::ffi::{self, Py_ssize_t};
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::internal_tricks::get_ssize_index;
|
||||
use crate::types::PySequence;
|
||||
use crate::{
|
||||
|
@ -293,6 +294,10 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
|
|||
ret
|
||||
}
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::Tuple(Some(vec![$( $T::type_output() ),+]))
|
||||
}
|
||||
}
|
||||
|
||||
impl <$($T: IntoPy<PyObject>),+> IntoPy<Py<PyTuple>> for ($($T,)+) {
|
||||
|
@ -304,6 +309,10 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
|
|||
ret
|
||||
}
|
||||
}
|
||||
|
||||
fn type_output() -> TypeInfo {
|
||||
TypeInfo::Tuple(Some(vec![$( $T::type_output() ),+]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) {
|
||||
|
@ -320,6 +329,10 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
|
|||
Err(wrong_tuple_length(t, $length))
|
||||
}
|
||||
}
|
||||
|
||||
fn type_input() -> TypeInfo {
|
||||
TypeInfo::Tuple(Some(vec![$( $T::type_input() ),+]))
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ note: the following trait must be implemented
|
|||
| / pub trait IntoPy<T>: Sized {
|
||||
| | /// Performs the conversion.
|
||||
| | fn into_py(self, py: Python<'_>) -> T;
|
||||
| |
|
||||
... |
|
||||
| | }
|
||||
| | }
|
||||
| |_^
|
||||
= note: this error originates in the attribute macro `pyo3::pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
|
Loading…
Reference in New Issue