Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import types
2import inspect
3from collections.abc import Iterable
4from typer.models import OptionInfo
5from typer.main import get_params_convertors_ctx_param_name_from_function
6from rich.console import Console
8console = Console()
11def copy_func(f, name=None):
12 """
13 Returns a deep copy of a function.
15 The new function has the same code, globals, defaults, closure, annotations, and name
16 (unless a new name is provided)
18 Derived from https://stackoverflow.com/a/30714299
19 """
20 fn = types.FunctionType(f.__code__, f.__globals__, name or f.__name__, f.__defaults__, f.__closure__)
21 # in case f was given attrs (note this dict is a shallow copy):
22 fn.__dict__.update(f.__dict__)
23 fn.__annotations__.update(f.__annotations__)
24 return fn
27def call_func(func, *args, **kwargs):
28 """
29 Calls a function while filtering the kwargs for only ones in the signature.
31 Args:
32 func (Callable): The function to call
34 Returns:
35 The result of the function call.
36 """
37 allowed_params, _, _ = get_params_convertors_ctx_param_name_from_function(func)
38 allowed_param_names = [p.name for p in allowed_params]
39 kwargs = {key: value for key, value in kwargs.items() if key in allowed_param_names}
40 return func(*args, **kwargs)
43def change_typer_to_defaults(func):
44 func = getattr(func, "__func__", func)
45 signature = inspect.signature(func)
47 # Create a dictionary with both the existing parameters for the function and the new ones
48 parameters = dict(signature.parameters)
50 for key, value in parameters.items():
51 if isinstance(value.default, OptionInfo):
52 parameters[key] = value.replace(default=value.default.default)
54 func.__signature__ = signature.replace(parameters=parameters.values())
56 # Change defaults directly
57 if func.__defaults__ is not None:
58 func.__defaults__ = tuple(
59 [value.default if isinstance(value, OptionInfo) else value for value in func.__defaults__]
60 )
63def add_kwargs(to_func, from_funcs):
64 """Adds all the keyword arguments from one function to the signature of another function.
66 Args:
67 from_funcs (callable or iterable): The function with new parameters to add.
68 to_func (callable): The function which will receive the new parameters in its signature.
69 """
71 if not isinstance(from_funcs, Iterable):
72 from_funcs = [from_funcs]
74 for from_func in from_funcs:
75 # Get the existing parameters
76 to_func = getattr(to_func, "__func__", to_func)
77 from_func = getattr(from_func, "__func__", from_func)
78 from_func_signature = inspect.signature(from_func)
79 to_func_signature = inspect.signature(to_func)
81 # Create a dictionary with both the existing parameters for the function and the new ones
82 to_func_parameters = dict(to_func_signature.parameters)
84 if "kwargs" in to_func_parameters:
85 kwargs_parameter = to_func_parameters.pop("kwargs")
87 from_func_kwargs = {
88 k: v
89 for k, v in from_func_signature.parameters.items()
90 if v.default != inspect.Parameter.empty and k not in to_func_parameters
91 }
92 # to_func_parameters['kwargs'] = kwargs_parameter
94 to_func_parameters.update(from_func_kwargs)
96 # Modify function signature with the parameters in this dictionary
97 # print('to_func', hex(id(to_func)))
98 to_func.__signature__ = to_func_signature.replace(parameters=to_func_parameters.values())
99 for key, value in from_func.__annotations__.items():
100 if key not in to_func.__annotations__:
101 to_func.__annotations__[key] = value