pyo3/types/
genericalias.rs

1use crate::err::PyResult;
2use crate::ffi_ptr_ext::FfiPtrExt;
3use crate::py_result_ext::PyResultExt;
4use crate::{ffi, Bound, PyAny, Python};
5
6/// Represents a Python [`types.GenericAlias`](https://docs.python.org/3/library/types.html#types.GenericAlias) object.
7///
8/// Values of this type are accessed via PyO3's smart pointers, e.g. as
9/// [`Py<PyGenericAlias>`][crate::Py] or [`Bound<'py, PyGenericAlias>`][Bound].
10///
11/// This type is particularly convenient for users implementing
12/// [`__class_getitem__`](https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__)
13/// for PyO3 classes to allow runtime parameterization.
14#[repr(transparent)]
15pub struct PyGenericAlias(PyAny);
16
17pyobject_native_type!(
18    PyGenericAlias,
19    ffi::PyDictObject,
20    pyobject_native_static_type_object!(ffi::Py_GenericAliasType),
21    "builtins",
22    "GenericAlias"
23);
24
25impl PyGenericAlias {
26    /// Creates a new Python GenericAlias object.
27    ///
28    /// origin should be a non-parameterized generic class.
29    /// args should be a tuple (possibly of length 1) of types which parameterize origin.
30    pub fn new<'py>(
31        py: Python<'py>,
32        origin: &Bound<'py, PyAny>,
33        args: &Bound<'py, PyAny>,
34    ) -> PyResult<Bound<'py, PyGenericAlias>> {
35        unsafe {
36            ffi::Py_GenericAlias(origin.as_ptr(), args.as_ptr())
37                .assume_owned_or_err(py)
38                .cast_into_unchecked()
39        }
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use crate::instance::BoundObject;
46    use crate::types::any::PyAnyMethods;
47    use crate::Python;
48
49    use super::PyGenericAlias;
50
51    // Tests that PyGenericAlias::new is identical to types.GenericAlias
52    // created from Python.
53    #[test]
54    fn equivalency_test() {
55        Python::attach(|py| {
56            let list_int = py.eval(c"list[int]", None, None).unwrap().into_bound();
57
58            let cls = py.eval(c"list", None, None).unwrap().into_bound();
59            let key = py.eval(c"(int,)", None, None).unwrap().into_bound();
60            let generic_alias = PyGenericAlias::new(py, &cls, &key).unwrap();
61
62            assert!(generic_alias.eq(list_int).unwrap());
63        })
64    }
65}