52 lines
1.7 KiB
Python
52 lines
1.7 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any, Callable
|
|
|
|
from attrs import NOTHING, Attribute, Factory
|
|
|
|
from .._compat import is_bare_final
|
|
from ..fns import raise_error
|
|
|
|
if TYPE_CHECKING: # pragma: no cover
|
|
from cattr.converters import BaseConverter
|
|
|
|
|
|
def find_structure_handler(
|
|
a: Attribute, type: Any, c: BaseConverter, prefer_attrs_converters: bool = False
|
|
) -> Callable[[Any, Any], Any] | None:
|
|
"""Find the appropriate structure handler to use.
|
|
|
|
Return `None` if no handler should be used.
|
|
"""
|
|
if a.converter is not None and prefer_attrs_converters:
|
|
# If the user as requested to use attrib converters, use nothing
|
|
# so it falls back to that.
|
|
handler = None
|
|
elif a.converter is not None and not prefer_attrs_converters and type is not None:
|
|
handler = c._structure_func.dispatch(type)
|
|
if handler == raise_error:
|
|
handler = None
|
|
elif type is not None:
|
|
if (
|
|
is_bare_final(type)
|
|
and a.default is not NOTHING
|
|
and not isinstance(a.default, Factory)
|
|
):
|
|
# This is a special case where we can use the
|
|
# type of the default to dispatch on.
|
|
type = a.default.__class__
|
|
handler = c._structure_func.dispatch(type)
|
|
if handler == c._structure_call:
|
|
# Finals can't really be used with _structure_call, so
|
|
# we wrap it so the rest of the toolchain doesn't get
|
|
# confused.
|
|
|
|
def handler(v, _, _h=handler):
|
|
return _h(v, type)
|
|
|
|
else:
|
|
handler = c._structure_func.dispatch(type)
|
|
else:
|
|
handler = c.structure
|
|
return handler
|