ju.util¶
Ju base utils.
Functions
|
Calls a function to an object, returing the result |
|
|
|
|
|
Will return a CallableMapper from the specification of a Mapper, which could be a Mapping itself, a callable, or (key, value) pairs. |
|
Will return a MappingMapper (a dict really) from the specification of a Mapper, which could be a Mapping itself, a callable, or (key, value) pairs. |
|
Returns a function that searches for a feature in a list of feature_processor_pairs |
|
Returns the output of a feature based on a featurizer and a list of feature_output_pairs |
|
Returns the output of a feature based on a featurizer and a mapping |
|
|
|
Checks if the type of a parameter's default value or its annotation matches a given type. |
|
|
|
Returns |
|
Returns a function that switches between cases based on a feature |
|
Returns a new dictionary with the same nested keys structure, where: |
|
Strips |
Classes
|
Implements the [.] operator with a backend caller. |
|
The missing type for objects that can be fetched from. |
- class ju.util.GetWithCaller(getter_func, default=Sentinel('NotSpecified'), handle_exceptions=(<class 'KeyError'>, ))[source]¶
Bases:
objectImplements the [.] operator with a backend caller.
>>> g = GetWithCaller(lambda x: x + 1) >>> g[4] 5
- class ju.util.Gettable(*args, **kwargs)[source]¶
Bases:
ProtocolThe missing type for objects that can be fetched from. The contract is that we can fetch an element from
objwith brackets:obj[k]. That is,objhas a__getitem__method.>>> isinstance(3, Gettable) # 3 is not Gettable (can't do 3[...]) False
But
dict,list, andstrare Gettable:>>> isinstance([1, 2, 3], Gettable) True >>> isinstance({'foo': 'bar'}, Gettable) True >>> isinstance('foo', Gettable) True
Note that so are their types:
>>> all(isinstance(c, Gettable) for c in (list, dict, str)) True
- ju.util.ensure_callable_mapper(mapper, *, default=Sentinel('NotSpecified'))[source]¶
Will return a CallableMapper from the specification of a Mapper, which could be a Mapping itself, a callable, or (key, value) pairs.
Further, if default is specified, the mapper will return that default if the requested key is not found.
>>> mapping = {'a': 1, 'b': 2} >>> func = ensure_callable_mapper(mapping, default=3) >>> func('a') 1 >>> func('c') 3 >>> items = [('a', 1), ('b', 2)] >>> func = ensure_callable_mapper(items, default=3) >>> func('a') 1 >>> func('c') 3
- ju.util.ensure_mapping_mapper(mapper, *, default=Sentinel('NotSpecified'))[source]¶
Will return a MappingMapper (a dict really) from the specification of a Mapper, which could be a Mapping itself, a callable, or (key, value) pairs.
Further, if default is specified, the mapper will return that default if the requested key is not found.
>>> func = lambda k: {'a': 1, 'b': 2}[k] >>> d = ensure_mapping_mapper(func, default=3) >>> d['a'] 1 >>> d['c'] 3 >>> items = [('a', 1), ('b', 2)] >>> d = ensure_mapping_mapper(items, default=3) >>> d['a'] 1 >>> d['c'] 3
- ju.util.feature_based_search(feature_processor_pairs, feature_similarity, default)[source]¶
Returns a function that searches for a feature in a list of feature_processor_pairs
- ju.util.feature_similarity_search(obj, *, featurizer, feature_based_search, feature_output_pairs, feature_similarity, similarity_base_match=<function <lambda>>)[source]¶
Returns the output of a feature based on a featurizer and a list of feature_output_pairs
- ju.util.feature_switch(obj, *, featurizer, feature_to_output_mapping, default)[source]¶
Returns the output of a feature based on a featurizer and a mapping
- ju.util.is_type(param, type_)[source]¶
Checks if the type of a parameter’s default value or its annotation matches a given type.
This function handles both regular types and subscripted generics.
- Parameters:
param (
Parameter) – The parameter to check.type_ (
Union[type,GenericAlias,Any]) – The type to check against.
- Returns:
True if the parameter’s type matches the given type, False otherwise.
- Return type:
Doctests:
>>> from inspect import Parameter >>> param = Parameter('p', Parameter.KEYWORD_ONLY, default=3.14) >>> is_type(param, float) True >>> is_type(param, int) False >>> param = Parameter('p', Parameter.KEYWORD_ONLY, default=[1, 2, 3]) >>> is_type(param, list) True >>> from typing import List, Union >>> is_type(param, List[int]) True >>> is_type(param, List[str]) False >>> is_type(param, Union[int, List[int]]) True
A parameterized generic built from an
collections.abctype (e.g.Sequence[str]) is itself an instance oftype– unliketypinggenerics such asList[str]– so it must still fall through to the__origin__-based branch below rather than being handed toisinstance(param.default, type_), which raisesTypeErrorfor a parameterized generic:>>> from collections.abc import Sequence >>> is_type(param, Sequence[int]) True >>> is_type(param, Sequence[str]) False
An annotation alone is enough, bare or subscripted, optional or not – it matches
type_or any subclass of it:>>> annotated = Parameter('p', Parameter.KEYWORD_ONLY, annotation=list[int]) >>> is_type(annotated, Sequence) True >>> is_type(annotated, list) True >>> is_type(annotated, List[int]) True >>> is_type(annotated, List[str]) False >>> is_type(annotated, str) False >>> optional = Parameter('p', Parameter.KEYWORD_ONLY, annotation=list | None) >>> is_type(optional, Sequence) True
- ju.util.resolve_annotations(func, parameters)[source]¶
Returns
parameters(a mapping or iterable ofParameter) with the string annotations offuncevaluated – all of them underfrom __future__ import annotations. An annotation that can’t be resolved (e.g. a name only imported underTYPE_CHECKING) is left as it was.>>> def f(a: "int", b: "list[float]" = None, c: "Nope" = 1): ... >>> sig = inspect.signature(f) >>> [(n, p.annotation) for n, p in resolve_annotations(f, sig.parameters).items()] [('a', <class 'int'>), ('b', list[float]), ('c', 'Nope')]
- ju.util.switch_case(mapping, default)[source]¶
Returns a function that switches between cases based on a feature
- ju.util.truncate_dict_values(d, *, max_list_size=2, max_string_size=66, middle_marker='...')[source]¶
Returns a new dictionary with the same nested keys structure, where:
List values are reduced to a maximum size of max_list_size.
String values longer than max_string_size are truncated in the middle.
Parameters
d (dict): The input dictionary. max_list_size (int, optional): Maximum size for lists. Defaults to 2. max_string_size (int, optional): Maximum length for strings. Defaults to None (no truncation). middle_marker (str, optional): String to insert in the middle of truncated strings. Defaults to ‘…’.
Returns
dict: A new dictionary with truncated lists and strings.
This can be useful when you have a large dictionary that you want to investigate, but printing/logging it takes too much space.
Example
>>> large_dict = {'a': [1, 2, 3, 4, 5], 'b': {'c': [6, 7, 8, 9], 'd': 'A string like this that is too long'}, 'e': [10, 11]} >>> truncate_dict_values(large_dict, max_list_size=3, max_string_size=20) {'a': [1, 2, 3], 'b': {'c': [6, 7, 8], 'd': 'A string...too long'}, 'e': [10, 11]}
You can use
Noneto indicate “no max”:>>> assert ( ... truncate_dict_values(large_dict, max_list_size=None, max_string_size=None) ... == large_dict ... )
- Return type:
- ju.util.unwrap_optional(annotation)[source]¶
Strips
Noneout of anOptional/union annotation; returns the annotation unchanged if it is not a union, and the single remaining member if there is one.AnnotatedandNewTypewrappers are peeled off too (also on the members).>>> from typing import Optional, Annotated, NewType >>> unwrap_optional(Optional[list[int]]) list[int] >>> unwrap_optional(int | None) <class 'int'> >>> unwrap_optional(int | str | None) typing.Union[int, str] >>> unwrap_optional(list[int]) list[int] >>> unwrap_optional(Annotated[Optional[list[int]], "meta"]) list[int] >>> unwrap_optional(Optional[NewType("UserId", int)]) <class 'int'>