-
Notifications
You must be signed in to change notification settings - Fork 2
Add support for Enums #29 #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a493d26
4c06b31
5afa686
3da4c2c
0da89a5
219fff3
9dc36a4
f3485a9
a4ba84c
56b051d
1bbdda5
82f7569
a5a70b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
| ) | ||
| from dataclasses import MISSING, Field, asdict, dataclass, fields, is_dataclass, replace | ||
| from datetime import date, datetime, time, timedelta | ||
| from enum import Enum | ||
| from functools import reduce | ||
| from importlib import import_module | ||
| from inspect import cleandoc, get_annotations, getmodule, getsource, isabstract | ||
|
|
@@ -28,10 +29,21 @@ | |
| from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, Generic, TypeVar, Union, cast, get_args, get_origin, overload | ||
| from weakref import WeakKeyDictionary | ||
|
|
||
| if sys.version_info < (3, 11): | ||
| import tomli as tomllib # pragma: no cover | ||
| else: | ||
| import tomllib # pragma: no cover | ||
| if sys.version_info < (3, 11): # pragma: no cover | ||
| import tomli as tomllib | ||
|
|
||
| if TYPE_CHECKING: | ||
|
|
||
| class ReprEnum(Enum): | ||
| ... | ||
|
|
||
| else: | ||
| from enum import IntEnum, IntFlag | ||
|
|
||
| ReprEnum = IntEnum | IntFlag | ||
| else: # pragma: no cover | ||
| import tomllib # noqa: I001 | ||
| from enum import ReprEnum | ||
|
|
||
|
|
||
| def _collect_type(field_type: type, context: str) -> type | Binder[Any]: | ||
|
|
@@ -49,7 +61,7 @@ def _collect_type(field_type: type, context: str) -> type | Binder[Any]: | |
| return object | ||
| elif not isinstance(field_type, type): | ||
| raise TypeError(f"Annotation for field '{context}' is not a type") | ||
| elif issubclass(field_type, str | int | float | date | time | timedelta | ModuleType | Path): | ||
| elif issubclass(field_type, str | int | float | date | time | timedelta | ModuleType | Path | Enum): | ||
| return field_type | ||
| elif field_type is type: | ||
| # https://github.com/python/mypy/issues/13026 | ||
|
|
@@ -209,7 +221,6 @@ def _check_field(field: Field, field_type: type, context: str) -> None: | |
|
|
||
| @dataclass(slots=True) | ||
| class _ClassInfo(Generic[T]): | ||
|
|
||
| _cache: ClassVar[MutableMapping[type[Any], _ClassInfo[Any]]] = WeakKeyDictionary() | ||
|
|
||
| dataclass: type[T] | ||
|
|
@@ -314,6 +325,24 @@ def _bind_to_single_type(self, value: object, field_type: type, context: str) -> | |
| if not isinstance(value, str): | ||
| raise TypeError(f"Expected TOML string for path '{context}', got '{type(value).__name__}'") | ||
| return field_type(value) | ||
| elif issubclass(field_type, ReprEnum): | ||
| if issubclass(field_type, int) and not isinstance(value, int): | ||
| raise TypeError(f"Value for '{context}': '{value}' is not of type int") | ||
| if issubclass(field_type, str) and not isinstance(value, str): | ||
| raise TypeError(f"Value for '{context}': '{value}' is not of type str") | ||
| return field_type(value) | ||
| elif issubclass(field_type, Enum): | ||
| if not isinstance(value, str): | ||
| raise TypeError( | ||
| f"Value for '{context}': '{value}' is not a valid key for enum '{field_type}', " | ||
| f"must be of type str" | ||
| ) | ||
| for enum_value in field_type: | ||
| if enum_value.name.lower() == value.lower(): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Small optimization: you could store
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since there is no other case where this is used in this function I'm not sure this would help 🤔 |
||
| return enum_value | ||
| raise TypeError( | ||
| f"Value for '{context}': '{value}' is not a valid key for enum '{field_type}', could not be found" | ||
| ) | ||
| elif isinstance(value, field_type) and ( | ||
| type(value) is not bool or field_type is bool or field_type is object | ||
| ): | ||
|
|
@@ -668,6 +697,13 @@ def format_toml_pair(key: str, value: object) -> str: | |
| def _to_toml_pair(value: object) -> tuple[str | None, Any]: | ||
| """Return a TOML-compatible suffix and value pair with the data from the given rich value object.""" | ||
| match value: | ||
| # enums have to be checked before basic types because for instance | ||
| # IntEnum is also of type int | ||
| case Enum(): | ||
| if isinstance(value, ReprEnum): | ||
| return None, value.value | ||
| else: | ||
| return None, value.name.lower() | ||
| case str() | int() | float() | date() | time() | Path(): # note: 'bool' is a subclass of 'int' | ||
| return None, value | ||
| case timedelta(): | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.