|  | 
| 4 | 4 | """Utilities to deal with configuration.""" | 
| 5 | 5 | 
 | 
| 6 | 6 | from collections.abc import Mapping | 
| 7 |  | -from typing import Any, TypeVar, cast | 
|  | 7 | +from typing import Any, ClassVar, Protocol, TypeVar, cast | 
| 8 | 8 | 
 | 
| 9 | 9 | from marshmallow import Schema | 
| 10 | 10 | from marshmallow_dataclass import class_schema | 
| 11 | 11 | 
 | 
| 12 |  | -T = TypeVar("T") | 
|  | 12 | + | 
|  | 13 | +class Dataclass(Protocol): | 
|  | 14 | +    """A protocol for dataclasses.""" | 
|  | 15 | + | 
|  | 16 | +    __dataclass_fields__: ClassVar[dict[str, Any]] | 
|  | 17 | +    """The fields of the dataclass.""" | 
|  | 18 | + | 
|  | 19 | + | 
|  | 20 | +DataclassT = TypeVar("DataclassT", bound=Dataclass) | 
| 13 | 21 | """Type variable for configuration classes.""" | 
| 14 | 22 | 
 | 
| 15 | 23 | 
 | 
| 16 | 24 | def load_config( | 
| 17 |  | -    cls: type[T], | 
|  | 25 | +    cls: type[DataclassT], | 
| 18 | 26 |     config: Mapping[str, Any], | 
| 19 | 27 |     /, | 
| 20 | 28 |     base_schema: type[Schema] | None = None, | 
| 21 | 29 |     **marshmallow_load_kwargs: Any, | 
| 22 |  | -) -> T: | 
|  | 30 | +) -> DataclassT: | 
| 23 | 31 |     """Load a configuration from a dictionary into an instance of a configuration class. | 
| 24 | 32 | 
 | 
| 25 | 33 |     The configuration class is expected to be a [`dataclasses.dataclass`][], which is | 
| @@ -56,4 +64,4 @@ def load_config( | 
| 56 | 64 |     instance = class_schema(cls, base_schema)().load(config, **marshmallow_load_kwargs) | 
| 57 | 65 |     # We need to cast because `.load()` comes from marshmallow and doesn't know which | 
| 58 | 66 |     # type is returned. | 
| 59 |  | -    return cast(T, instance) | 
|  | 67 | +    return cast(DataclassT, instance) | 
0 commit comments