Skip to main content

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