1+ """
2+ Supporting utilities for manipulating arguments of functions.
3+ """
4+
15from __future__ import annotations
26
37import inspect
48from functools import partial
5- from typing import Callable , Set , Tuple , Union
9+ from typing import Callable , Set , Union
610
711__all__ = ["maybe_add_argument" ]
812
913
10- def fn_accept_additional_argument (* args , fn : Callable , arg : str , ** kwargs ):
11- """
12- Calls the given function with the given arguments. In the process of calling the
13- wrapped function, it removes the specified keyword argument from the passed keyword
14- arguments. This function can be pickled by `pickle` as it is on the .
14+ def _accept_additional_argument (* args , fun : Callable , arg : str , ** kwargs ):
15+ """Calls the given function with the given positional and keyword arguments,
16+ removing `arg` from the keyword arguments.
1517
1618 Args:
1719 args: Positional arguments to pass to the function.
18- fn : The function to call.
20+ fun : The function to call.
1921 arg: The name of the argument to remove.
2022 kwargs: Keyword arguments to pass to the function.
2123
@@ -27,50 +29,30 @@ def fn_accept_additional_argument(*args, fn: Callable, arg: str, **kwargs):
2729 except KeyError :
2830 pass
2931
30- return fn (* args , ** kwargs )
32+ return fun (* args , ** kwargs )
3133
3234
33- def get_free_args_fn (fun : Union [Callable , partial ]) -> Set [str ]:
34- """
35- Accept a function or a partial definition and return the set of arguments that are
36- free. An argument is considered free if it is not set by the partial and is a
37- parameter of the function. Formally, this can be described as follows:
38-
39- Let phi be the function that returns the set of arguments that are set by a given
40- function. For functions f and g, one can extend the function phi recursively to psi
41- as follows:
42-
43- a) If `f = partial(g, **kwargs)`, two cases arise:
44- a) If `g = fn_accept_additional_argument`, then
45- `psi(f) = psi(kwargs.fn) + {kwargs.arg}`
46- b) Else, `psi(f) = psi(g) - kwargs.keys()`
47- b) Else,`psi(g) = phi(g)` (Note that this is the base case.)
48-
49- Transforming `fn_accept_additional_argument` into a partial function is done by:
50- `f(fn, arg) = partial(fn_accept_additional_argument, **{fn: fn, arg: arg})`
51- This function is the inverse (with respect to phi) to the function
52- `g(fn, arg, val) = partial(g, {arg: val})` in the sense that
53- `phi(fn) = phi(f(g(fn, arg, val), arg))`
54-
55- Together, these components form an algebraic system for reasoning about function
56- arguments. Each operation (phi, psi, f, g) transforms a function into a new
57- function in a way that changes the set of arguments that have been applied, and we
58- have precise rules for understanding those transformations.
35+ def free_arguments (fun : Union [Callable , partial ]) -> Set [str ]:
36+ """Computes the set of free arguments for a function or [partial object][].
37+
38+ All arguments of a function are considered free unless they are set by a
39+ partial. For example, if `f = partial(g, a=1)`, then `a` is not a free
40+ argument of `f`.
5941
6042 Args:
61- fun: A function composed of raw functions `f` or partial functions as
62- constructed in the description.
43+ fun: A callable or a [partial object][].
6344
6445 Returns:
65- A set of arguments that were set by the partial .
46+ The set of free arguments of `fun` .
6647 """
6748 args_set_by_partial : Set [str ] = set ()
6849
6950 def _rec_unroll_partial_function_args (g : Union [Callable , partial ]) -> Callable :
70- """
71- Store arguments and recursively call itself if the function is a partial. In the
72- end, return the initial wrapped function. Besides partial functions it also
73- supports `partial(fn_accept_additional_argument, *args, **kwargs)` constructs.
51+ """Stores arguments and recursively call itself if `g` is a partial. In
52+ the end, returns the initially wrapped function.
53+
54+ This handles the construct `partial(_accept_additional_argument, *args,
55+ **kwargs)` that is used by `maybe_add_argument`.
7456
7557 Args:
7658 g: A partial or a function to unroll.
@@ -80,11 +62,11 @@ def _rec_unroll_partial_function_args(g: Union[Callable, partial]) -> Callable:
8062 """
8163 nonlocal args_set_by_partial
8264
83- if isinstance (g , partial ) and g .func == fn_accept_additional_argument :
65+ if isinstance (g , partial ) and g .func == _accept_additional_argument :
8466 arg = g .keywords ["arg" ]
8567 if arg in args_set_by_partial :
8668 args_set_by_partial .remove (arg )
87- return _rec_unroll_partial_function_args (g .keywords ["fn " ])
69+ return _rec_unroll_partial_function_args (g .keywords ["fun " ])
8870 elif isinstance (g , partial ):
8971 args_set_by_partial .update (g .keywords .keys ())
9072 args_set_by_partial .update (g .args )
@@ -116,4 +98,4 @@ def maybe_add_argument(fun: Callable, new_arg: str) -> Callable:
11698 if new_arg in free_arguments (fun ):
11799 return fun
118100
119- return functools . partial (_accept_additional_argument , fun = fun , arg = new_arg )
101+ return partial (_accept_additional_argument , fun = fun , arg = new_arg )
0 commit comments