Skip to content

Commit a8705f3

Browse files
committed
Add documentation
1 parent 88835fd commit a8705f3

File tree

1 file changed

+77
-1
lines changed

1 file changed

+77
-1
lines changed

guide/src/python-typing-hints.md

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ As we can see, those are not full definitions containing implementation, but jus
4141

4242
### What do the PEPs say?
4343

44-
At the time of writing this documentation, the `pyi` files are referenced in three PEPs.
44+
At the time of writing this documentation, the `pyi` files are referenced in four PEPs.
4545

4646
[PEP8 - Style Guide for Python Code - #Function Annotations](https://www.python.org/dev/peps/pep-0008/#function-annotations) (last point) recommends all third party library creators to provide stub files as the source of knowledge about the package for type checker tools.
4747

@@ -55,6 +55,8 @@ It contains a specification for them (highly recommended reading, since it conta
5555

5656
[PEP561 - Distributing and Packaging Type Information](https://www.python.org/dev/peps/pep-0561/) describes in detail how to build packages that will enable type checking. In particular it contains information about how the stub files must be distributed in order for type checkers to use them.
5757

58+
[PEP560 - Core support for typing module and generic types](https://www.python.org/dev/peps/pep-0560/) describes the details on how Python's type system internally supports generics, including both runtime behavior and integration with static type checkers.
59+
5860
## How to do it?
5961

6062
[PEP561](https://www.python.org/dev/peps/pep-0561/) recognizes three ways of distributing type information:
@@ -165,3 +167,77 @@ class Car:
165167
:return: the name of the color our great algorithm thinks is the best for this car
166168
"""
167169
```
170+
171+
### Supporting Generics
172+
173+
Type annotations can also be made generic in Python. They are useful for working
174+
with different types while maintaining type safety. Usually, generic classes
175+
inherit from the `typing.Generic` metaclass.
176+
177+
Take for example the following `.pyi` file that specifies a `Car` that can
178+
accept multiple types of wheels:
179+
180+
```python
181+
from typing import Generic, TypeVar
182+
183+
W = TypeVar('W')
184+
185+
class Car(Generic[W]):
186+
def __init__(self, wheels: list[W]) -> None: ...
187+
188+
def get_wheels(self) -> list[W]: ...
189+
190+
def change_wheel(self, wheel_number: int, wheel: W) -> None: ...
191+
```
192+
193+
This way, the end-user can specify the type with variables such as `truck: Car[SteelWheel] = ...`
194+
and `f1_car: Car[AlloyWheel] = ...`.
195+
196+
There is also a special syntax for specifying generic types in Python 3.12+:
197+
198+
```python
199+
class Car[W]:
200+
def __init__(self, wheels: list[W]) -> None: ...
201+
202+
def get_wheels(self) -> list[W]: ...
203+
```
204+
205+
#### Runtime Behaviour
206+
207+
Stub files (`pyi`) are only useful for static type checkers and ignored at runtime. Therefore,
208+
PyO3 classes do not inherit from `typing.Generic` even if specified in the stub files.
209+
210+
This can cause some runtime issues, as annotating a variable like `f1_car: Car[AlloyWheel] = ...`
211+
can make Python call magic methods that are not defined.
212+
213+
To overcome this limitation, implementers can pass the `generic` parameter to `pyclass` in Rust:
214+
215+
```rust
216+
#[pyclass(generic)]
217+
```
218+
219+
#### Advanced Users
220+
221+
`#[pyclass(generic)]` implements a very simple runtime behavior that accepts
222+
any generic argument. Advanced users can opt to manually implement
223+
[`__class_geitem__`](https://docs.python.org/3/reference/datamodel.html#emulating-generic-types)
224+
for the generic class to have more control.
225+
226+
```rust
227+
impl MyClass {
228+
#[classmethod]
229+
#[pyo3(signature = (key, /))]
230+
pub fn __class_getitem__(
231+
cls: &Bound<'_, PyType>,
232+
key: &Bound<'_, PyAny>,
233+
) -> PyResult<PyObject> {
234+
/* implementation details */
235+
}
236+
}
237+
```
238+
239+
Note that [`pyo3::types::PyGenericAlias`][pygenericalias] can be helfpul when implementing
240+
`__class_geitem__` as it can create [`types.GenericAlias`][genericalias] objects from Rust.
241+
242+
[pygenericalias]: {{#PYO3_DOCS_URL}}/pyo3/types/struct.pygenericalias
243+
[genericalias]: https://docs.python.org/3/library/types.html#types.GenericAlias

0 commit comments

Comments
 (0)