
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
4.2 KiB
title | weight |
---|---|
Typing and validation | 6 |
Adding type hints with Generics
New in version 0.92
The Component
class optionally accepts type parameters
that allow you to specify the types of args, kwargs, slots, and
data:
class Button(Component[Args, Kwargs, Slots, Data, JsData, CssData]):
...
Args
- Must be aTuple
orAny
Kwargs
- Must be aTypedDict
orAny
Data
- Must be aTypedDict
orAny
Slots
- Must be aTypedDict
orAny
Here's a full example:
from typing import NotRequired, Tuple, TypedDict, SlotContent, SlotFunc
# Positional inputs
Args = Tuple[int, str]
# Kwargs inputs
class Kwargs(TypedDict):
variable: str
another: int
maybe_var: NotRequired[int] # May be ommited
# Data returned from `get_context_data`
class Data(TypedDict):
variable: str
# The data available to the `my_slot` scoped slot
class MySlotData(TypedDict):
value: int
# Slots
class Slots(TypedDict):
# Use SlotFunc for slot functions.
# The generic specifies the `data` dictionary
my_slot: NotRequired[SlotFunc[MySlotData]]
# SlotContent == Union[str, SafeString]
another_slot: SlotContent
class Button(Component[Args, Kwargs, Slots, Data, JsData, CssData]):
def get_context_data(self, variable, another):
return {
"variable": variable,
}
When you then call Component.render
or Component.render_to_response
, you will get type hints:
Button.render(
# Error: First arg must be `int`, got `float`
args=(1.25, "abc"),
# Error: Key "another" is missing
kwargs={
"variable": "text",
},
)
Usage for Python <3.11
On Python 3.8-3.10, use typing_extensions
from typing_extensions import TypedDict, NotRequired
Additionally on Python 3.8-3.9, also import annotations
:
from __future__ import annotations
Moreover, on 3.10 and less, you may not be able to use NotRequired
, and instead you will need to mark either all keys are required, or all keys as optional, using TypeDict's total
kwarg.
See PEP-655 for more info.
Passing additional args or kwargs
You may have a function that supports any number of args or kwargs:
def get_context_data(self, *args, **kwargs):
...
This is not supported with the typed components.
As a workaround:
-
For
*args
, set a positional argument that accepts a list of values:# Tuple of one member of list of strings Args = Tuple[List[str]]
-
For
*kwargs
, set a keyword argument that accepts a dictionary of values:class Kwargs(TypedDict): variable: str another: int # Pass any extra keys under `extra` extra: Dict[str, any]
Handling no args or no kwargs
To declare that a component accepts no Args, Kwargs, etc, you can use EmptyTuple
and EmptyDict
types:
from django_components import Component, EmptyDict, EmptyTuple
Args = EmptyTuple
Kwargs = Data = Slots = EmptyDict
class Button(Component[Args, Kwargs, Slots, Data, JsData, CssData]):
...
Runtime input validation with types
New in version 0.96
NOTE: Kwargs, slots, and data validation is supported only for Python >=3.11
In Python 3.11 and later, when you specify the component types, you will get also runtime validation of the inputs you pass to Component.render
or Component.render_to_response
.
So, using the example from before, if you ignored the type errors and still ran the following code:
Button.render(
# Error: First arg must be `int`, got `float`
args=(1.25, "abc"),
# Error: Key "another" is missing
kwargs={
"variable": "text",
},
)
This would raise a TypeError
:
Component 'Button' expected positional argument at index 0 to be <class 'int'>, got 1.25 of type <class 'float'>
In case you need to skip these errors, you can either set the faulty member to Any
, e.g.:
# Changed `int` to `Any`
Args = Tuple[Any, str]
Or you can replace Args
with Any
altogether, to skip the validation of args:
# Replaced `Args` with `Any`
class Button(Component[Any, Kwargs, Slots, Data, JsData, CssData]):
...
Same applies to kwargs, data, and slots.