-
-
Notifications
You must be signed in to change notification settings - Fork 136
Structure using the value of attr.ib converter, if defined #139
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
Changes from all commits
2cf4132
51ca82f
fd91e80
1ff9da7
8342cba
6bec6b2
db6793d
42e7285
d2ba3d7
82d4ffd
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 |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| from typing import Type | ||
|
|
||
|
|
||
| class StructureHandlerNotFoundError(Exception): | ||
| """Error raised when structuring cannot find a handler for converting inputs into :attr:`type_`.""" | ||
|
|
||
| def __init__(self, message: str, type_: Type) -> None: | ||
| super().__init__(message) | ||
| self.type_ = type_ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,18 @@ | ||
| import functools | ||
| import linecache | ||
| import re | ||
| import uuid | ||
| from dataclasses import is_dataclass | ||
| from typing import Any, Optional, Type, TypeVar | ||
| from typing import Any, Optional, TYPE_CHECKING, Type, TypeVar | ||
|
|
||
| import attr | ||
| from attr import NOTHING, resolve_types | ||
|
|
||
| from ._compat import adapted_fields, get_args, get_origin, is_bare, is_generic | ||
| from .errors import StructureHandlerNotFoundError | ||
|
|
||
| if TYPE_CHECKING: | ||
| from cattr.converters import Converter | ||
|
|
||
|
|
||
| @attr.s(slots=True, frozen=True) | ||
|
|
@@ -130,9 +135,10 @@ def generate_mapping(cl: Type, old_mapping): | |
|
|
||
| def make_dict_structure_fn( | ||
| cl: Type, | ||
| converter, | ||
| converter: "Converter", | ||
| _cattrs_forbid_extra_keys: bool = False, | ||
| _cattrs_use_linecache: bool = True, | ||
| _cattrs_prefer_attrib_converters: bool = False, | ||
| **kwargs, | ||
| ): | ||
| """Generate a specialized dict structuring function for an attrs class.""" | ||
|
|
@@ -185,26 +191,40 @@ def make_dict_structure_fn( | |
| # For each attribute, we try resolving the type here and now. | ||
| # If a type is manually overwritten, this function should be | ||
| # regenerated. | ||
| if type is not None: | ||
| if _cattrs_prefer_attrib_converters and a.converter is not None: | ||
| # The attribute has defined its own conversion, so pass | ||
| # the original value through without invoking cattr hooks | ||
| handler = None | ||
| elif type is not None: | ||
| handler = converter._structure_func.dispatch(type) | ||
| else: | ||
| handler = converter.structure | ||
|
|
||
| if not _cattrs_prefer_attrib_converters and a.converter is not None: | ||
| handler = _fallback_to_passthru(handler) | ||
|
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. I don't think we need to use Ideally the
Contributor
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. Sorry, I don't quite follow what you're proposing the code should do differently here. |
||
|
|
||
| struct_handler_name = f"structure_{an}" | ||
| globs[struct_handler_name] = handler | ||
|
|
||
| ian = an if (is_dc or an[0] != "_") else an[1:] | ||
| kn = an if override.rename is None else override.rename | ||
| globs[f"type_{an}"] = type | ||
| if a.default is NOTHING: | ||
| lines.append( | ||
| f" '{ian}': {struct_handler_name}(o['{kn}'], type_{an})," | ||
| ) | ||
| if handler: | ||
| lines.append( | ||
| f" '{ian}': {struct_handler_name}(o['{kn}'], type_{an})," | ||
| ) | ||
| else: | ||
| lines.append(f" '{ian}': o['{kn}'],") | ||
| else: | ||
| post_lines.append(f" if '{kn}' in o:") | ||
| post_lines.append( | ||
| f" res['{ian}'] = {struct_handler_name}(o['{kn}'], type_{an})" | ||
| ) | ||
| if handler: | ||
| post_lines.append( | ||
| f" res['{ian}'] = {struct_handler_name}(o['{kn}'], type_{an})" | ||
| ) | ||
| else: | ||
| post_lines.append(f" res['{ian}'] = o['{kn}']") | ||
|
|
||
| lines.append(" }") | ||
| if _cattrs_forbid_extra_keys: | ||
| allowed_fields = {a.name for a in attrs} | ||
|
|
@@ -237,6 +257,17 @@ def make_dict_structure_fn( | |
| return globs[fn_name] | ||
|
|
||
|
|
||
| def _fallback_to_passthru(func): | ||
| @functools.wraps(func) | ||
| def invoke(obj, type_): | ||
| try: | ||
| return func(obj, type_) | ||
| except StructureHandlerNotFoundError: | ||
| return obj | ||
|
|
||
| return invoke | ||
|
|
||
|
|
||
| def make_iterable_unstructure_fn(cl: Any, converter, unstructure_to=None): | ||
| """Generate a specialized unstructure function for an iterable.""" | ||
| handler = converter.unstructure | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.