# i2.util

Misc util objects

### Functions

| [`FileLikeObject`](#i2.util.FileLikeObject)(file, \*[, io_cls, open_mode])     | Context manager for file-like objects.                                                                                                                            |
|----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`asis`](#i2.util.asis)(x)                                           | The identity function: f(x) := x (takes only one argument, and returns it).                                                                                       |
| [`copy_func`](#i2.util.copy_func)(func, \*[, copy_dict, code, globals_])  | Make a (shallow) copy of a function.                                                                                                                              |
| [`deprecation_of`](#i2.util.deprecation_of)(func, old_name)                    | Wrap `func` so that each call emits a DeprecationWarning naming `old_name`.                                                                                       |
| [`dflt_idx_preprocessor`](#i2.util.dflt_idx_preprocessor)(obj, idx)                   | Get `idx` from `obj`: by item for ints, digit strings and Mappings, else by attribute.                                                                            |
| [`dp_get`](#i2.util.dp_get)(d, dot_path)                               | Get stuff from a dict (or any Mapping), using dot_paths (i.e. 'foo.bar' instead of ['foo']['bar']).                                                               |
| [`ensure_identifiers`](#i2.util.ensure_identifiers)(\*objs[, get_identfiers, ...]) | Ensure an iterable of identifiers                                                                                                                                 |
| [`get_app_folder`](#i2.util.get_app_folder)([folder_kind])                     | Get the full path of a directory suitable for storing application-specific configs, (or data, or cache, or state or runtime)                                      |
| [`get_function_body`](#i2.util.get_function_body)(func)                           | Get the body of a function as a (dedented) string, from its source code.                                                                                          |
| [`ignore_exception`](#i2.util.ignore_exception)(x)                               | The identity function: f(x) := x (takes only one argument, and returns it).                                                                                       |
| [`inject_method`](#i2.util.inject_method)(self, method_function[, ...])       | Inject a method into an object instance (binding the function to it).                                                                                             |
| [`insert_name_based_objects_in_scope`](#i2.util.insert_name_based_objects_in_scope)(\*names, ...)  | Make several string-parametrized objects and insert them in a scope (e.g. locals()).                                                                              |
| [`inspect_formatargspec`](#i2.util.inspect_formatargspec)(args[, varargs, ...])       | Copy formatargspec from python 3.7 standard library.                                                                                                              |
| [`is_lambda`](#i2.util.is_lambda)(func)                                   | Whether `func` is a lambda (its `__name__` is `"<lambda>"`).                                                                                                      |
| [`lambda_code`](#i2.util.lambda_code)(lambda_func)                          | Extract code of expression from lambda function.                                                                                                                  |
| [`mk_sentinel`](#i2.util.mk_sentinel)(name[, boolean_value, repr_, module]) | Creates and returns a new **instance** of a new class, suitable for usage as a "sentinel" since it is a kind of singleton (there can be only one instance of it.) |
| [`name_of_obj`](#i2.util.name_of_obj)(o, \*[, base_name_of_obj, ...])       | Tries to find the (or "a") name for an object, even if `__name__` doesn't exist.                                                                                  |
| [`path_extractor`](#i2.util.path_extractor)(tree, path[, getter, path_sep])    | Get items from a tree-structured object from a sequence of tree-traversal indices.                                                                                |
| [`register_object`](#i2.util.register_object)([obj, name])                      | Register an object (e.g. function, class) in the global registry.                                                                                                 |
| [`return_false`](#i2.util.return_false)(\*args, \*\*kwargs)                  | Return False, whatever the arguments.                                                                                                                             |
| [`return_none`](#i2.util.return_none)(\*args, \*\*kwargs)                   | Return None, whatever the arguments.                                                                                                                              |
| [`return_true`](#i2.util.return_true)(\*args, \*\*kwargs)                   | Return True, whatever the arguments.                                                                                                                              |

### Classes

| [`AttributeMapping`](#i2.util.AttributeMapping)                             | A read-only mapping with attribute access.                                                                                                                                                                         |
|-----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`AttributeMutableMapping`](#i2.util.AttributeMutableMapping)                      | A mutable mapping that provides both attribute and dictionary-style access.                                                                                                                                        |
| [`ConditionalExceptionCatcher`](#i2.util.ConditionalExceptionCatcher)(exception_types) | Context manager to catch exceptions of a certain type and instance condition.                                                                                                                                      |
| [`FolderSpec`](#i2.util.FolderSpec)(env_var, default_path)            |                                                                                                                                                                                                                    |
| [`FrozenDict`](#i2.util.FrozenDict)                                   | An immutable dict subtype that is hashable and can itself be used as a [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) key or [`set`](https://docs.python.org/3/builtins/stdtypes.html#set) entry. |
| [`FunctionBuilder`](#i2.util.FunctionBuilder)(name, \*\*kw)                | The FunctionBuilder type provides an interface for programmatically creating new functions, either based on existing functions or from scratch.                                                                    |
| [`LiteralVal`](#i2.util.LiteralVal)(val)                              | An object to indicate that the value should be considered literally.                                                                                                                                               |
| [`NoDefault`](#i2.util.NoDefault)()                                  | Type of the `no_default` sentinel, marking the absence of a default value.                                                                                                                                         |
| [`PicklableLambda`](#i2.util.PicklableLambda)(func[, name])                | Wraps a lambda function to make it picklable (through extracting its code) Also, provide it with a name, optionally.                                                                                               |
| [`frozendict`](#i2.util.frozendict)                                   |                                                                                                                                                                                                                    |
| [`imdict`](#i2.util.imdict)                                       | A dict whose mutating methods raise `TypeError`, hashable by identity.                                                                                                                                             |
| [`lazyprop`](#i2.util.lazyprop)(func)                               | A descriptor implementation of lazyprop (cached property) from David Beazley's "Python Cookbook" book.                                                                                                             |

### Exceptions

| [`ExistingArgument`](#i2.util.ExistingArgument)    | Raised by `FunctionBuilder.add_arg` when the argument name is already taken.     |
|----------------------------------------------------------------------|----------------------------------------------------------------------------------|
| [`FrozenHashError`](#i2.util.FrozenHashError)     | Raised (and cached) when a `frozendict` holds an unhashable value and is hashed. |
| [`MissingArgument`](#i2.util.MissingArgument)     | Raised by `FunctionBuilder.remove_arg` when the argument is not in the function. |
| [`OverwritesForbidden`](#i2.util.OverwritesForbidden) | Raise when a user is not allowed to overwrite a mapping's key                    |

### *class* i2.util.AttributeMapping

Bases: [`SimpleNamespace`](https://docs.python.org/3/library/types.html#types.SimpleNamespace), [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]

A read-only mapping with attribute access.

Useful when you want mapping interface but don’t need mutation.

**Examples**

```pycon
>>> ns = AttributeMapping(x=10, y=20)
>>> ns.x
10
>>> ns['y']
20
>>> list(ns)
['x', 'y']
```

#### *classmethod* from_mapping(mapping)

Create an AttributeMapping from a regular mapping.

This is useful when you want to convert a dictionary or other mapping
into an AttributeMapping for attribute-style access.

* **Return type:**
  [`AttributeMapping`](#i2.util.AttributeMapping)

### *class* i2.util.AttributeMutableMapping

Bases: [`AttributeMapping`](#i2.util.AttributeMapping), [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]

A mutable mapping that provides both attribute and dictionary-style access.

Extends AttributeMapping with mutation capabilities,
ensuring proper error handling and protocol compliance.

**Examples**

```pycon
>>> ns = AttributeMutableMapping(apple=1, banana=2)
>>> ns.apple
1
>>> ns['banana']
2
>>> ns['cherry'] = 3
>>> ns.cherry
3
>>> list(ns)
['apple', 'banana', 'cherry']
>>> len(ns)
3
>>> 'apple' in ns
True
>>> del ns['banana']
>>> 'banana' in ns
False
```

### *class* i2.util.ConditionalExceptionCatcher(exception_types, exception_condition=<function return_true>, handlers=<function asis>, \*, prevent_propagation=True)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Context manager to catch exceptions of a certain type and instance condition.

* **Parameters:**
  * **exception_types** (`Union`[[`BaseException`](https://docs.python.org/3/builtins/exceptions.html#BaseException), [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`BaseException`](https://docs.python.org/3/builtins/exceptions.html#BaseException)]]) – The type of exception to catch. Can be a single exception
    type or a tuple of exception types.
  * **exception_condition** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`BaseException`](https://docs.python.org/3/builtins/exceptions.html#BaseException)], [`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – A function that takes an exception instance and returns
    a “key” value indicating whether the exception should be caught.
    If the bool(key) is True, the exception is caught.
    If the bool(key) is False, the exception is not caught.
    The key can further be used to determine the handler to use, when the handlers
    argument is a mapping.
    The default is to catch all exceptions of the specified type(s).
  * **handlers** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`BaseException`](https://docs.python.org/3/builtins/exceptions.html#BaseException)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Specification of how to handle the exceptions. Can be a single
    function to run on the exception object when an exception of the specified
    type is caught, or a mapping (e.g. dict) of handler functions, keyed by the
    key returned by the exception_condition function.
  * **prevent_propagation** – Whether to prevent the exception from propagating. Defaults
    to `True`.

**Example**

```pycon
>>> exception_catcher = ConditionalExceptionCatcher(
...     ValueError, lambda e: e.args[0] == 'foo', handlers=print
... )
>>> with exception_catcher:
...     raise ValueError('foo')
foo
>>> with exception_catcher:
...     raise TypeError('foo')
Traceback (most recent call last):
    ...
TypeError: foo
>>> with exception_catcher:
...     raise ValueError('bar')
Traceback (most recent call last):
    ...
ValueError: bar
```

### *exception* i2.util.ExistingArgument

Bases: [`ValueError`](https://docs.python.org/3/builtins/exceptions.html#ValueError)

Raised by `FunctionBuilder.add_arg` when the argument name is already taken.

### i2.util.FileLikeObject(file, \*, io_cls=<class '_io.BytesIO'>, open_mode='rb')

Context manager for file-like objects.

The purpose of this context manager is to be able to ensure we have a file-like
object interface to work with, regardless of whether we are given a file path,
bytes of a file, or an open file pointer.

* **Parameters:**
  * **file** – The file path, bytes of a file, or an open file pointer.
  * **io_cls** – Accepted for interface compatibility; not used by the current
    implementation (bytes are always wrapped in `io.BytesIO`).
  * **open_mode** – The mode `open` is called with when `file` is a path.
* **Yields:**
  A file-like object.

### *class* i2.util.FolderSpec(env_var, default_path)

Bases: [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)

#### default_path

Alias for field number 1

#### env_var

Alias for field number 0

### *class* i2.util.FrozenDict

Bases: [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

An immutable dict subtype that is hashable and can itself be used
as a [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) key or [`set`](https://docs.python.org/3/builtins/stdtypes.html#set) entry. What
[`frozenset`](https://docs.python.org/3/builtins/stdtypes.html#frozenset) is to [`set`](https://docs.python.org/3/builtins/stdtypes.html#set), FrozenDict is to
[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict).

There was once an attempt to introduce such a type to the standard
library, but it was rejected: [PEP 416](https://www.python.org/dev/peps/pep-0416/).

Because FrozenDict is a [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) subtype, it automatically
works everywhere a dict would, including JSON serialization.

#### clear(\*a, \*\*kw)

raises a TypeError, because FrozenDicts are immutable

#### *classmethod* fromkeys(keys, value=None)

Create a new dictionary with keys from iterable and values set to value.

#### pop(\*a, \*\*kw)

raises a TypeError, because FrozenDicts are immutable

#### popitem(\*a, \*\*kw)

raises a TypeError, because FrozenDicts are immutable

#### setdefault(\*a, \*\*kw)

raises a TypeError, because FrozenDicts are immutable

#### update(\*a, \*\*kw)

raises a TypeError, because FrozenDicts are immutable

#### updated(\*a, \*\*kw)

Make a copy and add items from a dictionary or iterable (and/or
keyword arguments), overwriting values under an existing
key. See [`dict.update()`](https://docs.python.org/3/builtins/stdtypes.html#dict.update) for more details.

### *exception* i2.util.FrozenHashError

Bases: [`TypeError`](https://docs.python.org/3/builtins/exceptions.html#TypeError)

Raised (and cached) when a `frozendict` holds an unhashable value and is hashed.

### *class* i2.util.FunctionBuilder(name, \*\*kw)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The FunctionBuilder type provides an interface for programmatically
creating new functions, either based on existing functions or from
scratch.

#### NOTE
Based on [https://boltons.readthedocs.io](https://boltons.readthedocs.io)

Values are passed in at construction or set as attributes on the
instance. For creating a new function based of an existing one,
see the [`from_func()`](#i2.util.FunctionBuilder.from_func) classmethod. At any
point, [`get_func()`](#i2.util.FunctionBuilder.get_func) can be called to get a
newly compiled function, based on the values configured.

```pycon
>>> fb = FunctionBuilder('return_five', doc='returns the integer 5',
...                      body='return 5')
>>> f = fb.get_func()
>>> f()
5
>>> fb.varkw = 'kw'
>>> f_kw = fb.get_func()
>>> f_kw(ignored_arg='ignored_val')
5
```

Note that function signatures themselves changed quite a bit in
Python 3, so several arguments are only applicable to
FunctionBuilder in Python 3. Except for *name*, all arguments to
the constructor are keyword arguments.

* **Parameters:**
  * **name** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name of the function.
  * **doc** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – [Docstring](https://en.wikipedia.org/wiki/Docstring#Python) for the function, defaults to empty.
  * **module** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name of the module from which this function was
    imported. Defaults to None.
  * **body** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – String version of the code representing the body
    of the function. Defaults to `'pass'`, which will result
    in a function which does nothing and returns `None`.
  * **args** ([*list*](https://docs.python.org/3/builtins/stdtypes.html#list)) – List of argument names, defaults to empty list,
    denoting no arguments.
  * **varargs** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name of the catch-all variable for positional
    arguments. E.g., “args” if the resultant function is to have
    `*args` in the signature. Defaults to None.
  * **varkw** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – Name of the catch-all variable for keyword
    arguments. E.g., “kwargs” if the resultant function is to have
    `**kwargs` in the signature. Defaults to None.
  * **defaults** ([*tuple*](https://docs.python.org/3/builtins/stdtypes.html#tuple)) – A tuple containing default argument values for
    those arguments that have defaults.
  * **kwonlyargs** ([*list*](https://docs.python.org/3/builtins/stdtypes.html#list)) – Argument names which are only valid as
    keyword arguments. **Python 3 only.**
  * **kwonlydefaults** ([*dict*](https://docs.python.org/3/builtins/stdtypes.html#dict)) – A mapping, same as normal *defaults*,
    but only for the *kwonlyargs*. **Python 3 only.**
  * **annotations** ([*dict*](https://docs.python.org/3/builtins/stdtypes.html#dict)) – Mapping of type hints and so
    forth. **Python 3 only.**
  * **filename** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – The filename that will appear in
    tracebacks. Defaults to “boltons.funcutils.FunctionBuilder”.
  * **indent** ([*int*](https://docs.python.org/3/builtins/functions.html#int)) – Number of spaces with which to indent the
    function *body*. Values less than 1 will result in an error.
  * **dict** ([*dict*](https://docs.python.org/3/builtins/stdtypes.html#dict)) – Any other attributes which should be added to the
    functions compiled with this FunctionBuilder.

All of these arguments are also made available as attributes which
can be mutated as necessary.

#### add_arg(arg_name, default=Sentinel('NO_DEFAULT'), kwonly=False)

Add an argument with optional *default* (defaults to
`funcutils.NO_DEFAULT`). Pass *kwonly=True* to add a
keyword-only argument

#### *classmethod* from_func(func)

Create a new FunctionBuilder instance based on an existing
function. The original function will not be stored or
modified.

#### get_defaults_dict()

Get a dictionary of function arguments with defaults and the
respective values.

#### get_func(execdict=None, add_source=True, with_dict=True)

Compile and return a new function based on the current values of
the FunctionBuilder.

* **Parameters:**
  * **execdict** ([*dict*](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The dictionary representing the scope in
    which the compilation should take place. Defaults to an empty
    dict.
  * **add_source** ([*bool*](https://docs.python.org/3/builtins/functions.html#bool)) – Whether to add the source used to a
    special `__source__` attribute on the resulting
    function. Defaults to True.
  * **with_dict** ([*bool*](https://docs.python.org/3/builtins/functions.html#bool)) – Add any custom attributes, if
    applicable. Defaults to True.

To see an example of usage, see the implementation of
`wraps()`.

#### get_sig_str(with_annotations=True)

Return function signature as a string.

with_annotations is ignored on Python 2.  On Python 3 signature
will omit annotations if it is set to False.

#### remove_arg(arg_name)

Remove an argument from this FunctionBuilder’s argument list. The
resulting function will have one less argument per call to
this function.

* **Parameters:**
  **arg_name** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – The name of the argument to remove.

Raises a [`ValueError`](https://docs.python.org/3/builtins/exceptions.html#ValueError) if the argument is not present.

### *class* i2.util.LiteralVal(val)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

An object to indicate that the value should be considered literally.

```pycon
>>> t = LiteralVal(42)
>>> t.get_val()
42
>>> t()
42
```

#### get_val()

Get the value wrapped by Literal instance.

One might want to use `literal.get_val()` instead `literal()` to get the
value a `Literal` is wrapping because `.get_val` is more explicit.

That said, with a bit of hesitation, we allow the `literal()` form as well
since it is useful in situations where we need to use a callback function to
get a value.

### *exception* i2.util.MissingArgument

Bases: [`ValueError`](https://docs.python.org/3/builtins/exceptions.html#ValueError)

Raised by `FunctionBuilder.remove_arg` when the argument is not in the function.

### *class* i2.util.NoDefault

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Type of the `no_default` sentinel, marking the absence of a default value.

### *exception* i2.util.OverwritesForbidden

Bases: [`ValueError`](https://docs.python.org/3/builtins/exceptions.html#ValueError)

Raise when a user is not allowed to overwrite a mapping’s key

### *class* i2.util.PicklableLambda(func, name=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Wraps a lambda function to make it picklable (through extracting its code)
Also, provide it with a name, optionally.

```pycon
>>> f = lambda x, y=0: x + y
>>> ff = PicklableLambda(f)
>>> import pickle
>>> fff = pickle.loads(pickle.dumps(ff))
>>> assert fff(2, 3) == ff(2, 3) == f(2, 3)
```

For lambda code-extraction see:
[https://stackoverflow.com/questions/73980648/how-to-transform-a-lambda-function-into-a-pickle-able-function](https://stackoverflow.com/questions/73980648/how-to-transform-a-lambda-function-into-a-pickle-able-function)

### i2.util.asis(x)

The identity function: f(x) := x (takes only one argument, and returns it).

* **Return type:**
  [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`T`)

```pycon
>>> asis(3)
3
```

### i2.util.copy_func(func, , copy_dict=True, code=None, globals_=None)

Make a (shallow) copy of a function.

```pycon
>>> f = lambda x, *, y=2: x * y
>>> f.an_attr = 42
>>> f_copy = copy_func(f)
>>> f_copy(3) == f(3) == 6
True
>>> f_copy.an_attr == f.an_attr == 42
True
```

Verify that making an attribute in one won’t create an attribute in the other:

```pycon
>>> f.another_attr = 42
>>> hasattr(f_copy, 'another_attr')
False
>>> f_copy.yet_another_attr = 84
>>> hasattr(f, 'yet_another_attr')
False
```

* **Parameters:**
  * **func** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)) – The function to be copied.
  * **copy_dict** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Indicates whether to copy the `__dict__` attribute of the
    function (any attributes set on the function instance). Defaults to `True`.
  * **code** – The value to be used as the `__code__` attribute of the copy.
  * **globals_** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The value to be used as the `__globals__` attribute of the copy.
* **Returns:**
  A shallow copy of the function.

Note that it should always work with proper functions and attempts to do the
best job it can with other callables, but there are no guarantees on how
`copy_func` will behave with custom callables.

If these custom callables don’t have a `__code__` attribute, the copy will fail.
Furthermore, if the custom callable  doesn’t have `__globals__`, the empty
dictionary will be used as the globals.
We provide a `code` and `globals` argument to allow the user to provide
the `__code__` and `__globals__` attributes of the function to be copied.

### i2.util.deprecation_of(func, old_name)

Wrap `func` so that each call emits a DeprecationWarning naming `old_name`.

Bind the result to the old name to keep it importable while pointing users to `func`.

### i2.util.dflt_idx_preprocessor(obj, idx)

Get `idx` from `obj`: by item for ints, digit strings and Mappings, else by attribute.

The default `getter` of `path_extractor`.

```pycon
>>> dflt_idx_preprocessor({"a": 1}, "a"), dflt_idx_preprocessor([10, 20], "1")
(1, 20)
```

* **Raises:**
  [**KeyError**](https://docs.python.org/3/builtins/exceptions.html#KeyError) – If `idx` is neither an item nor an attribute of `obj`.

### i2.util.dp_get(d, dot_path)

Get stuff from a dict (or any Mapping), using dot_paths (i.e. ‘foo.bar’ instead of
[‘foo’][‘bar’]).

```pycon
>>> d = {'foo': {'bar': 2, 'alice': 'bob'}, 3: {'pi': 3.14}}
>>> assert dp_get(d, 'foo') == {'bar': 2, 'alice': 'bob'}
>>> assert dp_get(d, 'foo.bar') == 2
>>> assert dp_get(d, 'foo.alice') == 'bob'
```

### i2.util.ensure_identifiers(\*objs, get_identfiers=<method 'split' of 'str' objects>, is_identifier=<method 'isidentifier' of 'str' objects>)

Ensure an iterable of identifiers

```pycon
>>> list(ensure_identifiers('these', 'are', 'valid', 'identifiers'))
['these', 'are', 'valid', 'identifiers']
```

By default, `ensure_identifiers` will apply `str.split` to each `obj` of
`objs` (assumed to be strings!) so that it can extract identifiers from
space-separated strings of identifiers:

```pycon
>>> list(ensure_identifiers('these are valid identifiers'))
['these', 'are', 'valid', 'identifiers']
```

You can control this functionality through the `get_identfiers` argument, for
example, disallowing such splitting, or enabling the extraction of identifiers
from other objects than strings.

```pycon
>>> list(ensure_identifiers(
...     {'this': 0, 'works': 1}, {'too': 2},
...     get_identfiers=list
... ))
['this', 'works', 'too']
```

You can also control the `is_identifier` validatation function:

```pycon
>>> def less_than_6_chars(s): return len(s) < 6
>>> list(ensure_identifiers('okay', 'too_long', is_identifier=less_than_6_chars))
Traceback (most recent call last):
  ...
ValueError: too_long isn't an identifier according toless_than_6_chars
```

### i2.util.frozendict

alias of [`FrozenDict`](#i2.util.FrozenDict)

### i2.util.get_app_config_folder(, folder_kind='config')

Get the full path of a directory suitable for storing application-specific configs,
(or data, or cache, or state or runtime)

On Windows, this is typically %APPDATA%.
On macOS, this is typically ~/.config.
On Linux, this is typically ~/.config.

* **Parameters:**
  **folder_kind** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – The kind of folder to get. One of ‘config’, ‘data’, ‘cache’, ‘state’, ‘runtime’.
  Defaults to ‘config’.
  Here are concise explanations for each folder kind:
  **config**: User preferences and settings files (e.g., API keys, theme preferences, editor settings). Files users might edit manually or that define how the app behaves.
  **data**: Essential user-created content and application state (e.g., databases, saved games, user documents, session files). Data that should be backed up and persists across updates.
  **cache**: Temporary, regeneratable files (e.g., downloaded images, compiled assets, web cache). Can be safely deleted to free space without losing user work.
  **state**: Application state and logs that persist between sessions but aren’t critical user data (e.g., command history, undo history, recently opened files, log files). Unlike cache, shouldn’t be auto-deleted.
  **runtime**: Temporary runtime files that only exist while the app runs (e.g., PID files, Unix sockets, lock files, named pipes). Typically cleared on logout/reboot.
  **TL;DR**: config = settings, data = user files, cache = disposable, state = logs/history, runtime = process files.
* **Returns:**
  The full path of the app data folder.
* **Return type:**
  [*str*](https://docs.python.org/3/builtins/stdtypes.html#str)

See [https://github.com/i2mint/i2mint/issues/1](https://github.com/i2mint/i2mint/issues/1).

### i2.util.get_app_data_folder(, folder_kind='data')

Get the full path of a directory suitable for storing application-specific configs,
(or data, or cache, or state or runtime)

On Windows, this is typically %APPDATA%.
On macOS, this is typically ~/.config.
On Linux, this is typically ~/.config.

* **Parameters:**
  **folder_kind** ([*str*](https://docs.python.org/3/builtins/stdtypes.html#str)) – The kind of folder to get. One of ‘config’, ‘data’, ‘cache’, ‘state’, ‘runtime’.
  Defaults to ‘config’.
  Here are concise explanations for each folder kind:
  **config**: User preferences and settings files (e.g., API keys, theme preferences, editor settings). Files users might edit manually or that define how the app behaves.
  **data**: Essential user-created content and application state (e.g., databases, saved games, user documents, session files). Data that should be backed up and persists across updates.
  **cache**: Temporary, regeneratable files (e.g., downloaded images, compiled assets, web cache). Can be safely deleted to free space without losing user work.
  **state**: Application state and logs that persist between sessions but aren’t critical user data (e.g., command history, undo history, recently opened files, log files). Unlike cache, shouldn’t be auto-deleted.
  **runtime**: Temporary runtime files that only exist while the app runs (e.g., PID files, Unix sockets, lock files, named pipes). Typically cleared on logout/reboot.
  **TL;DR**: config = settings, data = user files, cache = disposable, state = logs/history, runtime = process files.
* **Returns:**
  The full path of the app data folder.
* **Return type:**
  [*str*](https://docs.python.org/3/builtins/stdtypes.html#str)

See [https://github.com/i2mint/i2mint/issues/1](https://github.com/i2mint/i2mint/issues/1).

### i2.util.get_app_folder(folder_kind='config')

Get the full path of a directory suitable for storing application-specific configs,
(or data, or cache, or state or runtime)

On Windows, this is typically %APPDATA%.
On macOS, this is typically ~/.config.
On Linux, this is typically ~/.config.

* **Parameters:**
  **folder_kind** ([`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)[`'config'`, `'data'`, `'cache'`, `'state'`, `'runtime'`]) – The kind of folder to get. One of ‘config’, ‘data’, ‘cache’, ‘state’, ‘runtime’.
  Defaults to ‘config’.
  Here are concise explanations for each folder kind:
  **config**: User preferences and settings files (e.g., API keys, theme preferences, editor settings). Files users might edit manually or that define how the app behaves.
  **data**: Essential user-created content and application state (e.g., databases, saved games, user documents, session files). Data that should be backed up and persists across updates.
  **cache**: Temporary, regeneratable files (e.g., downloaded images, compiled assets, web cache). Can be safely deleted to free space without losing user work.
  **state**: Application state and logs that persist between sessions but aren’t critical user data (e.g., command history, undo history, recently opened files, log files). Unlike cache, shouldn’t be auto-deleted.
  **runtime**: Temporary runtime files that only exist while the app runs (e.g., PID files, Unix sockets, lock files, named pipes). Typically cleared on logout/reboot.
  **TL;DR**: config = settings, data = user files, cache = disposable, state = logs/history, runtime = process files.
* **Returns:**
  The full path of the app data folder.
* **Return type:**
  [*str*](https://docs.python.org/3/builtins/stdtypes.html#str)

See [https://github.com/i2mint/i2mint/issues/1](https://github.com/i2mint/i2mint/issues/1).

### i2.util.get_function_body(func)

Get the body of a function as a (dedented) string, from its source code.

Decorator lines and the `def` line(s) are dropped. Requires the source to be
available through `inspect` (not the case for functions defined in a REPL).

```pycon
>>> def f(x):
...     y = x + 1
...     return y * 2
>>> print(get_function_body(f))
y = x + 1
return y * 2
```

### i2.util.ignore_exception(x)

The identity function: f(x) := x (takes only one argument, and returns it).

* **Return type:**
  [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`T`)

```pycon
>>> asis(3)
3
```

### *class* i2.util.imdict

Bases: [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

A dict whose mutating methods raise `TypeError`, hashable by identity.

#### clear() → None.  Remove all items from D.

#### pop(k) → v, remove specified key and return the corresponding value.

If the key is not found, return the default if given; otherwise,
raise a KeyError.

#### popitem(\*args, \*\*kws)

Remove and return a (key, value) pair as a 2-tuple.

Pairs are returned in LIFO (last-in, first-out) order.
Raises KeyError if the dict is empty.

#### setdefault(\*args, \*\*kws)

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

#### update(\*\*F) → None.  Update D from mapping/iterable E and F.

If E is present and has a .keys() method, then does:  for k in E.keys(): D[k] = E[k]
If E is present and lacks a .keys() method, then does:  for k, v in E: D[k] = v
In either case, this is followed by: for k in F:  D[k] = F[k]

### i2.util.inject_method(self, method_function, method_name=None)

Inject a method into an object instance (binding the function to it).

`method_function` can be:

> * a function (the method name is `method_name`, or the function’s name)
> * a `{method_name: function, ...}` dict (for multiple injections)
> * a list of functions or `(function, method_name)` pairs

Returns the instance, mutated.

```pycon
>>> class A: ...
>>> a = A()
>>> def greet(self, name):
...     return f"hi {name} from {type(self).__name__}"
>>> _ = inject_method(a, greet)
>>> a.greet("bob")
'hi bob from A'
>>> _ = inject_method(a, {"shout": lambda self, s: s.upper()})
>>> a.shout("x")
'X'
```

### i2.util.insert_name_based_objects_in_scope(\*names, factory, scope, allow_overwrites=False)

Make several string-parametrized objects and insert them in a scope (e.g. locals()).

This is useful when to avoid (error-prone) situations where we want the name we
assign an object to, to be aligned with it’s internal name, such as:

```default
foo = Factory('foo', ...)
bar = Factory('bar', ...)
baz = Factory('baz', ...)
```

* **Parameters:**
  * **names** – Identifier (valid python variable name) strings.
    These are used both as arguments of the `factory` and as keys for the
    `scope` the object the factory makes will be inserted under.
  * **factory** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – A function that takes a (valid python identifier) string and
    returns an object parametrized by that string.
  * **scope** ([`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)) – The `MutableMapping` we want to insert the objects in.
  * **allow_overwrites** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the objects we create can overwrite existing
    objects the `scope` may already have. If we don’t allow overwrites and we
    try to write under an existing key, a `OverwritesForbidden` error will be
    raised. This also includes the situation where we have some duplicates in
    `names`.
* **Returns:**
  None (this function has the side effect of inserting items in `scope`.

One of the (controversal) uses of `insert_name_based_objects_in_scope` is to be
able to make several string-parametrized

```pycon
>>> from collections import namedtuple
>>> from functools import partial
>>>
>>> factory = partial(namedtuple, field_names='apple banana')
>>> insert_namedtuples_in_locals = partial(insert_name_based_objects_in_scope,
...     factory=factory, scope=locals(), allow_overwrites=True
... )
>>> insert_namedtuples_in_locals('foo bar', 'baz')
```

And now `foo` exists!

```pycon
>>> 'foo' in locals()
True
>>> foo(1,2)
foo(apple=1, banana=2)
```

And so does `bar` and `baz`:

```pycon
>>> bar(3, banana=4)
bar(apple=3, banana=4)
>>> baz(apple=3, banana=4)
baz(apple=3, banana=4)
```

### i2.util.inspect_formatargspec(args, varargs=None, varkw=None, defaults=None, kwonlyargs=(), kwonlydefaults={}, annotations={}, formatarg=<class 'str'>, formatvarargs=<function <lambda>>, formatvarkw=<function <lambda>>, formatvalue=<function <lambda>>, formatreturns=<function <lambda>>, formatannotation=<function formatannotation>)

Copy formatargspec from python 3.7 standard library.
Python 3 has deprecated formatargspec and requested that Signature
be used instead, however this requires a full reimplementation
of formatargspec() in terms of creating Parameter objects and such.
Instead of introducing all the object-creation overhead and having
to reinvent from scratch, just copy their compatibility routine.

### i2.util.is_lambda(func)

Whether `func` is a lambda (its `__name__` is `"<lambda>"`).

### i2.util.lambda_code(lambda_func)

Extract code of expression from lambda function.
For lambda code-extraction see:
[https://stackoverflow.com/questions/73980648/how-to-transform-a-lambda-function-into-a-pickle-able-function](https://stackoverflow.com/questions/73980648/how-to-transform-a-lambda-function-into-a-pickle-able-function)

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### *class* i2.util.lazyprop(func)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A descriptor implementation of lazyprop (cached property) from David Beazley’s “Python Cookbook” book.
It’s

```pycon
>>> class Test:
...     def __init__(self, a):
...         self.a = a
...     @lazyprop
...     def len(self):
...         print('generating "len"')
...         return len(self.a)
>>> t = Test([0, 1, 2, 3, 4])
>>> t.__dict__
{'a': [0, 1, 2, 3, 4]}
>>> t.len
generating "len"
5
>>> t.__dict__
{'a': [0, 1, 2, 3, 4], 'len': 5}
>>> t.len
5
>>> # But careful when using lazyprop that no one will change the value of a without deleting the property first
>>> t.a = [0, 1, 2]  # if we change a...
>>> t.len  # ... we still get the old cached value of len
5
>>> del t.len  # if we delete the len prop
>>> t.len  # ... then len being recomputed again
generating "len"
3
```

### i2.util.mk_sentinel(name, boolean_value=False, repr_=<function \_default_sentinel_repr_method>, \*, module=None)

Creates and returns a new **instance** of a new class, suitable for usage as a
“sentinel” since it is a kind of singleton (there can be only one instance of it.)

A frequent use case for sentinels are where we want to indicate that something is
missing. Often, we use `None` for this, but sometimes `None` is a valid value in
our context (see for example the `inspect.Parameter.empty` sentinel to indicate
that an argument doesn’t have a default or annotation).
Other times, we may want to distinguish different kinds of “nothing”.

`mk_sentinel` can help you create such sentinels, takes care of annoying details
like pickability and allows you to control how to resolve your sentinel to a boolean.

* **Parameters:**
  * **name** – The name of your sentinel. Will be used for `__name__` attribute.
  * **boolean_value** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – The boolean value that the sentinel instance should resolve to.
  * **repr_** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)) – The method or string that should be used for the repr.
  * **module** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – The `__module__` to give the sentinel’s class (needed for
    pickling). By default it is taken from the calling frame’s `__name__`.
* **Returns:**
  A sentinel instance

```pycon
>>> Empty = mk_sentinel('Empty')
>>> Empty
Sentinel('Empty')
```

By default, the boolean resolution of a sentinel is `False`. Meaning:

```pycon
>>> Nothing = mk_sentinel('Nothing')
>>> bool(Nothing)
False
```

This is consistent with `None`, so that you can check that an object `x` is not
`Nothing` by doing `if x: ...` or idioms like:

```pycon
>>> x = Nothing
>>> x = x or 'default'
>>> x
'default'
```

(Though note that in situations where other elements that cast to `False` are
valid values for `x` (like `0`, `None`, or `False` itself), it’s safer to use
`if x is not Nothing: ...`.)

Anyway, I digress.
Point is that in some situations, the semantics  or usage of your sentinel is better
align with True. You can control what the boolean resolution of your
sentinel should be through the `boolean_value` argument:

```pycon
>>> Empty = mk_sentinel('Empty', boolean_value=True)
>>> bool(Empty)
True
```

You can also control what you see in the repr, specifying a string value;

```pycon
>>> Empty = mk_sentinel('undefined', repr_='undefined')
>>> Empty
undefined
```

or a method;

```pycon
>>> Empty = mk_sentinel('Empty', repr_=lambda self: f"<{self.__name__}>")
>>> Empty
<Empty>
```

And yes, even though we used a lambda here, it’s still picklable:

```pycon
>>> import pickle
```

```pycon
>>> Empty = mk_sentinel('Empty', repr_='Empty', module=__name__)
>>> pickle.loads(pickle.dumps(Empty))
Empty
```

Talking about pickle, here’s some more info on that:

```pycon
>>> unpickled_Empty = pickle.loads(pickle.dumps(Empty))
>>> # The unpickled version is "equal" to the original:
>>> unpickled_Empty == Empty
True
>>> # the types are the same too:
>>> type(unpickled_Empty) == type(Empty)
True
>>>
>>>
```

Note that though two sentinels might have the same name, they’re not equal:

```pycon
>>> Empty = mk_sentinel('Empty')
>>> AnotherEmptyWithSameName = mk_sentinel('Empty')
>>> Empty
Sentinel('Empty')
>>> AnotherEmptyWithSameName
Sentinel('Empty')
>>> # but...
>>> AnotherEmptyWithSameName == Empty
False
>>> # Note even the types are the same!
>>> type(AnotherEmptyWithSameName) == type(Empty)
False
```

One thing that makes the pickle work is that we took care of sticking in a
`__module__` for you. `mk_sentinel` figures this out by some dark magic
involving looking into the system’s “frames” etc. This may not always work since
some systems (e.g. `pypy`) may use different “under-the-hood” methods.

But if you want to control the value of `__module__` yourself, you can, simply
but indicating what the module of the sentinel is.
Usually, you’ll just specify it as `module=__name__`, which will stick the
name of the module you’re defining the sentinel in for you!

```pycon
>>> MySentinel = mk_sentinel('MySentinel', module=__name__)
```

Thanks: Inspired greately from the `make_sentinel` function of `boltons`:
See [https://boltons.readthedocs.io/](https://boltons.readthedocs.io/).

### i2.util.name_of_obj(o, \*, base_name_of_obj=operator.attrgetter('_\_name_\_'), caught_exceptions=(<class 'AttributeError'>, ), default_factory=<function return_none>)

Tries to find the (or “a”) name for an object, even if `__name__` doesn’t exist.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)

```pycon
>>> name_of_obj(map)
'map'
>>> name_of_obj([1, 2, 3])
'list'
>>> name_of_obj(print)
'print'
>>> name_of_obj(lambda x: x)
'<lambda>'
>>> from functools import partial
>>> name_of_obj(partial(print, sep=","))
'print'
>>> from functools import cached_property
>>> class A:
...     @property
...     def prop(self):
...         return 1.0
...     @cached_property
...     def cached_prop(self):
...         return 2.0
>>> name_of_obj(A.prop)
'prop'
>>> name_of_obj(A.cached_prop)
'cached_prop'
```

Note that `name_of_obj` uses the `__name__` attribute as its base way to get
a name. You can customize this behavior though.
For example, see that:

```pycon
>>> from inspect import Signature
>>> name_of_obj(Signature.replace)
'replace'
```

If you want to get the fully qualified name of an object, you can do:

```pycon
>>> alt = partial(name_of_obj, base_name_of_obj=attrgetter('__qualname__'))
>>> alt(Signature.replace)
'Signature.replace'
```

### i2.util.path_extractor(tree, path, getter=<function dflt_idx_preprocessor>, \*, path_sep='.')

Get items from a tree-structured object from a sequence of tree-traversal indices.

* **Parameters:**
  * **tree** – The object you want to extract values from:
    Can be any object you want, as long as the indices listed by path and how to get
    the items indexed are well specified by `path` and `getter`.
  * **path** – An iterable of indices that define how to traverse the tree to get
    to desired item(s). If this iterable is a string, the `path_sep` argument
    will be used to transform it into a tuple of string indices.
  * **getter** – A `(tree, idx)` function that specifies how to extract item `idx`
    from the `tree` object.
  * **path_sep** – The string separator to use if `path` is a string
* **Returns:**
  The `tree` item(s) referenced by `path`

```pycon
>>> tree = {'a': {'b': [0, {'c': [1, 2, 3]}]}}
>>> path_extractor(tree, path=['a'])
{'b': [0, {'c': [1, 2, 3]}]}
>>> path_extractor(tree, path=['a', 'b'])
[0, {'c': [1, 2, 3]}]
>>> path_extractor(tree, path=['a', 'b', 1])
{'c': [1, 2, 3]}
>>> path_extractor(tree, path=['a', 'b', 1, 'c'])
[1, 2, 3]
>>> path_extractor(tree, path=('a', 'b', 1, 'c', 2))
3
```

You could do the same by specifying the path as a dot-separated string.

```pycon
>>> path_extractor(tree, 'a.b.1.c.2')
3
```

You can use any separation you want.

```pycon
>>> path_extractor(tree, 'a/b/1/c/2', path_sep='/')
3
```

You can also use `*` to indicate that you want to keep all the nodes of a given
level.

```pycon
>>> tree = {'a': [{'b': [1, 10]}, {'b': [2, 20]}, {'b': [3, 30]}]}
>>> path_extractor(tree, 'a.*.b.1')
[10, 20, 30]
```

A generalization of `*` is to specify a callable which will be intepreted as
a filter function.

```pycon
>>> tree = {'a': [{'b': 1}, {'c': 2}, {'b': 3}, {'b': 4}]}
>>> path_extractor(tree, ['a', lambda x: 'b' in x])
[{'b': 1}, {'b': 3}, {'b': 4}]
>>> path_extractor(tree, ['a', lambda x: 'b' in x, 'b'])
[1, 3, 4]
```

### i2.util.register_object(obj=None, name=None, , registry)

Register an object (e.g. function, class) in the global registry.

The raw use is to define a registry Mapping and then call this function with the registry and the object to register.

```pycon
>>> registry = {}
>>> def wet():
...     pass
>>> register_object(wet, registry=registry)
<function wet at 0x...>
>>> registry
{'wet': <function wet at 0x...>}
```

```pycon
>>> register_object(wet, name='custom_name', registry=registry)
<function wet at 0x...>
>>> registry
{'wet': <function wet at 0x...>, 'custom_name': <function wet at 0x...>}
```

The most common use of this function is to use it as a decorator with a fixed (but mutable!) registry:

```pycon
>>> another_registry = {}
>>> register_to_another = register_object(registry=another_registry)
>>> @register_to_another
... def dry():
...     pass
>>> another_registry
{'dry': <function dry at 0x...>}
```

```pycon
>>> @register_to_another('DRY')
... def foo():
...     pass
>>> another_registry
{'dry': <function dry at 0x...>, 'DRY': <function foo at 0x...>}
```

### i2.util.return_false(\*args, \*\*kwargs)

Return False, whatever the arguments.

```pycon
>>> return_false(1, x=2)
False
```

### i2.util.return_none(\*args, \*\*kwargs)

Return None, whatever the arguments.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### i2.util.return_true(\*args, \*\*kwargs)

Return True, whatever the arguments.

```pycon
>>> return_true(1, x=2)
True
```
