Source code for dataclass_wizard.v1.decorators

from __future__ import annotations

from dataclasses import MISSING
from functools import wraps
from typing import TYPE_CHECKING, Callable, Union, cast

from ..type_def import DT
from ..utils.function_builder import FunctionBuilder


if TYPE_CHECKING:  # pragma: no cover
    from .models import Extras, TypeInfo


[docs] def process_patterned_date_time(func: Callable) -> Callable: """ Decorator for processing patterned date and time data. If the 'pattern' key exists in the `extras` dictionary, it updates the base and origin of the type information and processes the pattern before calling the original function. Supports both class methods and static methods. Args: func (Callable): The function to decorate, either a class method or static method. Returns: Callable: The wrapped function with pattern processing applied. """ # Determine if the function is a class method # noinspection PyUnresolvedReferences is_class_method = func.__code__.co_argcount == 3 if is_class_method: @wraps(func) def class_method_wrapper(cls, tp: TypeInfo, extras: Extras): # Process pattern if it exists in extras if (pb := extras.get('pattern')) is not None: pb.base = cast(type[DT], tp.origin) tp.origin = cast(type, pb) return pb.load_to_pattern(tp, extras) # Fallback to the original method return func(cls, tp, extras) return class_method_wrapper else: @wraps(func) def static_method_wrapper(tp: TypeInfo, extras: Extras): # Process pattern if it exists in extras if (pb := extras.get('pattern')) is not None: pb.base = cast(type[DT], tp.origin) tp.origin = cast(type, pb) return pb.load_to_pattern(tp, extras) # Fallback to the original method return func(tp, extras) return static_method_wrapper
[docs] def setup_recursive_safe_function( func: Callable = None, *, fn_name: Union[str, None] = None, is_generic: bool = False, add_cls: bool = True, ) -> Callable: """ A decorator to ensure recursion safety and facilitate dynamic function generation with `FunctionBuilder`, supporting both generic and non-generic types. The decorated function can define the logic for dynamically generated functions. If `fn_name` is provided, the decorator assumes that the function generation context (e.g., `with fn_gen.function(...)`) has already been handled externally and will not apply it again. :param func: The function to decorate. If None, the decorator is applied with arguments. :type func: Callable, optional :param fn_name: A format string for dynamically generating function names, or None. :type fn_name: str, optional :param is_generic: Whether the function deals with generic types. :type is_generic: bool, optional :param add_cls: Whether the class should be added to the function locals for `FunctionBuilder`. :type add_cls: bool, optional :return: The decorated function with recursion safety and dynamic function generation. :rtype: Callable """ if func is None: return lambda f: setup_recursive_safe_function( f, fn_name=fn_name, is_generic=is_generic, add_cls=add_cls, ) def _wrapper_logic(tp: TypeInfo, extras: Extras, _cls=None) -> str: """ Shared logic for both class and regular methods. Ensures recursion safety and integrates `FunctionBuilder` to dynamically create functions. :param tp: The type or generic type being processed. :param extras: A context dictionary containing auxiliary information like recursion guards and function builders. :type extras: dict :param _cls: The class context for class methods. Defaults to None. :return: The generated function call expression as a string. :rtype: str """ cls = tp.args if is_generic else tp.origin recursion_guard = extras['recursion_guard'] if (_fn_name := recursion_guard.get(cls)) is None: cls_name = extras['cls_name'] tp_name = func.__name__.split('_', 2)[-1] # Generate the function name if fn_name: _fn_name = fn_name.format(cls_name=tp.name) else: _fn_name = ( f'_load_{cls_name}_{tp_name}_{tp.field_i}' if is_generic else f'_load_{cls_name}_{tp_name}_{tp.name}' ) recursion_guard[cls] = _fn_name # Retrieve the main FunctionBuilder main_fn_gen = extras['fn_gen'] # Prepare a new FunctionBuilder for this function updated_extras = extras.copy() updated_extras['locals'] = _locals = {'cls': cls} if add_cls else {} updated_extras['fn_gen'] = new_fn_gen = FunctionBuilder() # Apply the decorated function logic if fn_name: # Assume `with fn_gen.function(...)` is already handled func(_cls, tp, updated_extras) if _cls else func(tp, updated_extras) else: # Apply `with fn_gen.function(...)` explicitly with new_fn_gen.function(_fn_name, ['v1'], MISSING, _locals): func(_cls, tp, updated_extras) if _cls else func(tp, updated_extras) # Merge the new FunctionBuilder into the main one main_fn_gen |= new_fn_gen return f'{_fn_name}({tp.v()})' # Determine if the function is a class method # noinspection PyUnresolvedReferences is_class_method = func.__code__.co_argcount == 3 if is_class_method: def wrapper_class_method(_cls, tp, extras) -> str: """ Wrapper logic for class methods. Passes the class context to `_wrapper_logic`. :param _cls: The class instance. :param tp: The type or generic type being processed. :param extras: A context dictionary with auxiliary information. :type extras: dict :return: The generated function call expression as a string. :rtype: str """ return _wrapper_logic(tp, extras, _cls) wrapper = wraps(func)(wrapper_class_method) else: wrapper = wraps(func)(_wrapper_logic) return wrapper
[docs] def setup_recursive_safe_function_for_generic(func: Callable) -> Callable: """ A helper decorator to handle generic types using `setup_recursive_safe_function`. Parameters ---------- func : Callable The function to be decorated, responsible for returning the generated function name. Returns ------- Callable A wrapped function ensuring recursion safety for generic types. """ return setup_recursive_safe_function(func, is_generic=True)