front.util#
Signature and mapping utilities shared by the front modules.
Two families live here: signature rewriting (inject_enum_annotations,
annotate_func_arguments) used to make functions dispatchable by a UI, and
small mapping helpers (deep_merge, subdict, normalize_map) used by
the spec compilation.
>>> deep_merge({'a': {'x': 1, 'y': 2}, 'b': 1}, {'a': {'y': 20}, 'c': 3})
{'a': {'x': 1, 'y': 20}, 'b': 1, 'c': 3}
Functions
|
Add annotations to the arguments of |
|
Merge |
|
Default display name: |
|
Default |
|
Return |
|
Return |
|
Make a function that will produce a (incrementally) new string at every call. |
|
Annotate chosen arguments of |
|
Make an |
|
Resolve a |
|
Get the name of a callable, or make one ( |
|
Return |
|
Get a sub-dict of Mapping |
|
- front.util.annotate_func_arguments(func, *, ignore_existing_annot=False, annot_for_argname=(), annot_for_dflt_type=(), dflt_annot)[source]#
Add annotations to the arguments of
func, by argument name or by default-value type.- Parameters:
func (
Callable) – The function whose args we want to annotateignore_existing_annot (
bool) – Set to True to ignore existing annots.annot_for_argname (
Union[dict[str,Any],Iterable[tuple[str,Any]]]) – Annotation for specific argnamesannot_for_dflt_type (
Union[dict[type,Any],Iterable[tuple[type,Any]]]) – Annotation for specific types. Arg defaults will be compared (withisinstance(dflt_val, types)) to types and the annotation (value) of the the first matching type (key) will be injecteddflt_annot (
Any) – Default annotation to use if no match found earlier. The default isinspect.Parameter.empty, which means “don’t annotate”. If you want all your params to be annotated no matter what, you might considertyping.Any, or in the case of command line interfaces,str.
- Returns:
A wrapped function with the desired signature changes, if any changes need to be made, or the same function untouched if not.
>>> from inspect import signature >>> from functools import partial >>> from typing import Any >>> >>> >>> def foo(a, b, c, aa: int=1, bb: int=1.0, cc: int=None, aaa=1, bbb=1.0, ccc=None): ... pass ...
If nothing changes, you just get back the same function:
>>> assert str(signature(annotate_func_arguments(foo))) == ( ... "(a, b, c, " ... "aa: int = 1, bb: int = 1.0, cc: int = None, " ... "aaa=1, bbb=1.0, ccc=None)" ... )
In the following:
b: strthrough the argname rule, butbb(as well asaaandbb) didn’t change becauseignore_existing_annot=Falseby default.aaa: float(even though default is1) andccc: 'NoneAnnot'because of theannot_for_dflt_typerules.
>>> annotator = partial( ... annotate_func_arguments, ... annot_for_argname = {'b': str, 'bb': str}, ... # don't confuse following with dict(int=float), which means {'int': float} ... annot_for_dflt_type = {int: float, type(None): 'NoneAnnot'}, ... ) >>> >>> wrapped_func = annotator(foo) >>> assert str(signature(wrapped_func)) == ( ... "(a, b: str, c, " ... "aa: int = 1, bb: int = 1.0, cc: int = None, " ... "aaa: float = 1, bbb=1.0, ccc: 'NoneAnnot' = None)" ... )
See in the following what happens if we ask the default annotation to be
Anyandignore_existing_annot=True:>>> another_annotator = partial( ... annotator, # use the previous one, but... ... dflt_annot=Any, # and specify a default annotation ... ignore_existing_annot=True # now ignore any existing annotations ... ) >>> >>> wrapped_func = another_annotator(foo) >>> assert str(signature(wrapped_func)) == ( ... "(a: Any, b: str, c: Any, " ... "aa: float = 1, bb: str = 1.0, cc: 'NoneAnnot' = None, " ... "aaa: float = 1, bbb: Any = 1.0, ccc: 'NoneAnnot' = None)" ... )
- front.util.deep_merge(a, b)[source]#
Merge
bintoarecursively (values ofbwin), returning a new dict.Nested mappings present in both are merged; any other value in
breplaces the one ina. Neither input is modified.>>> deep_merge({'a': {'x': 1, 'y': 2}, 'b': 1}, {'a': {'y': 20, 'z': 30}, 'c': 3}) {'a': {'x': 1, 'y': 20, 'z': 30}, 'b': 1, 'c': 3}
- front.util.dflt_name_trans(obj)[source]#
Default display name:
obj(or its name) with underscores as spaces, title-cased.>>> dflt_name_trans('my_func_name') 'My Func Name'
- front.util.dflt_trans(objs)[source]#
Default
objtransformation: ensure every object has a__name__, returning a list.Objects are passed through
copy.copy, which returns functions unchanged, so a lambda’s__name__is set on the lambda itself (to anUnnamedObjectNNNname).
- front.util.get_value(obj, *args, **kwargs)[source]#
Return
obj(*args, **kwargs)ifobjis callable, elseobjitself.>>> get_value(lambda: 3), get_value(3), get_value(lambda a, b: a + b, 1, 2) (3, 3, 3)
- front.util.incremental_str_maker(str_format='{:03.f}')[source]#
Make a function that will produce a (incrementally) new string at every call.
- front.util.iterable_to_enum(iterable, name='CustomEnum')[source]#
Make an
Enumwhose member names arestr(value)for each value ofiterable.>>> E = iterable_to_enum([1, 'two']) >>> list(E) [<CustomEnum.1: 1>, <CustomEnum.two: 'two'>] >>> E['1'].value 1
- front.util.normalize_map(map)[source]#
Resolve a
Map(mapping, callable returning one, or None) to a mapping; None gives{}.- Return type:
>>> normalize_map(None), normalize_map({'a': 1}), normalize_map(lambda: {'b': 2}) ({}, {'a': 1}, {'b': 2})
- front.util.obj_name(func)[source]#
Get the name of a callable, or make one (
UnnamedObjectNNN) for lambdas and nameless objects.
- front.util.param_default(param)[source]#
Return
param.default, orParameter.emptyif it isi2’sNotSet.NotSetin a signature means “no value given”, not a real default, so UI and schema builders must treat that param as required (no prefill, no type inference from the default).>>> from inspect import Parameter >>> from i2.deco import NotSet >>> param_default(Parameter('x', Parameter.KEYWORD_ONLY, default=3)) 3 >>> param_default(Parameter('x', Parameter.KEYWORD_ONLY, default=NotSet)) <class 'inspect._empty'> >>> param_default(Parameter('x', Parameter.KEYWORD_ONLY)) <class 'inspect._empty'>
- front.util.subdict(d, keys=None)[source]#
Get a sub-dict of Mapping
d, with only those keys that are both inkeysandd.Note that the dict will be ordered as
keysare, so can be used for reordering a Mapping.>>> subdict({'a': 1, 'b': 2, 'c': 3, 'd': 4}, keys=['b', 'a', 'd']) {'b': 2, 'a': 1, 'd': 4}