> built 2026-09-22 15:20 UTC from 14bd29d (master) · i2 0.1.74. Details: build_info.json

# index.html.md

<!-- generated by epythet -->

# i2

Core tools for minting code.

For human readers: [Documentation here.](https://i2mint.github.io/i2/)

<!-- epythet:agentic-readme:start -->

## For AI agents

`i2` ships tooling for coding agents. If you are one, start here.

**Skills** ([Agent Skills](https://agentskills.io) format), for any agent host.

| Skill               | Use it to                                                                                                                                |
|---------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| `i2-castgraph`      | routing data through a graph of type/representation conversions with i2.castgraph’s TransformationGraph — register pairwise transformers |
| `i2-multi-object`   | composing and combining a fixed collection of functions                                                                                  |
| `i2-sig-arithmetic` | building, merging, and editing function signatures with i2’s Sig — signature “+/-” arithmetic                                            |
| `i2-signatures`     | introspecting function signatures and binding call arguments to parameter names with i2’s Sig class — turning an arbitrary               |
| `i2-wrapper`        | wrapping functions to transform their interface                                                                                          |

**The documentation, machine-readable**: [`llms.txt`](https://i2mint.github.io/i2/llms.txt) indexes every page; [`i2.md`](https://i2mint.github.io/i2/i2.md) is the whole documentation in one file; every page has a `.md` twin; [`objects.inv`](https://i2mint.github.io/i2/objects.inv) maps symbols to URLs. The full list, with install lines, is on the site’s [For AI agents](https://i2mint.github.io/i2/ai-agents.html) page.

If you identify as a dinosaur, the rest of this README is written for you, starting at [Key Modules Overview]().

<!-- epythet:agentic-readme:end -->

## Install

```default
pip install i2
```

The smallest useful thing: read, edit and re-apply a function’s signature with `Sig`.

```python
from i2 import Sig

def f(a, b=2, *, c=3):
    return a + b * c

Sig(f)               # <Sig (a, b=2, *, c=3)>
Sig(f).names         # ['a', 'b', 'c']
g = Sig(f).ch_names(a="x")(f)   # f itself, with its signature rewritten (g is f)
Sig(g)               # <Sig (x, b=2, *, c=3)>
g(1)                 # 7
```

The flat, single-file view of the whole documentation is [i2.md](https://i2mint.github.io/i2/i2.md).

## Key Modules Overview

### i2.castgraph - Type/Kind-Based Transformation Graphs

`castgraph` provides a graph-based system for organizing transformations between different data representations (“kinds”). It routes objects through multi-hop conversion paths, selecting the optimal route based on cost.

The `castgraph` tool addresses the common friction point in software design where a function requires a specific data type or format, but the user possesses related data in a different, interchangeable representation (e.g., a file path instead of a loaded object). Inspired by Postel’s Law (“be liberal in what you accept”), the core problem is how to make interfaces highly flexible and accommodating of diverse inputs—eliminating tedious data preparation boilerplate for the user—while simultaneously adhering to the principle that “explicit is better than implicit” by keeping complex conversion logic out of the main application code; i2.castgraph solves this by providing a dedicated, cost-aware graph system to organize and execute necessary multi-hop transformations dynamically, effectively acting as an intelligent input adapter.
Read me in the [castgraph dev notes](https://github.com/i2mint/i2/discussions/77#discussioncomment-14928396).

**Basic Usage (Type-Based):**

```python
from i2.castgraph import TransformationGraph

graph = TransformationGraph()

# Register transformations between types
@graph.register_edge(str, float)
def str_to_float(s, ctx):
    return float(s)

@graph.register_edge(float, int)
def float_to_int(f, ctx):
    return int(f)

# Automatically routes str -> float -> int
result = graph.transform("42.7", int)
assert result == 42
```

**Advanced Usage (Kind-Based):**

```python
# Define custom "kinds" (not just types)
graph.add_node('json_string', isa=lambda x: isinstance(x, str) and x.startswith('{'))
graph.add_node('config_dict', isa=lambda x: isinstance(x, dict))

@graph.register_edge('json_string', 'config_dict')
def parse_json(text, ctx):
    import json
    return json.loads(text)

# Transform with automatic kind detection
result = graph.transform('{"key": "value"}', 'config_dict', from_kind='json_string')
```

**Key Features:**

- Multi-hop routing with cost-based path selection
- Support for arbitrary hashable kinds (types, strings, custom markers)
- Pluggable kind detection with predicates
- Context propagation for dependency injection
- MRO-aware fallback for type hierarchies

### i2.signatures - Function Signature Manipulation

`signatures` provides a calculus for working with function signatures - introspecting, merging, and modifying them programmatically.

**Signature Introspection:**

```python
from i2.signatures import Sig

def func(z, a: float = 1.0, /, b=2, *, c: int = 3):
    pass

sig = Sig(func)
print(sig.names)        # ['z', 'a', 'b', 'c']
print(sig.defaults)     # {'a': 1.0, 'b': 2, 'c': 3}
print(sig.annotations)  # {'a': <class 'float'>, 'c': <class 'int'>}
```

**Signature Construction:**

```python
# From function
sig1 = Sig(lambda x, y: x + y)

# From list of names
sig2 = Sig(['a', 'b', 'c'])

# From string
sig3 = Sig('x y z')

# All create callable Signature objects
print(sig2)  # <Sig (a, b, c)>
```

**Signature Merging:**

```python
def foo(x, y=1): pass
def bar(z: int, *, w=2): pass

# Combine signatures
combined = Sig(foo) + Sig(bar)
print(combined)  # <Sig (x, y=1, z: int, w=2)>
```

**Decorating with Signatures:**

```python
# Give a function a specific signature
@Sig('a b c')
def func(*args, **kwargs):
    print(f"Called with: {args}, {kwargs}")

# Now func has signature (a, b, c)
func(1, 2, 3)  # Works as expected
```

**Key Features:**

- Extract parameter names, kinds, defaults, and annotations
- Merge multiple signatures flexibly
- Apply signatures as decorators
- Support for all parameter kinds (positional-only, keyword-only, VAR_POSITIONAL, VAR_KEYWORD)
- Signature algebra for composing function interfaces

### i2.wrapper - Ingress/Egress Function Wrapping

`wrapper` provides the `Wrap` class for transforming function inputs and outputs through composable ingress/egress layers.

**Basic Wrapping:**

```python
from i2.wrapper import Wrap

def add(x, y):
    return x + y

# Transform inputs before function, outputs after
wrapped = Wrap(
    add,
    ingress=lambda x, y: (x * 2, y * 2),  # Double inputs
    egress=lambda result: result / 2       # Halve output
)

result = wrapped(3, 4)  # (3*2 + 4*2) / 2 = 7
assert result == 7
```

**Signature Transformation:**

```python
from i2.wrapper import Ingress

def process(data: dict):
    return data['value']

# Change signature: accept 'x' instead of 'data'
ingress = Ingress(
    outer_sig='x',
    inner_sig='data',
    kwargs_trans=lambda x: {'data': {'value': x}}
)

new_func = ingress(process)
result = new_func(42)  # Calls process({'value': 42})
assert result == 42
```

**The Wrap Flow:**

```default
*outer_args, **outer_kwargs
        ↓
    [ingress] - transform inputs
        ↓
*inner_args, **inner_kwargs
        ↓
     [func] - original function
        ↓
   func_output
        ↓
    [egress] - transform outputs
        ↓
  final_output
```

**Key Features:**

- Separate ingress (input transformation) and egress (output transformation)
- Signature-aware argument mapping
- Composable wrapper layers
- Supports partial application and argument reordering
- Clean separation of concerns for cross-cutting functionality

### i2.routing_forest - Conditional Logic as Data Structures

`routing_forest` lets you express nested if/then conditions as composable, reusable tree structures instead of tangled code.

**Basic Routing:**

```python
from i2.routing_forest import RoutingForest, CondNode, FinalNode

# Define routing logic as a forest
router = RoutingForest([
    CondNode(
        cond=lambda x: isinstance(x, int),
        then=FinalNode("It's an integer!")
    ),
    CondNode(
        cond=lambda x: isinstance(x, str),
        then=FinalNode("It's a string!")
    )
])

# Get first match
result = next(router(42))
assert result == "It's an integer!"
```

**Nested Conditions:**

```python
# Nested routing with multiple conditions
router = RoutingForest([
    CondNode(
        cond=lambda x: isinstance(x, (int, str)),
        then=RoutingForest([
            CondNode(
                cond=lambda x: int(x) >= 10,
                then=FinalNode("≥ 10")
            ),
            CondNode(
                cond=lambda x: int(x) % 2 == 1,
                then=FinalNode("Odd number")
            )
        ])
    )
])

# Can get all matches or just first
list(router(15))   # ['≥ 10', 'Odd number']
next(router(8))    # None (no matches)
```

**Pattern Matching Example:**

```python
# Router as pattern matcher
def route_value(value):
    router = RoutingForest([
        CondNode(
            cond=lambda x: x < 0,
            then=FinalNode("negative")
        ),
        CondNode(
            cond=lambda x: x == 0,
            then=FinalNode("zero")
        ),
        CondNode(
            cond=lambda x: x > 0,
            then=FinalNode("positive")
        )
    ])
    return next(router(value), "unknown")

assert route_value(-5) == "negative"
assert route_value(0) == "zero"
assert route_value(10) == "positive"
```

**Key Features:**

- Objectify nested if/then logic into composable components
- Both callable and iterable nodes
- Get first match, all matches, or default values
- Cleaner than nested if/elif/else chains for complex routing
- Reusable condition components

### i2.util - Utility Functions and Helpers

`util` provides miscellaneous utility functions for common patterns.

**Identity and Constant Functions:**

```python
from i2.util import asis, return_true, return_false, return_none

# Identity function
assert asis(42) == 42
assert asis([1, 2, 3]) == [1, 2, 3]

# Constant functions (useful as defaults)
assert return_true(anything, goes="here") is True
assert return_false("doesn't", "matter") is False
assert return_none(1, 2, 3) is None
```

**Object Naming:**

```python
from i2.util import name_of_obj

# Get name of various objects
assert name_of_obj(map) == 'map'
assert name_of_obj([1, 2, 3]) == 'list'
assert name_of_obj(lambda x: x) == '<lambda>'

from functools import partial
assert name_of_obj(partial(print, sep=",")) == 'print'
```

**Attribute/Item Access:**

```python
from i2.util import imdict

# Flexible dict-like access
data = imdict({'a': 1, 'b': 2})
assert data.a == 1  # Attribute access
assert data['b'] == 2  # Item access
```

**Laziness Utilities:**

```python
from i2.util import lazyprop

class DataLoader:
    @lazyprop
    def expensive_data(self):
        print("Loading...")
        return [1, 2, 3, 4, 5]

loader = DataLoader()
# First access computes and caches
data1 = loader.expensive_data  # Prints "Loading..."
# Subsequent accesses use cached value
data2 = loader.expensive_data  # No print
assert data1 is data2
```

**Key Features:**

- Common function patterns (identity, constants)
- Object introspection helpers
- Flexible attribute/item access wrappers
- Lazy evaluation utilities
- Deprecation helpers
- String manipulation tools

## Common Patterns

### Composing Transformations

```python
from i2.castgraph import TransformationGraph
from i2.wrapper import Wrap

# Define transformation graph
graph = TransformationGraph()

@graph.register_edge('csv', 'rows')
def parse_csv(text, ctx):
    return [line.split(',') for line in text.strip().split('\n')]

@graph.register_edge('rows', 'records')
def rows_to_records(rows, ctx):
    return [dict(zip(headers, row)) for row in rows[1:]]

# Use with wrapper for clean API
def process_csv(csv_text: str) -> list:
    return graph.transform(csv_text, 'records', from_kind='csv')

# Wrap to add validation
validated = Wrap(
    process_csv,
    ingress=lambda text: (text.strip(),),
    egress=lambda records: [r for r in records if r]  # Filter empties
)
```

### Dynamic Signature Manipulation

```python
from i2.signatures import Sig
from i2.wrapper import Ingress

# Start with a general function
def process(**kwargs):
    return sum(kwargs.values())

# Give it a specific signature
@Sig('a b c')
def typed_process(**kwargs):
    return process(**kwargs)

# Now can call with clear parameters
result = typed_process(1, 2, 3)
assert result == 6
```

### Routing with Validation

```python
from i2.routing_forest import RoutingForest, CondNode, FinalNode
from i2.util import return_none

def validate_input(value):
    """Route to appropriate validator."""
    router = RoutingForest([
        CondNode(
            cond=lambda x: isinstance(x, str),
            then=RoutingForest([
                CondNode(lambda x: len(x) > 0, FinalNode(True)),
                CondNode(lambda x: len(x) == 0, FinalNode(False))
            ])
        ),
        CondNode(
            cond=lambda x: isinstance(x, int),
            then=FinalNode(x >= 0)
        )
    ])
    return next(router(value), False)

assert validate_input("hello") is True
assert validate_input("") is False
assert validate_input(5) is True
assert validate_input(-1) is False
```

## What’s mint?

Mint stands for “Meta-INTerface”.

Minting is core technique of i2i: It can be seen as the encapsulation of a construct’s interface into a (data)
structure that contains everything one needs to know about the construct to perform a specific action
with or on the construct.

A little note on the use of “encapsulation”. The term is widely used in computer science,
and is typically tied to object oriented programming. Wikipedia provides two definitions:

* A language mechanism for restricting direct access to some of the object’s components.
* A language construct that facilitates the bundling of data with the methods (or other functions)
  operating on that data.

Though both these definitions apply to minting,
the original sense of the word “encapsulate” is even more relevant (from google definitions):

* express the essential features of (something) succinctly
* enclose (something) in or as if in a capsule

Indeed, minting is the process of enclosing a construct into a “mint” (for “Meta INTerface”)
that will express the features of the construct that are essential to the task at hand.
The mint provides a declarative layer of the construct that allows one to write code that operates with this layer,
which is designed to be (as) consistent (as possible) from one system/language to another.

For example, whether a (non-anonymous) function was written in C, Python, or JavaScript,
it will at least have a name, and it’s arguments will (most often) have names, and may have types.
Similarly with “data objects”: The data of both JavaScript and Python objects can be represented by a tree whose
leaves are base types, which can in turn be represented by a C struct.

<p class="epythet-aggregates">This documentation as a single file: <a href="i2.md">i2.md</a> (Markdown, for agents).</p>


# _autosummary/i2.base.html.md

# i2.base

Tools to provide meta-interfaces (“mints”) of python objects.

A mint is a `Mapping` view of the (meta-)information describing the interface of
an object: for a callable, its parameters (name, kind, default, annotation) and its
return annotation.

Main entry points:

- `Mint`: mint of any python object
- `MintOfCallable`: mint of a callable, with parameter information
- `ParametersMint`: mint of the parameters of a callable

### Functions

| [`is_not_empty`](_autosummary/i2.base.html.md#i2.base.is_not_empty)(obj)   | False for `inspect.Parameter.empty` and `NotFoundType` instances, True otherwise.   |
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------|
| [`name_of_obj`](_autosummary/i2.base.html.md#i2.base.name_of_obj)(o)      | Deprecated alias of `i2.signatures.name_of_obj` (emits a `DeprecationWarning`).     |

### Classes

| [`AttrFromKey`](_autosummary/i2.base.html.md#i2.base.AttrFromKey)(d)                   | Expose the keys of a mapping as attributes (`obj.k` reads `d[k]`).                         |
|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| [`KeyFromAttr`](_autosummary/i2.base.html.md#i2.base.KeyFromAttr)(d)                   | Expose the attributes of an object as mapping keys (`obj[k]` reads `getattr(d, k)`).       |
| [`Mint`](_autosummary/i2.base.html.md#i2.base.Mint)(obj[, attrs])               | Get a Mint object of a python object.                                                      |
| [`MintOfCallable`](_autosummary/i2.base.html.md#i2.base.MintOfCallable)(obj[, attrs])     | Get a Mint object of a python object.                                                      |
| [`MintOfCallableMixin`](_autosummary/i2.base.html.md#i2.base.MintOfCallableMixin)()            | Mint attributes computed from a callable's signature (parameters, return annotation, doc). |
| [`MintOfDocMixin`](_autosummary/i2.base.html.md#i2.base.MintOfDocMixin)()                 | Placeholder mixin for parsed-docstring mint attributes (not implemented yet).              |
| [`NotFoundType`](_autosummary/i2.base.html.md#i2.base.NotFoundType)()                   | Type of the `not_found` sentinel: falsy, repr `NotFound`.                                  |
| [`ParameterMint`](_autosummary/i2.base.html.md#i2.base.ParameterMint)(param[, position]) | Mint of one parameter: its name, kind, default and annotation (and position, if given).    |
| [`ParametersMint`](_autosummary/i2.base.html.md#i2.base.ParametersMint)([params])         | Get mint of the parameters of a callable.                                                  |

### *class* i2.base.AttrFromKey(d)

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

Expose the keys of a mapping as attributes (`obj.k` reads `d[k]`).

### *class* i2.base.KeyFromAttr(d)

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

Expose the attributes of an object as mapping keys (`obj[k]` reads `getattr(d, k)`).

### *class* i2.base.Mint(obj, attrs=None)

Bases: [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)

Get a Mint object of a python object.
A Mint will provide parameters that provide (meta-)information about the interface of the python object.

```pycon
>>> from pprint import pprint
>>> # Mint of a function
>>> def f(my_arg: int = 7) -> int:
...     return my_arg + 10
>>> mint = Mint(f)
>>> mint.obj_name, mint.type_name, mint.module_name, mint.obj_name
('f', 'function', 'i2.base', 'f')
>>> # Mint of a module
>>> import os as myos
>>> mint = Mint(myos)
>>> mint.obj_name, mint.type_name, mint.module_name, mint.obj_name
('os', 'module', 'os', 'os')
>>> assert set(list(mint)) == {'module_name', 'module', 'type_name', 'obj_name'}
>>> # Mint of a variable
>>> v = 10
>>> mint = Mint(v)
>>> mint.obj_name, mint.type_name, mint.module_name, mint.obj_name
(NotFound, 'int', NotFound, NotFound)
>>> assert set(list(mint)) == {'type_name'}  # see that there's only one non-null attr!
```

#### items() → a set-like object providing a view on D's items

### *class* i2.base.MintOfCallable(obj, attrs=None)

Bases: [`Mint`](_autosummary/i2.base.html.md#i2.base.Mint), [`MintOfCallableMixin`](_autosummary/i2.base.html.md#i2.base.MintOfCallableMixin), [`MintOfDocMixin`](_autosummary/i2.base.html.md#i2.base.MintOfDocMixin)

Get a Mint object of a python object.
A Mint will provide parameters that provide (meta-)information about the interface of the python object.

```pycon
>>> from pprint import pprint
>>> def f(my_arg: int = 7) -> int:
...     return my_arg + 10
>>> f.__doc__ = 'some documentation'
>>>
>>> mint = MintOfCallable(f)
>>> mint.obj_name
'f'
>>> mint.type_name
'function'
>>> mint.module_name
'i2.base'
>>> mint.parameters.my_arg
{'name': 'my_arg', 'kind': 'POSITIONAL_OR_KEYWORD', 'default': 7, 'annotation': <class 'int'>, 'position': 0}
>>> mint.doc_string
'some documentation'
>>> mint.return_annotation
<class 'int'>
>>> def g(a, b: 'some_string_id_of_a_custom_type', c=1, d: int = 1) -> float:
...     return a * b * c * d
>>> pprint(dict(MintOfCallable(g).parameters))
{'a': {'name': 'a', 'kind': 'POSITIONAL_OR_KEYWORD', 'position': 0},
 'b': {'name': 'b', 'kind': 'POSITIONAL_OR_KEYWORD', 'annotation': 'some_string_id_of_a_custom_type', 'position': 1},
 'c': {'name': 'c', 'kind': 'POSITIONAL_OR_KEYWORD', 'default': 1, 'position': 2},
 'd': {'name': 'd', 'kind': 'POSITIONAL_OR_KEYWORD', 'default': 1, 'annotation': <class 'int'>, 'position': 3}}
```

### *class* i2.base.MintOfCallableMixin

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

Mint attributes computed from a callable’s signature (parameters, return annotation, doc).

### *class* i2.base.MintOfDocMixin

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

Placeholder mixin for parsed-docstring mint attributes (not implemented yet).

### *class* i2.base.NotFoundType

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

Type of the `not_found` sentinel: falsy, repr `NotFound`.

### *class* i2.base.ParameterMint(param, position=None)

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

Mint of one parameter: its name, kind, default and annotation (and position, if given).

Accepts an `inspect.Parameter`-like object or a mapping with those keys; missing
attributes are set to `inspect.Parameter.empty`.

### *class* i2.base.ParametersMint(params={})

Bases: [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)

Get mint of the parameters of a callable.

```pycon
>>> import inspect
>>> from pprint import pprint
>>>
>>> def g(a, b: 'some_type', c=1, d: int = 1) -> float:
...     return a * b * c * d
>>> mint = ParametersMint(inspect.signature(g).parameters)
>>> # mint is a mapping (like a read-only dict), so...
>>> list(mint)
['a', 'b', 'c', 'd']
>>>
>>> for arg_spec in mint.values():
...     print(arg_spec)
{'name': 'a', 'kind': 'POSITIONAL_OR_KEYWORD', 'position': 0}
{'name': 'b', 'kind': 'POSITIONAL_OR_KEYWORD', 'annotation': 'some_type', 'position': 1}
{'name': 'c', 'kind': 'POSITIONAL_OR_KEYWORD', 'default': 1, 'position': 2}
{'name': 'd', 'kind': 'POSITIONAL_OR_KEYWORD', 'default': 1, 'annotation': <class 'int'>, 'position': 3}
>>> t = list(mint.items())
>>> t[0]
('a', {'name': 'a', 'kind': 'POSITIONAL_OR_KEYWORD', 'position': 0})
>>> t[1]
('b', {'name': 'b', 'kind': 'POSITIONAL_OR_KEYWORD', 'annotation': 'some_type', 'position': 1})
>>>
>>> mint = ParametersMint(inspect.signature(g).parameters)
>>> pprint(dict(mint))
{'a': {'name': 'a', 'kind': 'POSITIONAL_OR_KEYWORD', 'position': 0},
 'b': {'name': 'b', 'kind': 'POSITIONAL_OR_KEYWORD', 'annotation': 'some_type', 'position': 1},
 'c': {'name': 'c', 'kind': 'POSITIONAL_OR_KEYWORD', 'default': 1, 'position': 2},
 'd': {'name': 'd', 'kind': 'POSITIONAL_OR_KEYWORD', 'default': 1, 'annotation': <class 'int'>, 'position': 3}}
```

```pycon
>>> # and now, some cannibalistic fun...
>>> # The following is skipped because not working in 3.10
>>> pprint(
...     dict(ParametersMint(inspect.signature(ParametersMint).parameters))
... )
{'args': {'name': 'args', 'kind': 'VAR_POSITIONAL', 'position': 0},
 'kwds': {'name': 'kwds', 'kind': 'VAR_KEYWORD', 'position': 1}}
>>> pprint(
...     dict(ParametersMint(inspect.signature(ParametersMint.__init__).parameters))
... )
{'params': {'name': 'params', 'kind': 'POSITIONAL_OR_KEYWORD', 'default': FrozenDict({}), 'position': 1},
 'self': {'name': 'self', 'kind': 'POSITIONAL_OR_KEYWORD', 'position': 0}}
>>> pprint(
...     dict(ParametersMint(inspect.signature(ParametersMint.__new__).parameters))
... )
{'args': {'name': 'args', 'kind': 'VAR_POSITIONAL', 'position': 1},
 'cls': {'name': 'cls', 'kind': 'POSITIONAL_OR_KEYWORD', 'position': 0},
 'kwds': {'name': 'kwds', 'kind': 'VAR_KEYWORD', 'position': 2}}
```

#### items() → a set-like object providing a view on D's items

### i2.base.is_not_empty(obj)

False for `inspect.Parameter.empty` and `NotFoundType` instances, True otherwise.

### i2.base.name_of_obj(o)

Deprecated alias of `i2.signatures.name_of_obj` (emits a `DeprecationWarning`).


# _autosummary/i2.castgraph.html.md

# i2.castgraph

A lightweight transformation service for Python that solves the “stable role,
unstable representation” problem: a resource has a consistent semantic role
(e.g., configuration, text, structured record) but appears in many forms
(filepath, string, dict, custom class), while consumers expect specific
representations. castgraph organizes transformations as a graph of “kinds”
(data representations) and routes requests through the best available path.

**Key concepts**

- **Kind**: Any hashable identifier for a data representation (type, string, custom marker)
- **Transformation**: An edge in the graph that converts one kind to another
- **Kind Predicate (isa)**: A function that determines if an object is of a kind
- **TransformationGraph**: The main registry with graph-oriented interface

**Solution patterns**

- **Type Converter / Conversion Service**: central registry mapping (FromKind, ToKind) to transformer functions.
- **Adapter**: each edge adapts one representation to another.
- **Strategy**: routing/selection among multiple possible transformations via cost/priority.
- **(Optional) Canonical Data Model**: a hub kind to reduce pairwise conversions.
- **DDD Anti-Corruption Layer (ACL)**: keep external formats outside the core domain.
- **Typeclass / Multimethod idiom**: dispatch based on (source kind, target kind).

**Minimal example (new kind-based interface)**

Use the new TransformationGraph with flexible kinds (not limited to types).

```pycon
>>> from i2.castgraph import TransformationGraph
>>> graph = TransformationGraph()
>>> # Add nodes with predicates
>>> graph.add_node('text', isa=lambda x: isinstance(x, str))
>>> graph.add_node('json_dict', isa=lambda x: isinstance(x, dict))
>>> # Add transformation edges
>>> @graph.register_edge('text', 'json_dict')
... def text_to_json(t, ctx):
...     import json
...     return json.loads(t or "{}")
>>> # Transform using kinds (need explicit from_kind since 'text' != str)
>>> result = graph.transform('{"x": 1}', 'json_dict', from_kind='text')
>>> result["x"]
1
```

**Legacy example (type-based interface)**

The old ConversionRegistry interface still works but is deprecated.

```pycon
>>> from i2.castgraph import ConversionRegistry
>>> import warnings
>>> class Path(str): ...
>>> class Text(str): ...
>>> class Record(dict): ...
>>> reg = ConversionRegistry()
>>> with warnings.catch_warnings():
...     warnings.simplefilter("ignore")
...     @reg.register(Path, Text)
...     def path_to_text(p, ctx):
...         fs = (ctx or {}).get("fs", {})
...         return Text(fs.get(str(p), ""))
...     @reg.register(Text, Record, cost=0.5)
...     def text_to_record(t, ctx):
...         import json
...         return Record(json.loads(t or "{}"))
>>> ctx = {"fs": {"/app/data.json": '{"x": 1}'}}
>>> with warnings.catch_warnings():
...     warnings.simplefilter("ignore")
...     out = reg.convert(Path("/app/data.json"), Record, context=ctx)
>>> isinstance(out, Record) and out["x"] == 1
True
```

**Main tools**

- **TransformationGraph**: the main graph-based registry (recommended).
  - `.add_node(kind, isa=None)`: add a kind with optional predicate.
  - `.add_edge(src, dst, func, cost=1.0)`: add a transformation edge.
  - `.register_edge(src, dst, cost=1.0)`: decorator to add an edge.
  - `.transform(obj, to_kind, from_kind=None, context=None)`: transform with multi-hop routing.
  - `.transform_any(obj, to_kind, context=None)`: transform with automatic kind detection.
  - `.get_transformer(from_kind, to_kind)`: get a composed transformer function.
  - `.detect_kind(obj)`: detect the kind of an object.
  - `.reachable_from(kind)`: get all reachable kinds.
  - `.sources_for(kind)`: get all source kinds.
  - `.kinds()`: get all registered kinds.
- **ConversionRegistry**: DEPRECATED - use TransformationGraph instead.
  - `.register(From, To, cost=1.0)`: DEPRECATED - use `.register_edge()` instead.
  - `.convert(obj, ToType, context=None)`: DEPRECATED - use `.transform()` instead.
- **Kind**: Optional wrapper for explicit kind specification with predicates.
- **KindMatch**: Truthy result from kind predicates that can carry metadata.
- **ConversionError**: raised when no route exists between kinds.

**Design guidelines**

- Define a single TransformationGraph per bounded context; keep edges local.
- Prefer small, testable transformer functions with explicit kinds.
- Use a canonical domain kind as a **hub** when many formats interoperate.
- Assign **costs** to prefer fast/accurate routes; tune with metrics.
- Pass a **context** dict for side-channel knobs (I/O, flags, cache handles).
- Cache paths (via lru_cache) and consider result caching for hot transformations.
- Keep adapters at the boundaries; the core domain should consume domain kinds.
- Add identity edges implicitly; avoid no-op boilerplate.
- Write doctests on each transformer to lock behavior and invariants.
- Use bare hashables (types, strings) as kinds; Kind class is optional.

**Migration guide**

Old code using ConversionRegistry:

```default
reg = ConversionRegistry()
@reg.register(SrcType, DstType)
def convert_func(obj, ctx): ...
result = reg.convert(obj, DstType)
```

New code using TransformationGraph:

```default
graph = TransformationGraph()
@graph.register_edge(SrcType, DstType)
def transform_func(obj, ctx): ...
result = graph.transform(obj, DstType)
```

Or with string kinds:

```default
graph = TransformationGraph()
graph.add_node('src_format', isa=lambda x: ...)
@graph.register_edge('src_format', 'dst_format')
def transform_func(obj, ctx): ...
result = graph.transform(obj, 'dst_format')
```

**Design heritage**

castgraph is a composition of well-known patterns centered on a \*\*Type Converter /
Conversion Service\*\*, with **Adapter** edges and **Strategy**-based route selection.
At system boundaries, it complements DDD’s **Anti-Corruption Layer** and can employ
an integration **Canonical Data Model** to curb O(n²) pairwise mappings.
Its (FromType, ToType) dispatch style mirrors **typeclass/multimethod** idioms.
For background reading, see:

- .NET TypeConverter: [https://learn.microsoft.com/dotnet/api/system.componentmodel.typeconverter](https://learn.microsoft.com/dotnet/api/system.componentmodel.typeconverter)
- Spring ConversionService: [https://docs.spring.io/spring-framework/reference/core/validation/convert.html](https://docs.spring.io/spring-framework/reference/core/validation/convert.html)
- Apache Camel Type Converter: [https://camel.apache.org/manual/type-converter.html](https://camel.apache.org/manual/type-converter.html)
- Adapter: [https://refactoring.guru/design-patterns/adapter](https://refactoring.guru/design-patterns/adapter)
- Strategy: [https://refactoring.guru/design-patterns/strategy](https://refactoring.guru/design-patterns/strategy)
- Anti-Corruption Layer: [https://martinfowler.com/bliki/AntiCorruptionLayer.html](https://martinfowler.com/bliki/AntiCorruptionLayer.html)
- Canonical Data Model: [https://www.enterpriseintegrationpatterns.com/patterns/messaging/CanonicalDataModel.html](https://www.enterpriseintegrationpatterns.com/patterns/messaging/CanonicalDataModel.html)
- PEP 443 singledispatch: [https://peps.python.org/pep-0443/](https://peps.python.org/pep-0443/)

**Related**

- Issue that sparked this implementation: [https://github.com/i2mint/i2/issues/79](https://github.com/i2mint/i2/issues/79)
- Computational path resolution: [https://github.com/i2mint/meshed/discussions/71](https://github.com/i2mint/meshed/discussions/71)
- Subsuming concept - “routing”: [https://github.com/i2mint/i2/discussions/68](https://github.com/i2mint/i2/discussions/68)

### Functions

| [`design_guidelines`](_autosummary/i2.castgraph.html.md#i2.castgraph.design_guidelines)()   | Returns concise guidance for organizing casting in Python.   |
|------------------------------------------------------------------------|--------------------------------------------------------------|

### Classes

| [`ConversionRegistry`](_autosummary/i2.castgraph.html.md#i2.castgraph.ConversionRegistry)()                   | DEPRECATED: Use TransformationGraph instead.                                    |
|-----------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`Edge`](_autosummary/i2.castgraph.html.md#i2.castgraph.Edge)(src, dst, func[, cost])           | DEPRECATED: Use Transformation instead.                                         |
| [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)(identifier[, isa])                | Optional marker for explicit kind specification.                                |
| [`KindMatch`](_autosummary/i2.castgraph.html.md#i2.castgraph.KindMatch)([metadata])                  | Result of a successful kind predicate match.                                    |
| [`Transformation`](_autosummary/i2.castgraph.html.md#i2.castgraph.Transformation)(src, dst, func[, cost]) | An edge in the transformation graph.                                            |
| [`TransformationGraph`](_autosummary/i2.castgraph.html.md#i2.castgraph.TransformationGraph)()                  | A graph-based registry of transformations between kinds (data representations). |

### Exceptions

| [`ConversionError`](_autosummary/i2.castgraph.html.md#i2.castgraph.ConversionError)   |    |
|--------------------------------------------------------------------|----|

### *exception* i2.castgraph.ConversionError

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

### *class* i2.castgraph.ConversionRegistry

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

DEPRECATED: Use TransformationGraph instead.

A graph-based registry of converters between Python types with:

> - registration decorator
> - shortest-path (by total cost) routing
> - MRO-aware fallback for source types
> - caching of paths and (optionally) results

Design notes:

- Each converter has signature: func(obj, context) -> converted_obj
- Identity edges are implicit (T -> T) with cost 0.
- If multiple routes exist, the minimum total cost path is chosen.

#### convert(obj, to_type, , context=None, use_result_cache=False)

Convert `obj` to `to_type`, possibly via multi-hop.

* **Parameters:**
  * **obj** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – Source object to convert.
  * **to_type** ([`type`](https://docs.python.org/3/builtins/functions.html#type)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`U`)]) – Desired target type.
  * **context** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Arbitrary context propagated through the chain (e.g., config, flags).
  * **use_result_cache** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If True, cache results keyed by (id(obj), to_type).
* **Returns:**
  Converted object.
* **Return type:**
  [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`U`)
* **Raises:**
  [**ConversionError**](_autosummary/i2.castgraph.html.md#i2.castgraph.ConversionError) – If no conversion path is found.

### Examples

```pycon
>>> reg = ConversionRegistry()
>>> class X: ...
>>> class Y: ...
>>> class Z: ...
>>> @reg.register(X, Y)
... def x_to_y(x, ctx): return Y()
...
>>> @reg.register(Y, Z)
... def y_to_z(y, ctx): return Z()
...
>>> isinstance(reg.convert(X(), Z), Z)
True
```

MRO fallback: if a converter is registered for a base class, it applies to a subclass.

```pycon
>>> class Base: ...
>>> class Sub(Base): ...
>>> class Out: ...
>>> reg2 = ConversionRegistry()
>>> @reg2.register(Base, Out)
... def base_to_out(b, ctx): return Out()
...
>>> isinstance(reg2.convert(Sub(), Out), Out)
True
```

#### register(src=None, dst=None, , cost=1.0)

Decorator to register a converter function.

* **Return type:**
  [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any), [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]], [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any), [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]

```pycon
>>> reg = ConversionRegistry()
>>> class A: ...
>>> class B: ...
>>> @reg.register(A, B)
... def a_to_b(a, ctx): return B()
...
>>> isinstance(reg.convert(A(), B), B)
True
```

Types can be inferred from annotations:

```pycon
>>> class X: ...
>>> class Y: ...
>>> @reg.register()
... def x_to_y(x: X, ctx) -> Y:
...     return Y()
>>> isinstance(reg.convert(X(), Y), Y)
True
```

### *class* i2.castgraph.Edge(src, dst, func, cost=1.0)

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

DEPRECATED: Use Transformation instead. Kept for backward compatibility.

### *class* i2.castgraph.Kind(identifier, isa=None)

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

Optional marker for explicit kind specification.

A Kind wraps a hashable identifier and optionally an ‘isa’ predicate.
Users are NOT required to use this class - bare hashables work fine.
This class is for when you want to be explicit or bundle identifier + predicate.

```pycon
>>> text_kind = Kind('text', isa=lambda x: isinstance(x, str))
>>> text_kind.identifier
'text'
>>> text_kind.isa("hello")
True
```

#### isa(obj)

Check if obj is of this kind (predicate/recognizer function).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool) | [`KindMatch`](_autosummary/i2.castgraph.html.md#i2.castgraph.KindMatch)

### *class* i2.castgraph.KindMatch(metadata=None)

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

Result of a successful kind predicate match.

Evaluates to True but can carry additional metadata about the match
that downstream transformations might use.

```pycon
>>> match = KindMatch({'encoding': 'utf-8', 'analyzed': True})
>>> bool(match)
True
>>> match.metadata
{'encoding': 'utf-8', 'analyzed': True}
```

### *class* i2.castgraph.Transformation(src, dst, func, cost=1.0)

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

An edge in the transformation graph.

Represents a transformation function from one kind to another.

### *class* i2.castgraph.TransformationGraph

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

A graph-based registry of transformations between kinds (data representations).

A “kind” is any hashable identifier for a data representation - it can be a type,
a string, or any custom marker. The graph supports:

> - Flexible kind system (not limited to Python types)
> - Graph-oriented interface (add_node, add_edge)
> - Pluggable kind detection via predicates
> - Shortest-path (by total cost) routing
> - MRO-aware fallback for type-based kinds
> - Caching of paths and (optionally) results

Design notes:

- Each transformer has signature: func(obj, context) -> transformed_obj
- Identity edges are implicit (K -> K) with cost 0
- If multiple routes exist, the minimum total cost path is chosen
- Kinds can be types, strings, or any hashable objects

#### add_edge(src, dst, func, , cost=1.0)

Add a transformation (edge) between two kinds.

Automatically adds nodes if they don’t exist.

* **Parameters:**
  * **src** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)) – Source kind
  * **dst** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)) – Destination kind
  * **func** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)) – Transformation function with signature func(obj, context) -> transformed_obj
    or func(obj) -> transformed_obj (will be wrapped)
  * **cost** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Cost of this transformation (lower is preferred)
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### Examples

```pycon
>>> graph = TransformationGraph()
>>> def text_to_int(s, ctx): return int(s)
>>> graph.add_edge('text', int, text_to_int)
```

#### add_node(kind, isa=None)

Add a kind (node) to the graph with optional predicate.

* **Parameters:**
  * **kind** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)) – The kind identifier (can be a type, string, or Kind object)
  * **isa** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)], [`bool`](https://docs.python.org/3/builtins/functions.html#bool) | [`KindMatch`](_autosummary/i2.castgraph.html.md#i2.castgraph.KindMatch)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional predicate function to detect if an object is of this kind
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### Examples

```pycon
>>> graph = TransformationGraph()
>>> graph.add_node('text', isa=lambda x: isinstance(x, str))
>>> graph.add_node(int)  # Type implies isinstance check
```

#### convert(obj, to_type, , context=None, use_result_cache=False)

DEPRECATED: Use transform() instead.

This method is kept for backward compatibility.

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

### Examples

```pycon
>>> import warnings
>>> graph = TransformationGraph()
>>> @graph.register_edge(str, int)
... def str_to_int(s, ctx): return int(s)
>>> with warnings.catch_warnings():
...     warnings.simplefilter("ignore")
...     result = graph.convert("42", int)
>>> result
42
```

#### detect_kind(obj)

Detect the kind of an object.

Uses custom detector if set, otherwise tries registered predicates in order.
Returns None if no kind matches.

* **Parameters:**
  **obj** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – Object to classify
* **Returns:**
  The detected kind identifier, or None if no match
* **Return type:**
  [`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`None`](https://docs.python.org/3/builtins/constants.html#None)

### Examples

```pycon
>>> graph = TransformationGraph()
>>> graph.add_node('text', isa=lambda x: isinstance(x, str))
>>> graph.detect_kind("hello")
'text'
```

#### get_transformer(from_kind, to_kind, , context=None)

Get a function that transforms from_kind → to_kind.

Returns a composed transformer function (Pipe-like).

* **Parameters:**
  * **from_kind** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)) – Source kind
  * **to_kind** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)) – Destination kind
  * **context** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional context to bake into the transformer
* **Returns:**
  A function that transforms objects from from_kind to to_kind
* **Return type:**
  [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]

### Examples

```pycon
>>> graph = TransformationGraph()
>>> @graph.register_edge(str, int)
... def str_to_int(s, ctx): return int(s)
>>> transformer = graph.get_transformer(str, int)
>>> transformer("42")
42
```

#### *property* ingress

Return decorator factory with attribute access for kinds.

This property provides a flexible interface for decorating functions to
automatically transform their arguments to specified kinds.

Usage patterns:

1. Specify kind and argument name:
   @graph.ingress(‘text’, ‘content’)
   def func(content): …
2. Specify kind only (transforms first argument):
   @graph.ingress(‘text’)
   def func(arg): …
3. Use keyword argument:
   @graph.ingress(arg_name=’text’)
   def func(arg_name): …
4. Attribute-based syntax for registered kinds:
   @graph.ingress.text(‘content’)
   def func(content): …
5. Attribute-based for first argument:
   @graph.ingress.text
   def func(arg): …

### Examples

```pycon
>>> graph = TransformationGraph()
>>> graph.add_node('text', isa=lambda x: isinstance(x, str))
>>> graph.add_node(int)
>>> @graph.register_edge('text', int)
... def text_to_int(s, ctx): return int(s)
>>> @graph.ingress('text')
... def process(x):
...     return x + ' processed'
>>> # Can now pass int, will be transformed to text first
```

#### kinds()

Get all registered kinds (nodes in the graph).

* **Returns:**
  Set of all registered kind identifiers
* **Return type:**
  [`set`](https://docs.python.org/3/builtins/stdtypes.html#set)[[`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable)]

### Examples

```pycon
>>> graph = TransformationGraph()
>>> graph.add_node('text')
>>> graph.add_node(int)
>>> 'text' in graph.kinds()
True
```

#### reachable_from(kind)

Get all kinds reachable from this kind via transformations.

* **Parameters:**
  **kind** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)) – The starting kind
* **Returns:**
  Set of all reachable kind identifiers
* **Return type:**
  [`set`](https://docs.python.org/3/builtins/stdtypes.html#set)[[`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable)]

### Examples

```pycon
>>> graph = TransformationGraph()
>>> # ... register transformations ...
>>> reachable = graph.reachable_from('text')
```

#### register(src=None, dst=None, , cost=1.0)

DEPRECATED: Use register_edge() instead.

This method is kept for backward compatibility.

* **Return type:**
  [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any), [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]], [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any), [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]]

### Examples

```pycon
>>> import warnings
>>> graph = TransformationGraph()
>>> with warnings.catch_warnings():
...     warnings.simplefilter("ignore")
...     @graph.register(str, int)
...     def str_to_int(s, ctx): return int(s)
```

#### register_edge(src=None, dst=None, , cost=1.0)

Decorator to register a transformation edge.

Can infer src/dst from function annotations if not provided.

* **Parameters:**
  * **src** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Source kind (inferred from annotations if None)
  * **dst** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Destination kind (inferred from annotations if None)
  * **cost** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Cost of this transformation
* **Return type:**
  [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)

### Examples

```pycon
>>> graph = TransformationGraph()
>>> @graph.register_edge('text', int)
... def text_to_int(s, ctx): return int(s)
```

#### set_kind_detector(detector)

Set a custom kind detector function.

The detector receives an object and returns a kind identifier or None.

* **Parameters:**
  **detector** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Any`](https://docs.python.org/3/library/typing.html#typing.Any)], [`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Function that takes an object and returns its kind or None
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### Examples

```pycon
>>> graph = TransformationGraph()
>>> def my_detector(obj):
...     if isinstance(obj, str) and obj.startswith('{"'):
...         return 'json_string'
...     return None
>>> graph.set_kind_detector(my_detector)
```

#### sources_for(kind)

Get all kinds that can be transformed to this kind.

* **Parameters:**
  **kind** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)) – The destination kind
* **Returns:**
  Set of all source kind identifiers
* **Return type:**
  [`set`](https://docs.python.org/3/builtins/stdtypes.html#set)[[`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable)]

### Examples

```pycon
>>> graph = TransformationGraph()
>>> # ... register transformations ...
>>> sources = graph.sources_for(int)
```

#### transform(obj, to_kind, , from_kind=None, context=None, use_result_cache=False)

Transform obj to to_kind.

If from_kind not specified, uses type(obj) with MRO fallback.

* **Parameters:**
  * **obj** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – Object to transform
  * **to_kind** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)) – Destination kind
  * **from_kind** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Source kind (inferred if None)
  * **context** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional context passed to transformation functions
  * **use_result_cache** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If True, cache results keyed by (id(obj), to_kind)
* **Returns:**
  Transformed object
* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)
* **Raises:**
  [**ConversionError**](_autosummary/i2.castgraph.html.md#i2.castgraph.ConversionError) – If no transformation path is found

### Examples

```pycon
>>> graph = TransformationGraph()
>>> @graph.register_edge(str, int)
... def str_to_int(s, ctx): return int(s)
>>> graph.transform("42", int)
42
```

#### transform_any(obj, to_kind, , context=None, use_result_cache=False)

Transform obj to to_kind with automatic kind detection.

Uses configured kind detector or fallback detection strategy.

* **Parameters:**
  * **obj** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – Object to transform
  * **to_kind** ([`Hashable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Hashable) | [`Kind`](_autosummary/i2.castgraph.html.md#i2.castgraph.Kind)) – Destination kind
  * **context** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional context passed to transformation functions
  * **use_result_cache** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If True, cache results
* **Returns:**
  Transformed object
* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)
* **Raises:**
  [**ConversionError**](_autosummary/i2.castgraph.html.md#i2.castgraph.ConversionError) – If no transformation path is found or kind cannot be detected

### Examples

```pycon
>>> graph = TransformationGraph()
>>> graph.add_node('text', isa=lambda x: isinstance(x, str))
>>> @graph.register_edge('text', int)
... def text_to_int(s, ctx): return int(s)
>>> graph.transform_any("42", int)
42
```

### i2.castgraph.design_guidelines()

Returns concise guidance for organizing casting in Python.

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

```pycon
>>> "registry" in design_guidelines().lower()
True
```


# _autosummary/i2.chain_map.html.md

# i2.chain_map

Merge mappings

Marked for deprecation.

### Functions

| [`is_iterable`](_autosummary/i2.chain_map.html.md#i2.chain_map.is_iterable)(x)          | Similar in nature to [`callable()`](https://docs.python.org/3/builtins/functions.html#callable), `is_iterable` returns `True` if an object is iterable, `False` if not.   |
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `is_mapping`(x)                                                          |                                                                                                                                                                           |
| `not_mapping`(x)                                                         |                                                                                                                                                                           |
| [`unique_iter`](_autosummary/i2.chain_map.html.md#i2.chain_map.unique_iter)(src[, key]) | Yield unique elements from the iterable, *src*, based on *key*, in the order in which they first appeared in *src*.                                                       |

### Classes

| [`ChainMapTree`](_autosummary/i2.chain_map.html.md#i2.chain_map.ChainMapTree)(\*maps)   | Combine/overlay multiple hierarchical mappings.   |
|-------------------------------------------------------------------------|---------------------------------------------------|

### *class* i2.chain_map.ChainMapTree(\*maps)

Bases: [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)

Combine/overlay multiple hierarchical mappings. This efficiently merges
multiple hierarchical (could be several layers deep) dictionaries, producing
a new view into them that acts exactly like a merged dictionary, but without
doing any copying.
Because it doesn’t actually copy the data, it is intended to be used only
with immutable mappings. It is safe to change *leaf* data values,
and the results will be reflected here, but changing the structure of any
of the trees will not work.

```pycon
>>> base1 = {
...     'a1': 'base1.a1',
...     'a2': 'base1.a2',
...     'a3': {
...         'b1': 'base1.a3.b1',
...         'b2': 'base1.a3.b2',
...     },
... }
>>> base2 = {
...     'a2': 'base2.a2',
...     'a3': {
...         'b2': 'base2.a3.b2',
...         'b4': 'base2.a3.b4',
...     },
...     'a4': 'base2.a4',
... }
>>>
>>> cm = ChainMapTree(base1, base2)
>>> cm['a1']
'base1.a1'
>>> cm['a2']
'base1.a2'
>>> cm['a4']
'base2.a4'
>>> cm['a3']
ChainMapTree({'b1': 'base1.a3.b1', 'b2': 'base1.a3.b2'}, {'b2': 'base2.a3.b2', 'b4': 'base2.a3.b4'})
>>> cm['a3']['b1']
'base1.a3.b1'
>>> cm['a3']['b4']
'base2.a3.b4'
>>> cm = ChainMapTree(base2, base1)
>>> cm['a1']
'base1.a1'
>>> cm['a2']
'base2.a2'
>>> cm['a4']
'base2.a4'
>>> cm['a3']
ChainMapTree({'b2': 'base2.a3.b2', 'b4': 'base2.a3.b4'}, {'b1': 'base1.a3.b1', 'b2': 'base1.a3.b2'})
>>> cm['a3']['b2']
'base2.a3.b2'
>>> cm['a3']['b1']
'base1.a3.b1'
>>>
>>> # Let's do a ChainMapTree with THREE bases now!
>>> base3 = {
...     'a2': 'base3.a2',
...     'a3': {
...         'b2': 'base3.a3.b2',
...         'b4': 'base3.a3.b4',
...     },
...     'a4': 'base3.a4',
... }
>>> cm = ChainMapTree(base3, base2, base1)
>>> cm['a2']  # will get it from base3
'base3.a2'
>>> cm['a3']['b2']  # will get it from base3 (not base2)
'base3.a3.b2'
>>> cm['a3']['b1']  # will get it from base1 (since no one else has it)
'base1.a3.b1'
```

Based on: [https://gist.github.com/Klortho/7d83975559bdcc47ac64fd7d877934f6](https://gist.github.com/Klortho/7d83975559bdcc47ac64fd7d877934f6)

#### to_dict()

Convert to dict

```pycon
>>> a = {'a': {'x': 1, 'z': 3}, 'foo': "a's foo"}
>>> b = {'a': {'y': 222, 'z': 333}, 'foo': "b's foo"}
>>> cm = ChainMapTree(a, b)
>>> # It acts like a dict when you ask for items, but is not a dict. If you want a dict, do this:
>>> cm.to_dict()
{'a': {'x': 1, 'z': 3, 'y': 222}, 'foo': "a's foo"}
>>> # Compare to normal/flat/not-nested chaining:
>>> dict(a, **b)   # Note the precedence is the inverse of ChainMapTree!
{'a': {'y': 222, 'z': 333}, 'foo': "b's foo"}
>>>
>>> # See what you get if you specify b before a
>>> ChainMapTree(b, a).to_dict()
{'a': {'y': 222, 'z': 333, 'x': 1}, 'foo': "b's foo"}
>>> # Compare to normal/flat/not-nested chaining:
>>> dict(b, **a)  # Note the precedence is the inverse of ChainMapTree!
{'a': {'x': 1, 'z': 3}, 'foo': "a's foo"}
```

### i2.chain_map.is_iterable(x)

Similar in nature to [`callable()`](https://docs.python.org/3/builtins/functions.html#callable), `is_iterable` returns
`True` if an object is iterable, `False` if not.

```pycon
>>> is_iterable([])
True
>>> is_iterable(1)
False
```

### i2.chain_map.unique_iter(src, key=None)

Yield unique elements from the iterable, *src*, based on *key*,
in the order in which they first appeared in *src*.

```pycon
>>> repetitious = [1, 2, 3] * 10
>>> list(unique_iter(repetitious))
[1, 2, 3]
```

By default, *key* is the object itself, but *key* can either be a
callable or, for convenience, a string name of the attribute on
which to uniqueify objects, falling back on identity when the
attribute is not present.

```pycon
>>> pleasantries = ['hi', 'hello', 'ok', 'bye', 'yes']
>>> list(unique_iter(pleasantries, key=lambda x: len(x)))
['hi', 'hello', 'bye']
```


# _autosummary/i2.deco.html.md

# i2.deco

Decorator tools

### Functions

| [`add_method`](_autosummary/i2.deco.html.md#i2.deco.add_method)(obj, method_func[, method_name, ...])   | Dynamically add a method to an object.                                                                                                       |
|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
| [`assert_attrs`](_autosummary/i2.deco.html.md#i2.deco.assert_attrs)(attrs)                                | Asserts, at construction time, that the class contains a specific set of attributes                                                          |
| [`double_up_as_factory`](_autosummary/i2.deco.html.md#i2.deco.double_up_as_factory)(decorator_func)               | Repurpose a decorator both as it's original form, and as a decorator factory.                                                                |
| [`ensure_iterable_args`](_autosummary/i2.deco.html.md#i2.deco.ensure_iterable_args)([func])                       | Wrap a function so that specific arguments are assured to be iterable if they meet specific conditions.                                      |
| [`get_callable_from_factory_if_no_arguments`](_autosummary/i2.deco.html.md#i2.deco.get_callable_from_factory_if_no_arguments)(...)     | Will return the input itself if it's a callable with at least one argument.                                                                  |
| [`identity`](_autosummary/i2.deco.html.md#i2.deco.identity)(obj)                                      | Return the input unchanged.                                                                                                                  |
| [`input_output_decorator`](_autosummary/i2.deco.html.md#i2.deco.input_output_decorator)([preprocess, postprocess])  | Makes a decorator that preprocesses inputs and postprocesses outputs.                                                                        |
| [`is_not_set`](_autosummary/i2.deco.html.md#i2.deco.is_not_set)(x)                                      | Return `True` if `x` is the `NotSet` sentinel, and `False` otherwise.                                                                        |
| [`kwargs_for_func`](_autosummary/i2.deco.html.md#i2.deco.kwargs_for_func)(\*funcs, \*\*kwargs)               |                                                                                                                                              |
| [`mk_args_kwargs_merger`](_autosummary/i2.deco.html.md#i2.deco.mk_args_kwargs_merger)(func)                        | Make a function that will return a dict containing all {argname: argval} pairs from a function's call.                                       |
| [`mk_call_logger`](_autosummary/i2.deco.html.md#i2.deco.mk_call_logger)([logger, what_to_log, ...])         | Makes a decorator that logs each call to the wrapped function.                                                                               |
| [`mk_input_and_output_method_wrapper`](_autosummary/i2.deco.html.md#i2.deco.mk_input_and_output_method_wrapper)([...])          | Make a method decorator transforming named arguments (`arg_trans`) and, if given, the output (`method_output_trans`).                        |
| [`mk_method_trans_spec_from_methods_specs_dict`](_autosummary/i2.deco.html.md#i2.deco.mk_method_trans_spec_from_methods_specs_dict)(...)  | Utility to make inputs for wrap_class_methods_input_and_output more easily.                                                                  |
| [`postprocess`](_autosummary/i2.deco.html.md#i2.deco.postprocess)(post[, caught_post_errors, ...])       | Add some post-processing after a function                                                                                                    |
| [`preprocess`](_autosummary/i2.deco.html.md#i2.deco.preprocess)(pre)                                    | Make a decorator that feeds the wrapped function the output of `pre`.                                                                        |
| [`preprocess_arguments`](_autosummary/i2.deco.html.md#i2.deco.preprocess_arguments)(pre)                          | Make a decorator that lets `pre` rewrite the `(args, kwargs)` of every call.                                                                 |
| [`transform_args`](_autosummary/i2.deco.html.md#i2.deco.transform_args)([dflt_trans_func])                  | Make a decorator that transforms function arguments before calling the function.                                                             |
| [`transform_class_method_input_and_output`](_autosummary/i2.deco.html.md#i2.deco.transform_class_method_input_and_output)(cls, ...)  | Replace `cls.method` in place with a version whose named arguments are transformed by `arg_trans` and whose output by `method_output_trans`. |
| [`transform_instance_method_input_and_output`](_autosummary/i2.deco.html.md#i2.deco.transform_instance_method_input_and_output)(...)    | Instance-level counterpart of `transform_class_method_input_and_output`; experimental (it emits a warning saying so).                        |
| [`transparently_wrapped`](_autosummary/i2.deco.html.md#i2.deco.transparently_wrapped)(func)                        | Wrap `func` so it is called with its positional arguments packed in one tuple.                                                               |
| [`wrap_class_methods`](_autosummary/i2.deco.html.md#i2.deco.wrap_class_methods)([...])                          | Make a decorator that wraps specific methods.                                                                                                |
| [`wrap_class_methods_input_and_output`](_autosummary/i2.deco.html.md#i2.deco.wrap_class_methods_input_and_output)([...])         | Make a decorator that wraps specific methods, transforming specific argument values a nd output values.                                      |
| [`wrap_instance_methods`](_autosummary/i2.deco.html.md#i2.deco.wrap_instance_methods)([...])                       | Make a function that wraps the named methods of an instance, as `wrap_class_methods_input_and_output` does for a class (experimental).       |
| [`wrap_method_output`](_autosummary/i2.deco.html.md#i2.deco.wrap_method_output)(wrapper_func)                   | Make a method decorator that applies `wrapper_func` to the method's output.                                                                  |
| [`wraps`](_autosummary/i2.deco.html.md#i2.deco.wraps)(wrapped[, assigned, updated])                | Copy of `functools.wraps` (kept local: it avoids a Jupyter tab-completion issue).                                                            |

### Classes

| [`FuncFactory`](_autosummary/i2.deco.html.md#i2.deco.FuncFactory)(func, \*[, include, exclude])   | Make a function factory.   |
|----------------------------------------------------------------------------------------------|----------------------------|

### Exceptions

| [`OutputPostProcessingError`](_autosummary/i2.deco.html.md#i2.deco.OutputPostProcessingError)   | Raised by `postprocess` when the post-processing function fails.   |
|------------------------------------------------------------------------------|--------------------------------------------------------------------|

### *class* i2.deco.FuncFactory(func, , include=(), exclude=())

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

Make a function factory.

but more convenient and helpful (e.g. is picklable, produces functions with
signatures, etc.)

One can use `functools.partials` to fix, or change, the defaults of
arguments of a function `func` thereby creating a different function.

```pycon
>>> def foo(a, b, *, c=2) -> float:
...     return a * b + c
>>> foo(10, 2)
22
>>> foo(10, b=2, c=3)
23
>>> from functools import partial
>>> new_foo = partial(foo, b=2, c=3)  # change default c=3 and add one: b=2
>>> new_foo(10)  # now the function can be called with one argument (couldn't before)
23
```

In essence, `FuncFactory` is equivalent to:

```text
FuncFactory = lambda func: lambda *args, **kwargs: partial(func, *args, **kwargs)
```

but more convenient and helpful. For one, it doesn’t use `lambda`, so is picklable.
It also has a more helpful signature:

```pycon
>>> factory = FuncFactory(foo)
>>> factory
<FuncFactory(foo)>(a, b, *, c=2) -> ...Callable[..., float]
```

(Note that the repr even reuses `foo`’s return annotation to tell us that our
factory will return a callable that returns that type (if the annotation is a type).

An instance of `FuncFactory` is a factory of functions, that is, it can make
functions for you based on the instance’s underlying `func`:

```pycon
>>> f = factory(b=2, c=3)
>>> f(10)
23
```

Note that:

```pycon
>>> ff = factory(2, 3)  # equivalent to ``factory(a=2, b=3)``
>>> ff(c=10)
16
```

Further, you can tell `FuncFactory` to `include` or `exclude` specific
arguments, using their names or indices to specify them.

```pycon
>>> factory_no_a = FuncFactory(foo, exclude=['a'])
>>> factory_no_a
<FuncFactory(foo)>(b, *, c=2) -> ...Callable[..., float]
>>> g = factory_no_a(2, 3)  # equivalent to ``factory(b=2, c=3)`` as no ``a`` here
>>> g(10)
23
```

Recipe: Say you’re normalizing some data accessor into callback functions and you
want to create functions that provide a specific object when called (with no args).
Sure, you can do this by specifying `lambda: obj` every time, but lambdas can be
problematic (e.g. their not picklable).

Here’s another solution:

```pycon
>>> def identity(obj):
...     return obj
>>> func_returning_obj = FuncFactory(identity)
>>> get_42 = func_returning_obj(42)
>>> get_42()
42
```

#### NOTE
A convenience property has been added to implement this recipe:

```pycon
>>> get_42, get_hello = map(FuncFactory.func_returning_obj, (42, 'hello'))
>>> get_42()
42
>>> get_hello()
'hello'
```

#### to_jdict()

Return a `{"func": ...}` dict from which `from_jdict` rebuilds the factory.

#### *classmethod* wrap(include=(), exclude=())

Return a `FuncFactory` constructor with `include` and `exclude` fixed.

### *exception* i2.deco.OutputPostProcessingError

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

Raised by `postprocess` when the post-processing function fails.

### i2.deco.add_method(obj, method_func, method_name=None, class_name=None)

Dynamically add a method to an object.

* **Parameters:**
  * **obj** – The object to add a method to
  * **method_func** – The function to use as a method. The first argument must be the object itself
    (usually called self)
  * **method_name** – The desired function name. If None, will take method_func._\_name_\_
  * **class_name** – The desired class name. If None, will take type(obj)._\_name_\_
* **Returns:**
  the object, but with the additional method (or a different function for it)

```pycon
>>> class A:
...     def __init__(self, x=10):
...         self.x = x
>>> def times(self, y):
...     return self.x * y
>>> def plus(self, y):
...     return self.x + y
>>> a = A(x=10)
>>> a = add_method(a, plus, '__call__')  # add a __call__ method, assigning it to plus
>>> a(2)
12
>>> a = add_method(a, times, '__call__')  # reassign the __call__ method to times instead
>>> a(2)
20
>>> a = add_method(a, plus, '__getitem__')  # assign the method __getitem__ to plus
>>> a[2]  # see that it works
12
>>> a(2)  # and that we still have our __call__ method
20
```

### i2.deco.assert_attrs(attrs)

Asserts, at construction time, that the class contains a specific set of attributes

* **Parameters:**
  **attrs** – An attribute name (string) or a list of attribute names whose existence needs to be enforced.
* **Returns:**
  A class decorator that will enforce the existence of the attrs when an instance is made

```pycon
>>> @assert_attrs('foo')
... class A:
...     bar = 10
...
>>> try:
...     a = A()
... except AttributeError:
...     print("AttributeError, as expected, because missing the foo attribute")
AttributeError, as expected, because missing the foo attribute
>>> @assert_attrs('foo')
... class B:
...     def foo(self): pass
>>> b = B()
>>>
>>> class A:
...     bar = 10
>>> class B:
...     def foo(self): pass
>>>
>>> @assert_attrs(['foo', 'bar'])
... class C(A, B):
...     pass
>>> c = C()
```

### i2.deco.double_up_as_factory(decorator_func)

Repurpose a decorator both as it’s original form, and as a decorator factory.
That is, from a decorator that is defined do `wrapped_func = decorator(func, **params)`,
make it also be able to do `wrapped_func = decorator(**params)(func)`.

#### NOTE
You’ll only be able to do this if all but the first argument are keyword-only,
and the first argument (the function to decorate) has a default of `None` (this is for your own good).
This is validated before making the “double up as factory” decorator.

```pycon
>>> @double_up_as_factory
... def decorator(func=None, *, multiplier=2):
...     def _func(x):
...         return func(x) * multiplier
...     return _func
...
>>> def foo(x):
...     return x + 1
...
>>> foo(2)
3
>>> wrapped_foo = decorator(foo, multiplier=10)
>>> wrapped_foo(2)
30
```

The object to wrap doesn’t have to be given positionally: it can also be given by
keyword, under the name the decorator gave its first parameter (here, `func`).
This matters because forwarding arguments through `**kwargs` is a very common way
to call a decorator, so `decorator(func=foo)` must mean what `decorator(foo)`
means:

```pycon
>>> decorator(func=foo, multiplier=10)(2)
30
>>> decorator(func=foo)(2)
6
```

It is the *absence* of an object to wrap – not the way it’s passed – that asks for
a factory:

```pycon
>>> from functools import partial
>>> isinstance(decorator(multiplier=3), partial)
True
>>> isinstance(decorator(func=foo), partial)
False
```

```pycon
>>> multiply_by_3 = decorator(multiplier=3)
>>> wrapped_foo = multiply_by_3(foo)
>>> wrapped_foo(2)
9
>>>
>>> @decorator(multiplier=3)
... def foo(x):
...     return x + 1
...
>>> foo(2)
9
```

Note that to be able to use double_up_as_factory, your first argument (the object to be wrapped) needs to default
to None and be the only argument that is not keyword-only (i.e. all other arguments need to be keyword only).

```pycon
>>> @double_up_as_factory
... def decorator_2(func, *, multiplier=2):
...     '''Should not be able to be transformed with double_up_as_factory'''
Traceback (most recent call last):
  ...
AssertionError: First argument of the decorator function needs to default to None. Was <class 'inspect._empty'>
>>> @double_up_as_factory
... def decorator_3(func=None, multiplier=2):
...     '''Should not be able to be transformed with double_up_as_factory'''
Traceback (most recent call last):
  ...
AssertionError: All arguments (besides the first) need to be keyword-only
```

Note also that the name of that first argument is effectively **reserved**: it always
means “the object to wrap”. For a decorator that also takes `**kwargs`, this means
a decorator argument can never share that name. Say a decorator’s first parameter is
`func` and it renames parameters via `**kwargs`:

```pycon
>>> @double_up_as_factory
... def rename(func=None, **new_name_for_old_name):
...     return new_name_for_old_name  # (stand-in for the real work)
```

You can rename an ordinary parameter through the factory form:

```pycon
>>> rename(b='bee')(lambda a, b: None)
{'b': 'bee'}
```

But you cannot use it to rename a parameter that happens to be called `func`:
`rename(func='callback')` is read as “wrap the object `'callback'`”, not as
“rename `func` to `callback`”, so it returns nonsense rather than a factory:

```pycon
>>> rename(func='callback')
{}
```

This is a pre-existing limitation of the double-up idiom – there is no way to tell
the two intents apart – and it is not specific to passing the object by keyword.
Before keyword-passing was supported the same call failed later and differently,
with `TypeError: rename() got multiple values for argument 'func'`.  If a decorator
needs an argument with the same name as its first parameter, don’t use
`double_up_as_factory`.

### i2.deco.ensure_iterable_args(func=None, \*\*condition_of_argname)

Wrap a function so that specific arguments are assured to be iterable if
they meet specific conditions.

The condition, in the example below, is being a string.
Note that in general, the condition needs to be a boolean function.
The explicit form of our example would say `names=lambda x: isinstance(x, str)`,
but `ensure_iterable_args` allows the convenience of just specifying the type,
or a tuple of types, and the actually boolean function will be made for you.

```pycon
>>> @ensure_iterable_args(names=str)
... def greet_people(names, greeting='Hello'):
...     for name in names:
...         yield f"{greeting} {name}!"
>>> assert list(greet_people(['Alice', 'Bob'])) == ['Hello Alice!', 'Hello Bob!']
>>> assert list(greet_people('Alice')) == ['Hello Alice!']
```

Note that to decorate a function, you can also use the form:

```pycon
>>> greet_people = ensure_iterable_args(greet_people, names=str)
```

### i2.deco.get_callable_from_factory_if_no_arguments(func_or_factory_thereof)

Will return the input itself if it’s a callable with at least one argument.
If not, it will consider it to be a factory, call it to get the actual
callable object that the user presumably is seeking to get

### i2.deco.identity(obj)

Return the input unchanged.

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

### i2.deco.input_output_decorator(preprocess=None, postprocess=None)

Makes a decorator that preprocesses inputs and postprocesses outputs.
Use it if you want to transform the input of a function or method before calling it, or if you want
to transform the returned value before returning it.

* **Parameters:**
  * **preprocess** – Function to be applied to input
  * **postprocess** – Function to be applied to output
* **Returns:**
  a decorator that preprocesses inputs and postprocesses outputs

#### SEE ALSO
preprocess and postprocess decorators if you need only to pre or post process!

```pycon
>>> # Examples with "normal functions"
>>> def f(x=3):
...     '''Some doc...'''
...     return x + 10
>>> ff = input_output_decorator()(f)
>>> print((ff(5.0)))
15.0
>>> ff = input_output_decorator(preprocess=int)(f)
>>> print((ff(5.0)))
15
>>> ff = input_output_decorator(preprocess=int, postprocess=lambda x: "Hello {}!".format(x))(f)
>>> print((ff('5')))
Hello 15!
>>> ff = input_output_decorator(postprocess=lambda x: "Hello {}!".format(x))(f)
>>> print((ff(5.0)))
Hello 15.0!
>>> print((ff.__doc__))
Some doc...
>>>
>>> # examples with methods (bounded, class methods, static methods
>>> class F:
...     '''This is not what you'd expect: The doc of the class, not the function'''
...     def __init__(self, y=10):
...         '''Initialize'''
...         self.y = y
...     def __call__(self, x=3):
...         '''Some doc...'''
...         return self.y + x
...     @staticmethod
...     def static_method(x, y):
...         return "What {} {} you have".format(x, y)
...     @classmethod
...     def class_method(cls, x):
...         return "{} likes {}".format(cls.__name__, x)
>>>
>>> f = F()
>>> ff = input_output_decorator()(f)
>>> print((ff(5.0)))
15.0
>>> ff = input_output_decorator(preprocess=int)(f)
>>> print((ff(5.0)))
15
>>> ff = input_output_decorator(preprocess=int, postprocess=lambda x: "Hello {}!".format(x))(f)
>>> print((ff('5')))
Hello 15!
>>> ff = input_output_decorator(postprocess=lambda x: "Hello {}!".format(x))(f)
>>> print((ff(5.0)))
Hello 15.0!
>>> print((ff.__doc__))
This is not what you'd expect: The doc of the class, not the function
```

# >>>

### i2.deco.is_not_set(x)

Return `True` if `x` is the `NotSet` sentinel, and `False` otherwise.

Signature consumers (UI or schema generators, for example) can use it to treat a
`NotSet` default like `inspect.Parameter.empty`, i.e. “required, no default”:

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

```pycon
>>> from inspect import Parameter
>>> is_not_set(NotSet)
True
>>> is_not_set(None), is_not_set(Parameter.empty), is_not_set("NotSet")
(False, False, False)
>>> def default_or_empty(param):
...     return Parameter.empty if is_not_set(param.default) else param.default
>>> p = Parameter('x', Parameter.KEYWORD_ONLY, default=NotSet)
>>> default_or_empty(p) is Parameter.empty
True
```

### i2.deco.kwargs_for_func(\*funcs, \*\*kwargs)

* **Parameters:**
  * **funcs**
  * **kwargs**
* **Returns:**

```pycon
>>> from i2.tests.objects_for_testing import formula1, sum_of_args, mult, add
>>> def print_dict(d):  # just a util for this doctest
...     from pprint import pprint
...     pprint({k.__name__: d[k] for k in sorted(d, key=lambda x: x.__name__)})
>>> print_dict(kwargs_for_func(formula1, mult, add,
...                           w=1, x=2, z=3, a=4, b=5))
{'add': {'a': 4, 'b': 5},
 'formula1': {'w': 1, 'x': 2, 'z': 3},
 'mult': {'x': 2}}
```

### i2.deco.mk_args_kwargs_merger(func)

Make a function that will return a dict containing all {argname: argval} pairs from a function’s call.
That is, it merges all non-keyword arguments with the keyword-arguments, with the right name, so that
the arguments can be handled more uniformly.

* **Parameters:**
  **func** – The function that will be called, whose signature should be looked at to make the
  merging function
* **Returns:**
  A function merge_args_and_kwargs(args, kwargs) that can be used to merge arguments

```pycon
>>> def func(a, b, c=3):
...     return a * (b + c)
>>> merger = mk_args_kwargs_merger(func)
>>> dict(merger([1], {'b': 10}))
{'a': 1, 'b': 10}
>>> dict(merger([], {'a': 1, 'b': 10}))
{'a': 1, 'b': 10}
>>> dict(merger([1, 10], {}))
{'a': 1, 'b': 10}
>>> dict(merger([], {}))
{}
>>> # Usage demo:
>>> assert func(*[1], **{'b': 10}) == func(**merger([1], {'b': 10}))
>>> assert func(*[], **{'a': 1, 'b': 10}) == func(**merger([], {'a': 1, 'b': 10}))
>>> assert func(**{'a': 1, 'b': 10}) == func(**merger([], {'a': 1, 'b': 10}))
```

### i2.deco.mk_call_logger(logger=<built-in function print>, what_to_log=<function \_call_signature>, log_output=False, func_is_bounded=False)

Makes a decorator that logs each call to the wrapped function.

* **Parameters:**
  * **logger** – The actual function that logs stuff. Default is print. The “stuff” it logs is given by
    the what_to_log argument (a function).
  * **what_to_log** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable), [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – A function taking inputs (func, args, kwargs) of the call, and returning something to log
    (usually, and by default, a string)
  * **func_is_bounded** – Whether the function is bounded (like a method) or not
* **Returns:**
  A decorator

```pycon
>>> # Example of use on (unbounded) function, with default args
>>> @mk_call_logger()
... def useless_computation(x, y=2, z='foo'):
...     return z * (x + y)
...
>>> _ = useless_computation(3, y=1, z='ha')
useless_computation(3, y=1, z='ha')
```

The same example, but with output logging too

```pycon
>>> @mk_call_logger(log_output=True)
... def useless_computation(x, y=2, z='foo'):
...     return z * (x + y)
>>> _ = useless_computation(3, y=1, z='ha')
useless_computation(3, y=1, z='ha')
-> hahahaha
```

And now a bit more involved…

```pycon
>>>
>>> # Example of use on class method, with a different what_to_log function.
>>> class A:
...     def __init__(self, a=10):
...         self.a = a
...     def add(self, x):
...         return self.a + x
...     def multiply(self, x):
...         return self.a * x
...
>>> def _name_args_kwargs(func, args, kwargs) -> str:
...     return "Calling {} with\n  args={}\n  kwargs={}".format(func.__name__, args, kwargs)
...
>>>
>>> log_calls = mk_call_logger(what_to_log=_name_args_kwargs, func_is_bounded=True)
>>> for method in ['add', 'multiply']:
...     A_method = getattr(A, method)
...     setattr(A, method, mk_call_logger(what_to_log=_name_args_kwargs, func_is_bounded=True)(A_method))
...
>>>
>>> a = A()
>>> a.add(x=2)
Calling add with
  args=()
  kwargs={'x': 2}
12
>>> a.multiply(2)
Calling multiply with
  args=(2,)
  kwargs={}
20
```

### i2.deco.mk_input_and_output_method_wrapper(method_output_trans=None, \*\*arg_trans)

Make a method decorator transforming named arguments (`arg_trans`) and, if
given, the output (`method_output_trans`).

### i2.deco.mk_method_trans_spec_from_methods_specs_dict(methods_specs_dict)

Utility to make inputs for wrap_class_methods_input_and_output more easily.

* **Parameters:**
  **methods_specs_dict** – a dict where
  keys are method names (either a single string, or a tuple of strings)
  values are the trans_spec dicts that should be associated to those methods
* **Returns:**
  A dict in the method_trans_spec (input of wrap_class_method) format.

```pycon
>>> methods_specs_dict = {}
>>> methods_specs_dict['foo'] = {'x': str, 'y': int}
>>> methods_specs_dict[('foo', 'bar')] = {'z': list, 'method_output_trans': float}
>>> methods_specs_dict[('bar', )] = {'zz': int}
>>> method_trans_spec = mk_method_trans_spec_from_methods_specs_dict(methods_specs_dict)
>>> list(method_trans_spec.keys())
['foo', 'bar']
>>> method_trans_spec['foo']
{'x': <class 'str'>, 'y': <class 'int'>, 'z': <class 'list'>, 'method_output_trans': <class 'float'>}
>>> method_trans_spec['bar']
{'z': <class 'list'>, 'method_output_trans': <class 'float'>, 'zz': <class 'int'>}
```

### i2.deco.postprocess(post, caught_post_errors=(<class 'Exception'>, ), verbose_error_message=False)

Add some post-processing after a function

* **Parameters:**
  **post** – The function to apply to the output

```pycon
>>> list_range = postprocess(list)(range)
>>> list_range(4)
[0, 1, 2, 3]
>>> sum_range = postprocess(sum)(range)
>>> sum_range(4)
6
```

#### NOTE
The decorator also sticks the return annotation of the post function on the wrapped one.

Use cases:

- Changing a generator into a container returning function
  In many situations, writing a generator is simpler than writing a function
  that accumulates a list or a dict etc.
  So here, you just write the generator and tag this decorator on top, to get the same effect.

```pycon
>>> from inspect import signature
>>> @postprocess(dict)
... def bar(x):
...     for i in range(x):
...         yield str(i), i
>>> bar(3)
{'0': 0, '1': 1, '2': 2}
>>> signature(bar)
<Signature (x) -> dict>
>>>
>>> @postprocess(list)
... def foo(x):
...     for i in range(x):
...         yield i
>>> foo(3)
[0, 1, 2]
>>> from inspect import signature
>>> signature(foo)
<Signature (x) -> list>
```

- Triggering something (like logging, or forwarding) when a function returns

```pycon
>>> def log_this(x):
...     print(f"Logging {x}")
...     return x
>>> logged_foo = postprocess(log_this)(foo)
>>> t = logged_foo(2)
Logging [0, 1]
>>> assert t == [0, 1]
```

- Using a function that does a lot to make several functions that do less.
  (e.g. Extracting/making a python object from a function returning a raw http response)

### i2.deco.preprocess(pre)

Make a decorator that feeds the wrapped function the output of `pre`.

The wrapped function receives a single argument: `pre(*args, **kwargs)`, computed
from whatever the caller passed.

```pycon
>>> @preprocess(int)
... def double(x):
...     return 2 * x
>>> double("21")
42
```

#### SEE ALSO
`postprocess`: apply a function to the output instead.
`preprocess_arguments`: `pre` returns the `(args, kwargs)` pair to call the
wrapped function with, instead of a single value.

### i2.deco.preprocess_arguments(pre)

Make a decorator that lets `pre` rewrite the `(args, kwargs)` of every call.

`pre(*args, **kwargs)` must return an `(args, kwargs)` pair; the wrapped
function is then called with that pair.

```pycon
>>> @preprocess_arguments(lambda *args, **kwargs: (
...     tuple(int(a) for a in args), {k: int(v) for k, v in kwargs.items()}
... ))
... def add(a, b):
...     return a + b
>>> add("1", b="2")
3
```

#### SEE ALSO
`preprocess`: `pre` returns a single value that becomes the only argument.
`transform_args`: transform named arguments one by one.

### i2.deco.transform_args(dflt_trans_func=None, , \*\*trans_func_for_arg)

Make a decorator that transforms function arguments before calling the function.
Works with plain functions and bounded methods.
For example:

> * original argument: a relative path –> used argument: a full path
> * original argument: a pickle filepath –> used argument: the loaded object
* **Parameters:**
  * **rootdir** – rootdir to be used for all name arguments of target function
  * **name_arg** – the position (int) or argument name of the argument containing the name
* **Returns:**
  a decorator

```pycon
>>> # Example with a plain function
>>> def f(a, b, c='default_c'):
...     return "a={a}, b={b}, c={c}".format(a=a, b=b, c=c)
>>> def prepend_root(x):
...     return 'ROOT/' + x
>>>
>>> def test(f):
...     assert f('foo', 'bar', 3) == 'a=foo, b=bar, c=3'
...     ff = transform_args()(f)  # no transformation specification, so function is unchanged
...     assert ff('foo', 'bar', c=3) == 'a=foo, b=bar, c=3'
...     ff = transform_args(a=prepend_root)(f)  # prepend root to a
...     assert ff('foo', c=3, b='bar') == 'a=ROOT/foo, b=bar, c=3'  # note: testing different order of args
...     ff = transform_args(b=prepend_root)(f)  # prepend root to b
...     assert ff(c=3, b='bar', a='foo') == 'a=foo, b=ROOT/bar, c=3'  # note: testing different order of args
...     ff = transform_args(a=prepend_root, b=prepend_root)(f)  # prepend root to a and b
...     assert ff('foo', 'bar', 3) == 'a=ROOT/foo, b=ROOT/bar, c=3'
...     assert ff('foo', 'bar') == 'a=ROOT/foo, b=ROOT/bar, c=default_c'  # defaults still work
>>>
>>> test(f)
>>>
>>> # Example with bounded method, wrapping from instance
>>> class A:
...     def __init__(self, sep=''):
...         self.sep = sep
...     def f(self, a, b, c='default_c'):
...         return f"a={a}{self.sep} b={b}{self.sep} c={c}"
>>>
>>> a = A(sep=',')
>>> test(a.f)
>>>
>>> # Example with bounded method, wrapping from class
>>> A.f = transform_args(a=prepend_root, b=prepend_root)(A.f)
>>> a = A(sep=',')
>>> assert a.f('foo', 'bar', 3) == 'a=ROOT/foo, b=ROOT/bar, c=3'
>>> assert a.f('foo', 'bar') == 'a=ROOT/foo, b=ROOT/bar, c=default_c'  # defaults still work
```

### i2.deco.transform_class_method_input_and_output(cls, method, method_output_trans=None, \*\*arg_trans)

Replace `cls.method` in place with a version whose named arguments are
transformed by `arg_trans` and whose output by `method_output_trans`.

### i2.deco.transform_instance_method_input_and_output(obj, method, method_output_trans=None, \*\*arg_trans)

Instance-level counterpart of `transform_class_method_input_and_output`;
experimental (it emits a warning saying so).

### i2.deco.transparently_wrapped(func)

Wrap `func` so it is called with its positional arguments packed in one tuple.

### i2.deco.wrap_class_methods(\_return_a_copy_of_the_class=True, \_raise_error_if_non_existent_method=True, \*\*wrapper_for_method)

Make a decorator that wraps specific methods.

#### IMPORTANT
The decorator will by default return a copy of the class. This might incur some run time overhead.
If this is desirable, for example, when you want to create several decorations of a same class.
If you want to change the class itself (e.g. you’re only loading it once in a module, and decorating it), then
specify \_return_a_copy_of_the_class=False

Note that \_return_a_copy_of_the_class=True has a side effect of building russian dolls of essentially subclasses
of the class, which may have some undesirable results if repeated too many times.

* **Parameters:**
  * **\_return_a_copy_of_the_class** – Specifies whether to
    return a copy of the class (_return_a_copy_of_the_class=True, the default),
    or change the actual loaded class itself (_return_a_copy_of_the_class=False)
  * **wrapper_for_method** – method_name=wrapper_function pairs.
* **Returns:**
  A class wrapper. That is, a decorator that takes a class and returns a decorated version of it
  (or decaorates “in-place” if \_return_a_copy_of_the_class=False

#### SEE ALSO
* wrap_method_output: The function that is called for every method we wrap.
* transform_class_method_input_and_output: A wrap_class_methods that is specialized for input arg and output
  : transformation.

```pycon
>>> from functools import wraps
>>> class A:
...     def __init__(self, a=10):
...         self.a = a
...     def add(self, x):
...         return self.a + x
...     def multiply(self, x):
...         return self.a * x
...
>>> a = A()
>>> a.add(2)
12
>>> a.multiply(2)
20
>>>
>>> def log_calls(func):
...     name = func.__name__
...     @wraps(func)
...     def _func(self, *args, **kwargs):
...         print("Calling {} with\n  args={}\n  kwargs={}".format(name, args, kwargs))
...         return func(self, *args, **kwargs)
...     return _func
...
>>> AA = wrap_class_methods(**{k: log_calls for k in ['add', 'multiply']})(A)
>>> a = AA()
>>> a.add(x=3)
Calling add with
  args=()
  kwargs={'x': 3}
13
>>> a.multiply(3)
Calling multiply with
  args=(3,)
  kwargs={}
30
```

### i2.deco.wrap_class_methods_input_and_output(\_return_a_copy_of_the_class=True, \_raise_error_if_non_existent_method=True, \*\*method_trans_spec)

Make a decorator that wraps specific methods, transforming specific argument values a nd output values.

#### IMPORTANT
The decorator will by default return a copy of the class. This might incur some run time overhead.
If this is desirable, for example, when you want to create several decorations of a same class.
If you want to change the class itself (e.g. you’re only loading it once in a module, and decorating it), then
specify \_return_a_copy_of_the_class=False

* **Parameters:**
  * **\_return_a_copy_of_the_class** – Specifies whether to
    return a copy of the class (_return_a_copy_of_the_class=True, the default),
    or change the actual loaded class itself (_return_a_copy_of_the_class=False)
  * **method_trans_spec** – method_name=trans_specs_for_method pairs.
    The trans_specs_for_method is a dict that is understood by transform_class_method_input_and_output.
    Except for one special case, it’s keys are argument names and values are callables to call on those
    arguments’ values.
    The special case is method_output_trans. This specifies that the callable it points to should be called
    on output of method. Here’s one recipe for outputs: If the output of a function is an iterable and you want
    to apply a function trans to each element of the output, specify method_output_trans=lambda x: map(trans, x).
* **Returns:**
  A wrapped class

#### SEE ALSO
* mk_method_trans_spec_from_methods_specs_dict: a utility to make method_trans_spec more easily
* transform_class_method_input_and_output: The function that is called for every method we wrap.

In the following, we will show two examples.

- The first is a toy example to demonstrate the basic functionality.
- The second demonstrates a more involved case, but is still a silly example.
- The third demonstrates more the type of application we’d use wrap_class_methods_input_and_output for in real life.

### FIRST EXAMPLE

We make an Ops class that wraps Counter, allowing one to add items and show the counts of items added.

```pycon
>>> from collections import UserDict
>>> import re
>>> from collections import Counter
>>>
>>> class Ops:
...     def __init__(self):
...         self.counter = Counter()
...     def add_item(self, item):
...         self.counter.update({item: 1})
...     def show(self):
...         return self.counter
>>> # Here's an example of what Ops does
>>> ops = Ops()
>>> for item in ['this', 'is', 'that', 'and', 'that', 'is', 'this']:
...     ops.add_item(item)
...
>>> ops.show()
Counter({'this': 2, 'is': 2, 'that': 2, 'and': 1})
>>>
>>> # But say we don't want to count actual words added, but just the first two letters of these words,
>>> # and say we want to show() to return the dict, not the Counter.
>>> NewOps = wrap_class_methods_input_and_output(
...     _return_a_copy_of_the_class=False,
...     add_item=dict(item=lambda x: x[:2]),  # intercept items fed to add_item and keep only 2 first letters
...     show=dict(method_output_trans=dict)  # intercept output of show method, converting to dict
... )(Ops)
>>> # let's try it out!
>>> ops = NewOps()
>>> for item in ['this', 'is', 'that', 'and', 'that', 'is', 'this']:
...     ops.add_item(item)
...
>>> ops.show()
{'th': 4, 'is': 2, 'an': 1}
>>> # See that we specified _return_a_copy_of_the_class=False?
>>> # Now look at what happens if we try to use Ops, the original class, again. It behaves like NewOps.
>>> # That's usually not the behavior we want, so be careful!
>>> ops = Ops()
>>> for item in ['this', 'is', 'that', 'and', 'that', 'is', 'this']:
...     ops.add_item(item)
...
>>> ops.show()
{'th': 4, 'is': 2, 'an': 1}
>>>
>>>
```

### SECOND EXAMPLE

Wrap a dict (or rather, the safer collections.UserDict), doing weird things to the input and output
keys and values

```pycon
>>> val_in_trans = lambda x: 'hello {}'.format(x)  # prepend "hello " to incoming values
>>> val_out_trans = lambda x: re.sub('hello', 'hi', x)  # replace "hello" by "hi" in output values
>>> key_in_trans = lambda x: '__' + x  # prepend incoming keys with double underscore
>>> key_out_trans = lambda x: x[2:]  # remove the first two characters (underscores) from keys when output
>>>
>>> methods_specs_dict = {
...     ('__contains__', '__getitem__', '__setitem__', '__delitem__'): dict(key=key_in_trans),
...     '__setitem__': dict(item=val_in_trans),
...     '__iter__': dict(method_output_trans=lambda x: map(key_out_trans, x)),
...     '__getitem__': dict(method_output_trans=val_out_trans)
... }
>>>
>>> methods_specs_dict = mk_method_trans_spec_from_methods_specs_dict(methods_specs_dict)
>>>
>>> @wrap_class_methods_input_and_output(**methods_specs_dict)
... class AA(UserDict):
...     pass
...
>>> aa = AA()
>>> aa['foo'] = 'shoo'  # store 'shoo' under 'foo'
>>> # the __str__ method isn't wrapped, so we see the actual STORED keys and values
>>> # we see that __foo, not foo is the actual key, and "hello shoo" the value:
>>> assert str(aa) == "{'__foo': 'hello shoo'}"
>>> assert 'foo' in aa  # yet from the interface, it looks like 'foo' is a key of aa...
>>> assert '__foo' not in aa  # ... and '__foo' is not a key.
>>> aa['foo'] = 'bar'  # let's replace the value of 'foo'
>>> assert str(aa) == "{'__foo': 'hello bar'}"  # see what's stored
>>> aa['star'] = 'wars'  # let's add another
>>> assert list(aa) == ['foo', 'star']  # what are the keys? (this uses __iter__ under the hood)
>>> # In the following, we'll use methods keys(), values(), and items(), none of which we wrapped.
>>> # And yet, they work as expected, since they pass on their work to methods we wrapped.
>>> assert list(aa.keys()) == ['foo', 'star']  # another way to get keys
>>> # see here that when we ask for values, we don't get what we asked to store, ...
>>> # ... nor what is actually stored, but something else
>>> assert list(aa.values()) == ['hi bar', 'hi wars']
>>> assert str(list(aa.items())) == "[('foo', 'hi bar'), ('star', 'hi wars')]"  # the keys and values we get from items()
>>> assert str(aa) == "{'__foo': 'hello bar', '__star': 'hello wars'}"  # what is actually stored
>>> del aa['foo']  # testing deletion of a key
>>> assert str(aa) == "{'__star': 'hello wars'}"  # it worked!
>>>
>>>
```

### THIRD EXAMPLE

Here again, we’ll wrap UserDict. But instead of being silly, we’ll pretend we need to store waveforms
in binary format (so input values will have to be wrapped), but still retrieving these waveforms as lists
(so output values will have to be wrapped).
Additionally, we’ll pretend we’re working with wav files within some root directory, but don’t
want the root dir or the ‘.wav’ extension to appear in our keys. So we’ll have to wrap input and output keys.
Of course, this is just pretend. Don’t use this with real waveforms. It won’t work.

```pycon
>>> root = '/ROOT/DIR/'
>>> abs_path_of_rel_path = lambda rel_path: root + rel_path + '.wav'  # transform a relative path to an absolute one
>>> rel_path_of_abs_path = lambda x: x.replace(root, '').replace('.wav', '')  # transform an absolute path to a relative one
>>> list_to_bytes = bytes
>>> bytes_to_list = list
>>>
>>> methods_specs_dict = {
...     ('__contains__', '__getitem__', '__setitem__', '__delitem__'): dict(key=abs_path_of_rel_path),
...     '__setitem__': dict(item=list_to_bytes),
...     '__iter__': dict(method_output_trans=lambda x: map(rel_path_of_abs_path, x)),
...     '__getitem__': dict(method_output_trans=bytes_to_list)
... }
>>>
>>> methods_specs_dict = mk_method_trans_spec_from_methods_specs_dict(methods_specs_dict)
>>>
>>> @wrap_class_methods_input_and_output(**methods_specs_dict)
... class Wf(UserDict):
...     pass
...
>>> year = [2, 0, 1, 9]
>>> down = [5, 4, 3, 2, 1]
>>>
>>> wf = Wf()
>>> wf['year'] = year
>>> print(str(wf).replace("b'", "'"))
{'/ROOT/DIR/year.wav': '\x02\x00\x01\t'}
>>> 'year' in wf
True
>>> wf['down'] = down
>>> print(str(wf).replace("b'", "'"))
{'/ROOT/DIR/year.wav': '\x02\x00\x01\t', '/ROOT/DIR/down.wav': '\x05\x04\x03\x02\x01'}
>>> list(wf.keys())
['year', 'down']
>>> list(wf.values())
[[2, 0, 1, 9], [5, 4, 3, 2, 1]]
>>> list(wf.items())
[('year', [2, 0, 1, 9]), ('down', [5, 4, 3, 2, 1])]
>>> len(wf)
2
>>> del wf['year']
>>> len(wf)
1
>>> list(wf.items())
[('down', [5, 4, 3, 2, 1])]
```

### i2.deco.wrap_instance_methods(\_return_a_copy_of_the_class=True, \_raise_error_if_non_existent_method=True, \*\*method_trans_spec)

Make a function that wraps the named methods of an instance, as
`wrap_class_methods_input_and_output` does for a class (experimental).

`_return_a_copy_of_the_class` is accepted for symmetry but not used.

### i2.deco.wrap_method_output(wrapper_func)

Make a method decorator that applies `wrapper_func` to the method’s output.

### i2.deco.wraps(wrapped, assigned=('_\_module_\_', '_\_name_\_', '_\_qualname_\_', '_\_doc_\_', '_\_annotations_\_', '_\_type_params_\_'), updated=('_\_dict_\_',))

Copy of `functools.wraps` (kept local: it avoids a Jupyter tab-completion issue).


# _autosummary/i2.doc_mint.html.md

# i2.doc_mint

Meta-interfaces

### Functions

| [`assert_wants`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.assert_wants)(example, \*args, \*\*kwargs)         | Render a `doctest.Example` as an `assert` comparing its source to its want.                                                                                     |
|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`convert_string`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.convert_string)(s, converters)                     | Convert `s` with the first converter that matches (a dict containing `s` as a key, or a callable returning something other than None); return `s` if none does. |
| [`docstring_to_params`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.docstring_to_params)(docstring, \*[, ...])         | Parse a docstring and extract parameter specifications.                                                                                                         |
| [`doctest_string`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.doctest_string)(obj[, example_callback, recurse])  | Extract the doctests found in given object.                                                                                                                     |
| [`doctest_string_print`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.doctest_string_print)(obj[, ...])                  | Extract the doctests found in given object.                                                                                                                     |
| [`doctest_string_trans_lines`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.doctest_string_trans_lines)(doctest_obj[, ...])    | Yield `example_callback(example)` for each example of a `doctest.DocTest`.                                                                                      |
| [`find_in_params`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.find_in_params)(query, params, \*[, search_in])    | Find parameters in a list of parameter specifications.                                                                                                          |
| [`indent_lines`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.indent_lines)(string, indent)                      | Indent each line of a string.                                                                                                                                   |
| [`inject_docstring_content`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.inject_docstring_content)(to_inject, \*[, ...])    | Inject content into the docstring of a function.                                                                                                                |
| [`literal_eval_converter`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.literal_eval_converter)(s[, max_length])           | Evaluate `s` as a Python literal, or return None when it is not one (or is longer than `max_length`, or contains a newline or `;`).                             |
| [`mk_example_wants_callback`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.mk_example_wants_callback)(source_want_func)       | Make a `doctest.Example` callback from a `(source, want) -> str` function.                                                                                      |
| [`most_common_indent`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.most_common_indent)(string[, ignore_first_line])   | Find the most common indentation in a string.                                                                                                                   |
| [`non_doctest_lines`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.non_doctest_lines)(doc)                            | Generator of lines of the doc string that are not in a doctest scope.                                                                                           |
| [`old_doctest_string`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.old_doctest_string)(obj[, output_prefix, ...])     | Extract the doctests found in given object.                                                                                                                     |
| [`output_prefix`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.output_prefix)(example, \*args, \*\*kwargs)        | Render a `doctest.Example` as its source followed by a `# OUTPUT:` line.                                                                                        |
| [`params_to_docstring`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.params_to_docstring)(params, \*[, doc_style, ...]) | Generate a docstring from a list of parameter specifications.                                                                                                   |
| [`register_converter`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.register_converter)(converter)                     | Register a new converter.                                                                                                                                       |
| [`split_line_comments`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.split_line_comments)(s)                            | Split a single line into its code and its `#` comment (empty if none).                                                                                          |
| [`split_text_and_doctests`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.split_text_and_doctests)(doc_string)               | Generates alternating blocks of "text" (string) and "doctest blocks" (`DoctestBlock` instances, which are essentially a list of `ExampleX` instances).          |
| [`string_param_to_obj`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.string_param_to_obj)(string_to_object_mapping)     | Convert a string representation of a parameter to an object.                                                                                                    |
| [`strip_comments`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.strip_comments)(code)                              | Remove whole-line `#` comments from `code` (inline comments are kept).                                                                                          |

### Classes

| [`DoctestBlock`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.DoctestBlock)([seq])                            | A list that (should) contain doctest Example instances   |
|-------------------------------------------------------------------------------------------------|----------------------------------------------------------|
| [`ExampleX`](_autosummary/i2.doc_mint.html.md#i2.doc_mint.ExampleX)(source[, want, exc_msg, lineno, ...]) | doctest.Example eXtended to have more convenient methods |

### *class* i2.doc_mint.DoctestBlock(seq=())

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

A list that (should) contain doctest Example instances

### *class* i2.doc_mint.ExampleX(source, want=None, exc_msg=None, lineno=0, indent=0, options=None)

Bases: [`Example`](https://docs.python.org/3/library/doctest.html#doctest.Example)

doctest.Example eXtended to have more convenient methods

### i2.doc_mint.assert_wants(example, \*args, \*\*kwargs)

Render a `doctest.Example` as an `assert` comparing its source to its want.

```pycon
>>> import doctest
>>> assert_wants(doctest.Example(source='1 + 1\n', want='2\n'))
'assert (1 + 1) == 2 #'
```

### i2.doc_mint.convert_string(s, converters)

Convert `s` with the first converter that matches (a dict containing `s` as a
key, or a callable returning something other than None); return `s` if none does.

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

```pycon
>>> convert_string("None", dflt_str_to_obj_converters), convert_string("3.5", dflt_str_to_obj_converters)
(None, 3.5)
>>> convert_string("hello", dflt_str_to_obj_converters)
'hello'
```

### i2.doc_mint.docstring_to_params(docstring, \*, doc_style='numpy', converters=[{'-inf': -inf, 'False': False, 'None': None, 'True': True, 'bool': <class 'bool'>, 'complex': <class 'complex'>, 'dict': <class 'dict'>, 'float': <class 'float'>, 'inf': inf, 'int': <class 'int'>, 'list': <class 'list'>, 'nan': nan, 'set': <class 'set'>, 'str': <class 'str'>, 'tuple': <class 'tuple'>}, <function literal_eval_converter>])

Parse a docstring and extract parameter specifications.

* **Parameters:**
  * **docstring** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The docstring to parse.
  * **doc_style** ([`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)[`'numpy'`, `'google'`, `'rest'`]) – The style of the docstring to parse. One of ‘numpy’, ‘google’, or ‘rest’.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  A list of parameter specifications, where each specification is a dictionary containing:
  - name: The name of the parameter (str).
  - default: The default value of the parameter (str, optional).
  - annotation: The type annotation for the parameter (str, optional).
  - description: A description of the parameter (str).

**Examples**

```pycon
>>> docstring = '''
... Parameters
... ----------
... x : int, default=1
...     An integer value.
... y : str, default=None
...     An optional string.
... '''
>>> params = docstring_to_params(docstring)
>>> params  == [
...     {'name': 'x', 'default': 1, 'annotation': int, 'description': 'An integer value.'},
...     {'name': 'y', 'default': None, 'annotation': str, 'description': 'An optional string.'}
... ]
True
```

```pycon
>>> docstring = '''
... Args:
...     x (int, optional): An integer value. Defaults to 1.
...     y (str, optional): An optional string. Defaults to None.
... '''
>>> params = docstring_to_params(docstring, doc_style='google')
>>> params == [
...     {"name": "x", "default": 1, "annotation": int, "description": "An integer value."},
...     {"name": "y", "default": None, "annotation": str, "description": "An optional string."},
... ]
True
```

```pycon
>>> docstring = '''
... :param x: An integer value. (Default: 1)
... :type x: int
... :param y: An optional string. (Default: None)
... :type y: str
... '''
>>> params = docstring_to_params(docstring, doc_style='rest')
>>> params == [
...     {"name": "x", "default": 1, "annotation": int, "description": "An integer value."},
...     {"name": "y", "default": None, "annotation": str, "description": "An optional string."},
... ]
True
```

### i2.doc_mint.doctest_string(obj, example_callback=<function mk_example_wants_callback.<locals>.example_wants_callback>, recurse=True)

Extract the doctests found in given object.

* **Parameters:**
  * **obj** – Object (module, class, function, etc.) you want to extract doctests from.
  * **recurse** – Whether the process should find doctests in the attributes of the object, recursively.
* **Params output_prefix:**
* **Returns:**
  A string containing the doctests, with output lines prefixed by ‘# Output:’

### i2.doc_mint.doctest_string_print(obj, example_callback=<function mk_example_wants_callback.<locals>.example_wants_callback>, recurse=True)

Extract the doctests found in given object.

* **Parameters:**
  * **obj** – Object (module, class, function, etc.) you want to extract doctests from.
  * **recurse** – Whether the process should find doctests in the attributes of the object, recursively.
* **Returns:**
  A string containing the doctests, with output lines prefixed by ‘# Output:’

### i2.doc_mint.doctest_string_trans_lines(doctest_obj, example_callback=<function mk_example_wants_callback.<locals>.example_wants_callback>)

Yield `example_callback(example)` for each example of a `doctest.DocTest`.

### i2.doc_mint.find_in_params(query, params, , search_in=('name', 'description'))

Find parameters in a list of parameter specifications.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The query to search for.
  * **params** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable) | [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – 

    The list of parameter specifications.
    Params can be provided in two formats:
    - A function, from which the params will be extracted from the docstring.
    - A list of dictionaries where each dictionary specifies a parameter, containing:
      > - name: The name of the parameter (str).
      > - default: The default value of the parameter (any, optional).
      > - annotation: The type annotation for the parameter (str, optional).
      > - description: A description of the parameter (str).

    If a callable is provided, it will be used to generate the list of parameter specifications.
  * **search_in** – The fields to search in each parameter specification.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  A list of parameter specifications that match the query.

### Examples

```pycon
>>> params = [
...     {"name": "x", "default": 1, "annotation": "int", "description": "An integer value."},
...     {"name": "y", "default": None, "annotation": "str", "description": "An optional string."},
... ]
>>> find_in_params('int', params)
[{'name': 'x', 'default': 1, 'annotation': 'int', 'description': 'An integer value.'}]
```

### i2.doc_mint.indent_lines(string, indent)

Indent each line of a string.

* **Parameters:**
  * **string** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The string to indent.
  * **indent** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The string to use for indentation.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The indented string.

**Examples**

```pycon
>>> print(indent_lines('This is a test.\nAnother line.', ' ' * 8))
        This is a test.
        Another line.
```

### i2.doc_mint.inject_docstring_content(to_inject, , position=-1, indent=True)

Inject content into the docstring of a function.

#### NOTE
If you use the decorator on a string, it will assume that string is the doc
string you want to transform and return the modified string directly.

* **Parameters:**
  * **to_inject** – The content to inject.
  * **position** – The position in the docstring to inject the content.
    If an integer, the content is injected at that line number (pushing the rest down).
    If a string, will consider it as a regex pattern to match the line to inject after.
    Default is -1, to inject at the end.
  * **indent** – Control on indent.
    If True, will use the most common indent of the input docstrings.
    If a string, it will use that specific string.
* **Returns:**
  A decorator that injects the content into the docstring of the decorated function.

**Examples**

```pycon
>>> @inject_docstring_content('This is a test.')
... def test_func():
...     '''This is the docstring.'''
...     pass
>>> test_func.__doc__
'This is the docstring.\nThis is a test.'
>>> @inject_docstring_content('This is a test.', position='###INSERT HERE###')
... def test_func():
...     '''This is the docstring.
...     ###INSERT HERE###
...     More blah.
...     '''
...     pass
>>> test_func.__doc__
'This is the docstring.\n    ###INSERT HERE###\n    More blah.\n    '
```

### i2.doc_mint.literal_eval_converter(s, max_length=1000)

Evaluate `s` as a Python literal, or return None when it is not one (or is
longer than `max_length`, or contains a newline or `;`).

```pycon
>>> literal_eval_converter("[1, 2]"), literal_eval_converter("foo")
([1, 2], None)
```

### i2.doc_mint.mk_example_wants_callback(source_want_func)

Make a `doctest.Example` callback from a `(source, want) -> str` function.

The callback returns the example’s source untouched when the example expects no
output.

### i2.doc_mint.most_common_indent(string, ignore_first_line=True)

Find the most common indentation in a string.

* **Parameters:**
  * **string** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The string to analyze.
  * **ignore_first_line** – Whether to ignore the first line when determining the
    indentation. Default is True since the first line often has no indentation
    because of the way python strings appear in code.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The most common indentation string.

**Examples**

```pycon
>>> most_common_indent('    This is a test.\n    Another line.')
'    '
```

### i2.doc_mint.non_doctest_lines(doc)

Generator of lines of the doc string that are not in a doctest scope.

```pycon
>>> def _test_func():
...     '''Line 1
...     Another
...     >>> doctest_1
...     >>> doctest_2
...     line_after_a_doc_test
...     another_line_that_is_in_the_doc_test scope
...
...     But now we're out of a doctest's scope
...
...     >>> Oh no, another doctest!
...     '''
>>> from inspect import getdoc
>>>
>>> list(non_doctest_lines(getdoc(_test_func)))
['Line 1', 'Another', "But now we're out of a doctest's scope", '']
```

* **Parameters:**
  **doc**
* **Returns:**

### i2.doc_mint.old_doctest_string(obj, output_prefix='# OUTPUT: ', include_attr_without_doctests=False, recurse=True)

Extract the doctests found in given object.

* **Parameters:**
  * **obj** – Object (module, class, function, etc.) you want to extract doctests from.
  * **output_prefix**
  * **recurse** – Whether the process should find doctests in the attributes of the object, recursively.
* **Returns:**
  A string containing the doctests, with output lines prefixed by ‘# Output:’

### i2.doc_mint.output_prefix(example, \*args, \*\*kwargs)

Render a `doctest.Example` as its source followed by a `# OUTPUT:` line.

```pycon
>>> import doctest
>>> output_prefix(doctest.Example(source='1 + 1\n', want='2\n'))
'1 + 1\n# OUTPUT: 2\n'
```

### i2.doc_mint.params_to_docstring(params, , doc_style='numpy', take_name_of_types=False, quote_string_defaults=True)

Generate a docstring from a list of parameter specifications.

* **Parameters:**
  * **params** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – 

    A list of dictionaries where each dictionary specifies a parameter.
    Each dictionary should contain:
    > - name: The name of the parameter (str).
    > - default: The default value of the parameter (any, optional).
    > - annotation: The type annotation for the parameter (str, optional).
    > - description: A description of the parameter (str).
  * **doc_style** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The style of the docstring to generate. One of ‘numpy’, ‘google’, or ‘rest’.
  * **take_name_of_types** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether to use the name of the type as the annotation (bool).
  * **quote_string_defaults** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether to quote string defaults (bool).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  A formatted docstring (str).

**Examples**

```pycon
>>> params = [
...     {"name": "x", "default": 1, "annotation": "int", "description": "An integer value."},
...     {"name": "y", "default": None, "annotation": "str", "description": "An optional string."},
... ]
>>> print(params_to_docstring(params))
:param x: An integer value.
:type x: int, default=1
:param y: An optional string.
:type y: str, default=None
:param <BLANKLINE>:
:param >>> print(params_to_docstring(params:
:type >>> print(params_to_docstring(params: +NORMALIZE_WHITESPACE
:param doc_style='google'))  # doctest:
:type doc_style='google'))
:param Args: x (int, optional): An integer value. Defaults to 1.
             y (str, optional): An optional string. Defaults to None.
:param <BLANKLINE>:
:param >>> print(params_to_docstring(params:
:type >>> print(params_to_docstring(params: +NORMALIZE_WHITESPACE
:param doc_style='rest'))  # doctest:
:type doc_style='rest'))
:param :
:type : param x: An integer value. (Default: 1)
:param :
:type : type x: int
:param :
:type : param y: An optional string. (Default: None)
:param :
:type : type y: str
:param <BLANKLINE>:
```

### i2.doc_mint.register_converter(converter)

Register a new converter. A converter can be:

> - A dict: { “None”: None, “int”: int, … }
> - A callable: lambda s: attempt to parse s and return object or None

### i2.doc_mint.split_line_comments(s)

Split a single line into its code and its `#` comment (empty if none).

```pycon
>>> split_line_comments("f(1)  # a comment")
('f(1)  ', ' a comment')
```

### i2.doc_mint.split_text_and_doctests(doc_string)

Generates alternating blocks of “text” (string) and “doctest blocks”
(`DoctestBlock` instances, which are essentially a list of `ExampleX` instances).

```pycon
>>> example = '''
...     This is to test the doctest splitter.
...     Until now, we're in a text block.
...     The following is a doctest block:
...
...     >>> 2 + 3
...     5
...     >>> t = 5
...     >>> tt = 10
...
...     This is another text block, followed with another doctest block:
...
...     >>> def foo():
...     ...     return 42
...     >>> foo()
...     42
...
... '''
>>>
>>> blocks = list(split_text_and_doctests(example))
```

There are 5 blocks:

```pycon
>>> len(blocks)
5
```

The first block is a string, corresponding to explanatory text of the doc string:

```pycon
>>> isinstance(blocks[0], str)
True
>>> print(blocks[0])

This is to test the doctest splitter.
Until now, we're in a text block.
The following is a doctest block:

```

The next block is a `DoctestBlock` instance.

```pycon
>>> block = blocks[1]
>>> isinstance(block, DoctestBlock)
True
```

This block has 3 elements (`ExampleX` instances)

```pycon
>>> len(block)
3
```

If you ask for the string representation of this block, you’ll get a doctest string:

```pycon
>>> str(block)
'    >>> 2 + 3\n    5\n    >>> t = 5\n    >>> tt = 10\n'
```

### i2.doc_mint.string_param_to_obj(string_to_object_mapping, string=None)

Convert a string representation of a parameter to an object.

Use Case: When parsing a docstring, you get values as strings, but these values
may need to be converted to their actual object types
(in the case of default and annotatios for example).
This is a helper function to do that conversion.

* **Parameters:**
  * **string** – The string representation of the parameter.
  * **string_to_object_mapping** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – A mapping from string representations to objects.
* **Returns:**
  The object corresponding to the string representation.

**Examples**

```pycon
>>> string_to_object_mapping = {
...     'None': None,
...     'list': list,
...     'int': int,
... }
>>> string_to_obj = string_param_to_obj(string_to_object_mapping)
>>> string_to_obj('None')
>>> string_to_obj('list')
<class 'list'>
>>> string_to_obj('int')
<class 'int'>
>>> string_to_obj('not something listed')
'not something listed'
```

### i2.doc_mint.strip_comments(code)

Remove whole-line `#` comments from `code` (inline comments are kept).

```pycon
>>> strip_comments("# header\nx = 1  # set x\n")
'x = 1  # set x\n'
```


# _autosummary/i2.errors.html.md

# i2.errors

Error objects

### Functions

| [`log_and_return`](_autosummary/i2.errors.html.md#i2.errors.log_and_return)(msg[, logger])   | Pass `msg` to `logger` (`print` by default) and return it.   |
|----------------------------------------------------------------------------------|--------------------------------------------------------------|

### Classes

| [`HandleExceptions`](_autosummary/i2.errors.html.md#i2.errors.HandleExceptions)([on_error])   | A context manager that catches and (specifically) handles specific exceptions.   |
|---------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
| [`ModuleNotFoundIgnore`](_autosummary/i2.errors.html.md#i2.errors.ModuleNotFoundIgnore)()         | Context manager meant to ignore import errors.                                   |

### Exceptions

| [`AuthorizationError`](_autosummary/i2.errors.html.md#i2.errors.AuthorizationError)                             | Base class for errors about what the caller is allowed to do.                |
|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| [`DataError`](_autosummary/i2.errors.html.md#i2.errors.DataError)                                      | Base class for errors about the data itself.                                 |
| [`DuplicateRecordError`](_autosummary/i2.errors.html.md#i2.errors.DuplicateRecordError)                           | A `DataError` for a record that already exists.                              |
| [`ForbiddenError`](_autosummary/i2.errors.html.md#i2.errors.ForbiddenError)                                 | An `AuthorizationError` for an operation that is not allowed.                |
| [`InputError`](_autosummary/i2.errors.html.md#i2.errors.InputError)                                     | Raised for invalid input.                                                    |
| [`InterruptWithBlock`](_autosummary/i2.errors.html.md#i2.errors.InterruptWithBlock)                             | Raise inside a `with` block to leave it early; pair with `HandleExceptions`. |
| [`NotFoundError`](_autosummary/i2.errors.html.md#i2.errors.NotFoundError)                                  | A `DataError` for a record that does not exist.                              |
| [`OverwritesNotAllowed`](_autosummary/i2.errors.html.md#i2.errors.OverwritesNotAllowed)(\*args[, forbidden_keys]) | To raise when writes are only allowed if the item doesn't already exist      |

### *exception* i2.errors.AuthorizationError

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

Base class for errors about what the caller is allowed to do.

### *exception* i2.errors.DataError

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

Base class for errors about the data itself.

### *exception* i2.errors.DuplicateRecordError

Bases: [`DataError`](_autosummary/i2.errors.html.md#i2.errors.DataError)

A `DataError` for a record that already exists.

### *exception* i2.errors.ForbiddenError

Bases: [`AuthorizationError`](_autosummary/i2.errors.html.md#i2.errors.AuthorizationError)

An `AuthorizationError` for an operation that is not allowed.

### *class* i2.errors.HandleExceptions(on_error=<factory>)

Bases: [`AbstractContextManager`](https://docs.python.org/3/library/contextlib.html#contextlib.AbstractContextManager)

A context manager that catches and (specifically) handles specific exceptions.

It takes one argument: A dict (or mapping) of exception type keys and callback
values. If within a with block, the particular (listed) exception happens,
the callback is called and it’s returned value is assigned to the
`HandleExceptions` instance’s `.exit_value` attribute.
That attribute will only exist if the with block existed with an exception
caught by `HandleExceptions`.

A callback is an argument-less function. If you need to specify arguments, you can
envoke the command pattern, using `functools.partial` to make a argument-less
function. See in the example below how we ask `HandleExceptions` to print a
specific string if a `ZeroDivisionError` happens:

```pycon
>>> from functools import partial
>>> def print_and_return(msg):
...     print(msg)
...     return msg
>>> with HandleExceptions({
...     ZeroDivisionError: partial(print_and_return, "You interrupted me"),
...     KeyboardInterrupt: lambda: 'imagine this is code to notify someone'
... }) as he:
...     print('This works')
...     0 / 0
...
This works
You interrupted me
```

You can check if the context exited with a handled exception, and if so, what
the callback returned value was.

```pycon
>>> he.exited_with_handled_exception()
True
>>> he.exit_value
'You interrupted me'
```

Also available, whether the exception was a handled one or not: The exception
instance itself:

```pycon
>>> he.exited_with_exception
ZeroDivisionError('division by zero')
```

If all you want to do though is print a string (and have the same string
available in the `exit_value` attribute, we got you covered!
Just specify a string and we’ll make that printer callaback for you!

```pycon
>>> from functools import partial
>>>
>>> with HandleExceptions({ZeroDivisionError: "You interrupted me again!"}):
...     print('This also works')
...     0 / 0
This also works
You interrupted me again!
```

Note that specifying `partial(print, "some message")` will work as a
“printing callback”, but the string won’t be available in `.exit_value` since
`print` returns None.

A few recipes now…

You can also use your own custom exception types to do things like interrupt
a with block early given some condition(s).

```pycon
>>> with HandleExceptions(
...     {InterruptWithBlock: "The with block was interrupted early."}
... ):
...     print('before condition')
...     x = 5 % 2
...     if x:
...         raise InterruptWithBlock()
...     print('after condition')
...
...
before condition
The with block was interrupted early.
```

#### TIP
If you need to do stuff with an exception, but reraise it, you can
still do that in your callback. Just say `raise` at the end of the callback!

```pycon
>>> def print_and_raise(msg):
...     print(msg)
...     raise
>>>
>>> with HandleExceptions({
...     ZeroDivisionError: partial(print_and_raise, "That again!"),
... }):
...     print('This also works')
...     0 / 0
This also works
That again!
Traceback (most recent call last):
    ...
ZeroDivisionError: division by zero
```

#### exited_with_handled_exception()

Whether the last `with` block ended on an exception listed in `on_error`.

#### initialize()

Forget the outcome of a previous `with` block (done on every `__enter__`).

### *exception* i2.errors.InputError

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

Raised for invalid input.

### *exception* i2.errors.InterruptWithBlock

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

Raise inside a `with` block to leave it early; pair with `HandleExceptions`.

```pycon
>>> with HandleExceptions({InterruptWithBlock: "stopped early"}) as h:
...     raise InterruptWithBlock()
...     print("never printed")
stopped early
>>> h.exit_value
'stopped early'
```

### *class* i2.errors.ModuleNotFoundIgnore

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

Context manager meant to ignore import errors.
The use case in mind is when we want to condition some code on the existence of some package.

### *exception* i2.errors.NotFoundError

Bases: [`DataError`](_autosummary/i2.errors.html.md#i2.errors.DataError)

A `DataError` for a record that does not exist.

### *exception* i2.errors.OverwritesNotAllowed(\*args, forbidden_keys=None, \*\*kwargs)

Bases: [`AuthorizationError`](_autosummary/i2.errors.html.md#i2.errors.AuthorizationError)

To raise when writes are only allowed if the item doesn’t already exist

### i2.errors.log_and_return(msg, logger=<built-in function print>)

Pass `msg` to `logger` (`print` by default) and return it.


# _autosummary/i2.footprints.html.md

# i2.footprints

Analyzing what attributes of an input object a function actually uses

### Functions

| [`accessed_attributes`](_autosummary/i2.footprints.html.md#i2.footprints.accessed_attributes)(func[, object_name])        | Extracts the attributes accessed by a function or method.                                |
|--------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|
| [`attr_list`](_autosummary/i2.footprints.html.md#i2.footprints.attr_list)(root, func_name)                      | Extracts attributes from ast tree processing only func_name function or method           |
| [`attribute_dependencies`](_autosummary/i2.footprints.html.md#i2.footprints.attribute_dependencies)(cls[, filt, ...])        | Extracts (method_name, accessed_attributes) pairs for a class or instance thereof.       |
| [`attrs_used_by_method`](_autosummary/i2.footprints.html.md#i2.footprints.attrs_used_by_method)(method, \*[, src_code])    | Extracts a list of cls attributes which are used by a method or method_name function     |
| [`attrs_used_by_method_computation`](_autosummary/i2.footprints.html.md#i2.footprints.attrs_used_by_method_computation)(cls_method)    | Tracks the access to attributes within an execution.                                     |
| [`cls_and_method_name_of_method`](_autosummary/i2.footprints.html.md#i2.footprints.cls_and_method_name_of_method)(method)           | The `(class, name)` pair of a method, bound method or property.                          |
| [`dict_to_graph`](_autosummary/i2.footprints.html.md#i2.footprints.dict_to_graph)(graph[, from_key_to_values, ...]) | Convert a `{node: neighbours}` dictionary to a graphviz (or mermaid) graph string.       |
| [`dunders_diff`](_autosummary/i2.footprints.html.md#i2.footprints.dunders_diff)(x, y)                              | The set of dunder names `x` has and `y` does not (module names are imported).            |
| [`ensure_ast`](_autosummary/i2.footprints.html.md#i2.footprints.ensure_ast)(o[, src_code])                       | Casts input object `o` to a AST node.                                                    |
| [`get_class_that_defined_method`](_autosummary/i2.footprints.html.md#i2.footprints.get_class_that_defined_method)(method)           | Get class for unbound/bound method.                                                      |
| [`get_imports_from_obj`](_autosummary/i2.footprints.html.md#i2.footprints.get_imports_from_obj)(o[, recursive])            | Getting imports for an object (usually, module)                                          |
| [`get_source`](_autosummary/i2.footprints.html.md#i2.footprints.get_source)(obj)                                 | Get source string of a python object                                                     |
| [`init_argument_names`](_autosummary/i2.footprints.html.md#i2.footprints.init_argument_names)(cls, \*[, no_error_action]) | Get the list of argument names                                                           |
| [`list_func_calls`](_autosummary/i2.footprints.html.md#i2.footprints.list_func_calls)(fn)                             | Extracts functions and methods called from fn                                            |
| [`module_if_string`](_autosummary/i2.footprints.html.md#i2.footprints.module_if_string)(x)                             | Import `x` if it is a module name string; otherwise return it as is.                     |
| [`object_dependencies`](_autosummary/i2.footprints.html.md#i2.footprints.object_dependencies)(obj, \*[, get_source])      | Map each method of a class (or of an instance's class) to the attributes it reads.       |
| [`start_tracking`](_autosummary/i2.footprints.html.md#i2.footprints.start_tracking)(tracker_instance)                | Ctx manager to gracefully start/stop tracking.                                           |
| [`trace_class_decorator`](_autosummary/i2.footprints.html.md#i2.footprints.trace_class_decorator)(cls[, names_and_sigs])    | Add tracing methods to `cls`, each appending `(name, *args)` to the instance's `.trace`. |

### Classes

| [`AttributeVisitor`](_autosummary/i2.footprints.html.md#i2.footprints.AttributeVisitor)(object_name)   | Collect, in `.attributes`, the attribute names accessed on `object_name` in an AST.        |
|----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| [`Import`](_autosummary/i2.footprints.html.md#i2.footprints.Import)(module, name, alias)     |                                                                                            |
| [`MethodTrace`](_autosummary/i2.footprints.html.md#i2.footprints.MethodTrace)()                   | Record the operator dunders applied to an instance, as `(name, *args)` tuples in `.trace`. |
| [`Tracker`](_autosummary/i2.footprints.html.md#i2.footprints.Tracker)(\*args, \*\*kwargs)     | Tracks the attribute access right after `start_track` is set to True.                      |

### *class* i2.footprints.AttributeVisitor(object_name)

Bases: [`NodeVisitor`](https://docs.python.org/3/library/ast.html#ast.NodeVisitor)

Collect, in `.attributes`, the attribute names accessed on `object_name` in an AST.

### *class* i2.footprints.Import(module, name, alias)

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

#### alias

Alias for field number 2

#### module

Alias for field number 0

#### name

Alias for field number 1

### *class* i2.footprints.MethodTrace

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

Record the operator dunders applied to an instance, as `(name, *args)` tuples in `.trace`.

See: [https://github.com/i2mint/i2/issues/56](https://github.com/i2mint/i2/issues/56) for more details.

```pycon
>>> t = MethodTrace()
>>> ((t + 3) - 2) * 5 / 10
<MethodTrace with .trace = ('__add__', 3), ... ('__truediv__', 10)>
>>> assert t.trace == [
...     ('__add__', 3), ('__sub__', 2), ('__mul__', 5), ('__truediv__', 10)
... ]
>>>
>>>
>>> w = t[42]
>>> t[42] = 'mol'  # an operation with two arguments
>>> # ... and now an operation with no arguments:
>>> ~t
<MethodTrace with .trace = ... ('__setitem__', 42, 'mol'), ('__invert__',)>
>>>
>>> assert t.trace == [
... ('__add__', 3), ('__sub__', 2), ('__mul__', 5), ('__truediv__', 10),
... ('__getitem__', 42), ('__setitem__', 42, 'mol'), ('__invert__',)
... ]
>>>
```

### *class* i2.footprints.Tracker(\*args, \*\*kwargs)

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

Tracks the attribute access right after `start_track` is set to True.

Add this to \_\_metaclass_\_ for any class that you need to track attributes for given a
target method.

#### on_access(key)

on attribute access, record attribute if and only if its not from
core attribute or `attrs_to_ignore` set to class.

### i2.footprints.accessed_attributes(func, object_name=None)

Extracts the attributes accessed by a function or method.

(This is a simpler, but narrow, version of `attrs_used_by_method`).

```pycon
>>> def func(a, b, c):
...     return a + b.bar + c
...
>>> # Commenting out the testing code, as execution is not performed in the PCI
>>> def foo(self):
...     a = 2
...     self.method(x)
...     y = self.prop
...     return a + func(x, self, y)
...
>>> assert accessed_attributes(foo, 'self') == {'method', 'prop'}
```

### i2.footprints.attr_list(root, func_name)

Extracts attributes from ast tree processing only func_name function or method

* **Parameters:**
  * **root** – root node of ast tree
  * **func_name** – name of the function
* **Returns:**
  a list of attributes names

### i2.footprints.attribute_dependencies(cls, filt=<function \_is_method_like>, \*, name_of_obj=<function name_of_obj>, exclude_names=<function init_argument_names>)

Extracts (method_name, accessed_attributes) pairs for a class or instance thereof.

* **Parameters:**
  * **cls** ([`type`](https://docs.python.org/3/builtins/functions.html#type)) – The class or instance to analyze
  * **filt** – A function that filters the attributes to consider
  * **name_of_obj** – A function that returns the name of an object
  * **exclude_names** (`Union`[[`Container`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Container), [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)]) – A list of names to exclude from the analysis or a function that
    returns such a list given the class
* **Returns:**
  A generator of (method_name, accessed_attributes) pairs

### i2.footprints.attrs_used_by_method(method, , src_code=None)

Extracts a list of cls attributes which are used by a method or method_name function

#### NOTE
The function tries to analyzed the source code deeply, gathering indirect
references to the instance attributes. As a result, it is not very robust.
You may want to check out the simpler (but narrow) function: `accessed_attributes`.

* **Parameters:**
  * **method** – The method (object) to analyze
  * **src_code** – The source code in which the method’s class is defined, when
    `inspect` cannot retrieve it (for example in a notebook).
* **Returns:**
  A list of attribute names (of the class or instance thereof) that are accessed in the code of the said method.

Consider the method `A.target_method` coming from the following code in
`i2.tests.footprints_test`:

```default
def func(obj):
    """Accesses attributes 'a' and 'b' of obj"""
    return obj.a + obj.b

class A:
    e = 2

    def __init__(self, a=1, b=0, c=1, d=10):
        self.a = a
        self.b = b
        self.c = c
        self.d = d

    def target_method(self, x):
        """Accesses ['a', 'b', 'c', 'e']"""
        t = func(self)  # and this function will access some attributes!
        tt = self.other_method(t)
        return x * tt / self.e

    def other_method(self, x=1):
        """Accesses ['c', 'e']"""
        w = self.c * 2  # c is accessed first
        return self.e + self.c * x - w  # and c is accessed again

    @classmethod
    def a_class_method(cls, y):
        """Accesses ['e']"""
        return cls.e + y
```

### i2.footprints.attrs_used_by_method_computation(cls_method, init_kwargs=None, method_kwargs=None, remove_duplicates=True)

Tracks the access to attributes within an execution.

### i2.footprints.cls_and_method_name_of_method(method)

The `(class, name)` pair of a method, bound method or property.

```pycon
>>> from i2.tests.footprints_test import A
>>> cls_and_method_name_of_method(A().target_method) == (A, "target_method")
True
```

### i2.footprints.dict_to_graph(graph, from_key_to_values=True, , kind='graphviz', indent='    ', prefix='', suffix='', display=False)

Convert a `{node: neighbours}` dictionary to a graphviz (or mermaid) graph string.

You provide a graph in the form of a `{from_node: to_nodes, ...}` or
`{to_node: from_nodes, ...}` dictionary, and will get a graphviz string.
You can use this to visualize a graph (e.g. a dependency graph) in a jupyter notebook.

* **Parameters:**
  * **graph** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The graph, in the form of a to convert to graphviz.
  * **from_key_to_values** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the keys of the graph are from nodes or to nodes.
  * **kind** ([`Literal`](https://docs.python.org/3/library/typing.html#typing.Literal)[`'graphviz'`, `'mermaid'`]) – The kind of graphviz string to return. Either “graphviz” or “mermaid”.
  * **indent** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The indent to use for the graphviz string.
  * **graphviz_template** – The template to use for the graphviz string.
  * **display** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool) | [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)) – Whether to display the graphviz string as a graph in a jupyter notebook. Requires graphviz.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The graphviz string.

Example (but bear in mind the order of the nodes in graphviz_str may be different):

```pycon
>>> graph_dict = {
...     "A": ["B", "C"],
...     "B": ["D"],  # note that "D" is not mentioned as a key
...     "C": ["D", "E", "F"],
...     "E": [],
... }
>>> # Keys are from nodes
>>> graphviz_str = dict_to_graph(graph_dict)
>>> print(graphviz_str)
digraph G {
    "A" -> "B";
    "A" -> "C";
    "B" -> "D";
    "C" -> "D";
    "C" -> "E";
    "C" -> "F";
}
```

```pycon
>>> # Keys are to nodes
>>> graphviz_str = dict_to_graph(graph_dict, from_key_to_values=False)
>>> print(graphviz_str)
digraph G {
    "B" -> "A";
    "C" -> "A";
    "D" -> "B";
    "D" -> "C";
    "E" -> "C";
}
```

The default `kind` is graphviz, but you can also use mermaid:

```pycon
>>> graphviz_str = dict_to_graph(graph_dict, kind="mermaid")
>>> print(graphviz_str)
graph TD
    A --> B;
    A --> C;
    B --> D;
    C --> D;
    C --> E;
    C --> F;
```

### i2.footprints.dunders_diff(x, y)

The set of dunder names `x` has and `y` does not (module names are imported).

### i2.footprints.ensure_ast(o, src_code=None)

Casts input object `o` to a AST node.

If the input is an AST node, it is returned as is.
If the input is a filepath, the file is read and parsed as source code.
If the input is a string, it is parsed as source code.
If the input is an object, the source code is extracted and parsed.

Let’s get an AST for the ensure_ast function itself:

* **Return type:**
  [`AST`](https://docs.python.org/3/library/ast.html#ast.AST)

```pycon
>>> assert isinstance(ensure_ast(ensure_ast), ast.AST)
```

Note that sometimes the source code of an object cannot be accessed via normal
means (for example, in REPLs like jupyter notebooks) so we need to pass it in.

```pycon
>>> src_code = '''
... class MyClass:
...     x = 1
...     def my_method(self):
...         return self.x + 1
... a = 10
... '''
>>> assert isinstance(ensure_ast('MyClass', src_code), ast.AST)
```

### i2.footprints.get_class_that_defined_method(method)

Get class for unbound/bound method.

### i2.footprints.get_imports_from_obj(o, recursive=False)

Getting imports for an object (usually, module)

### i2.footprints.get_source(obj)

Get source string of a python object

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

### i2.footprints.init_argument_names(cls, , no_error_action=None)

Get the list of argument names

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

```pycon
>>> from dataclasses import dataclass
>>> @dataclass
... class DataClass:
...     x: str
...     y: float = 2
...     z = 3
...
>>> init_argument_names(DataClass)
['x', 'y']
```

#### NOTE
Some builtin types don’t have signatures, so we get:

```text
ValueError: no signature found for builtin type ...
```

By default, we handle this by returning an empty list, but a callable
no_error_action will call that function and return its result.
Anything else will result in raising the error.

### i2.footprints.list_func_calls(fn)

Extracts functions and methods called from fn

* **Parameters:**
  **fn** – reference to function or method
* **Returns:**
  a list of functions or methods names

### i2.footprints.module_if_string(x)

Import `x` if it is a module name string; otherwise return it as is.

### i2.footprints.object_dependencies(obj, \*, get_source=<function get_source>)

Map each method of a class (or of an instance’s class) to the attributes it reads.

Attributes accessed through the method’s first argument (usually `self`) count;
attributes that are only assigned to do not. Members for which `get_source` raises
`TypeError` (builtins, descriptors) are skipped.

```pycon
>>> class C:
...     def __init__(self):
...         self.a = 1
...     def m(self):
...         return self.a + self.helper()
...     def helper(self):
...         return 2
>>> object_dependencies(C)["m"] == {"a", "helper"}
True
```

### i2.footprints.start_tracking(tracker_instance)

Ctx manager to gracefully start/stop tracking.

### i2.footprints.trace_class_decorator(cls, names_and_sigs=(('_\_mul_\_', <Sig (self, b, /)>), ('_\_ilshift_\_', <Sig (self, b, /)>), ('_\_ior_\_', <Sig (self, value, /)>), ('_\_neg_\_', <Sig (self, /)>), ('_\_xor_\_', <Sig (self, b, /)>), ('_\_contains_\_', <Sig (self, key: KT, /) -> bool>), ('_\_and_\_', <Sig (self, b, /)>), ('_\_ifloordiv_\_', <Sig (self, b, /)>), ('_\_lt_\_', <Sig (self, b, /)>), ('_\_ne_\_', <Sig (self, b, /)>), ('_\_setitem_\_', <Sig (self, key: KT, value: VT, /) -> Any>), ('_\_imod_\_', <Sig (self, b, /)>), ('_\_index_\_', <Sig (self, /)>), ('_\_le_\_', <Sig (self, b, /)>), ('_\_floordiv_\_', <Sig (self, b, /)>), ('_\_imatmul_\_', <Sig (self, b, /)>), ('_\_iadd_\_', <Sig (self, b, /)>), ('_\_eq_\_', <Sig (self, b, /)>), ('_\_ipow_\_', <Sig (self, b, /)>), ('_\_rshift_\_', <Sig (self, b, /)>), ('_\_pos_\_', <Sig (self, /)>), ('_\_abs_\_', <Sig (self, /)>), ('_\_delitem_\_', <Sig (self, key: KT, /) -> Any>), ('_\_lshift_\_', <Sig (self, b, /)>), ('_\_itruediv_\_', <Sig (self, b, /)>), ('_\_gt_\_', <Sig (self, b, /)>), ('_\_call_\_', <Sig (self, /, \*args, \*\*kwargs)>), ('_\_iconcat_\_', <Sig (self, b, /)>), ('_\_ge_\_', <Sig (self, b, /)>), ('_\_isub_\_', <Sig (self, b, /)>), ('_\_inv_\_', <Sig (self, /)>), ('_\_ixor_\_', <Sig (self, b, /)>), ('_\_matmul_\_', <Sig (self, b, /)>), ('_\_pow_\_', <Sig (self, b, /)>), ('_\_truediv_\_', <Sig (self, b, /)>), ('_\_iand_\_', <Sig (self, b, /)>), ('_\_getitem_\_', <Sig (self, key: KT, /) -> ~VT>), ('_\_add_\_', <Sig (self, b, /)>), ('_\_sub_\_', <Sig (self, b, /)>), ('_\_imul_\_', <Sig (self, b, /)>), ('_\_concat_\_', <Sig (self, b, /)>), ('_\_irshift_\_', <Sig (self, b, /)>), ('_\_invert_\_', <Sig (self, /)>), ('_\_or_\_', <Sig (self, value, /)>), ('_\_not_\_', <Sig (self, /)>), ('_\_mod_\_', <Sig (self, b, /)>), ('_\_len_\_', <Sig (self, /) -> int>), ('_\_ror_\_', <Sig (self, value, /)>), ('_\_reversed_\_', <Sig (self, /)>), ('_\_iter_\_', <Sig (self, /) -> collections.abc.Iterator[~KT]>), ('_\_rmul_\_, ', <Sig (self, other)>), ('_\_rlshift_\_, ', <Sig (self, other)>), ('_\_rdivmod_\_, ', <Sig (self, other)>), ('_\_rfloordiv_\_, ', <Sig (self, other)>), ('_\_rrshift_\_, ', <Sig (self, other)>), ('_\_rmod_\_, ', <Sig (self, other)>), ('_\_rand_\_, ', <Sig (self, other)>), ('_\_rdiv_\_, ', <Sig (self, other)>), ('_\_radd_\_, ', <Sig (self, other)>), ('_\_rpow_\_, ', <Sig (self, other)>), ('_\_rsub_\_, ', <Sig (self, other)>), ('_\_rxor_\_, ', <Sig (self, other)>), ('_\_rtruediv_\_, ', <Sig (self, other)>)), method_factory=<function \_dflt_method_factory>)

Add tracing methods to `cls`, each appending `(name, *args)` to the instance’s `.trace`.

By default the methods are the operator, dict and reflected-operator dunders, made
by `method_factory(name, sig)`; each returns the instance so calls can be chained.


# _autosummary/i2.html.md

# i2

Meta-programming tools to build declarative frameworks

### Modules

| [`base`](_autosummary/i2.base.html.md#module-i2.base)                     | Tools to provide meta-interfaces ("mints") of python objects.                                                                                                                                                                                                                                                               |
|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`castgraph`](_autosummary/i2.castgraph.html.md#module-i2.castgraph)           | A lightweight transformation service for Python that solves the "stable role, unstable representation" problem: a resource has a consistent semantic role (e.g., configuration, text, structured record) but appears in many forms (filepath, string, dict, custom class), while consumers expect specific representations. |
| [`chain_map`](_autosummary/i2.chain_map.html.md#module-i2.chain_map)           | Merge mappings                                                                                                                                                                                                                                                                                                              |
| [`deco`](_autosummary/i2.deco.html.md#module-i2.deco)                     | Decorator tools                                                                                                                                                                                                                                                                                                             |
| [`doc_mint`](_autosummary/i2.doc_mint.html.md#module-i2.doc_mint)             | Meta-interfaces                                                                                                                                                                                                                                                                                                             |
| [`errors`](_autosummary/i2.errors.html.md#module-i2.errors)                 | Error objects                                                                                                                                                                                                                                                                                                               |
| [`footprints`](_autosummary/i2.footprints.html.md#module-i2.footprints)         | Analyzing what attributes of an input object a function actually uses                                                                                                                                                                                                                                                       |
| [`io_trans`](_autosummary/i2.io_trans.html.md#module-i2.io_trans)             | Tools to make input and output transforming decorators.                                                                                                                                                                                                                                                                     |
| [`itypes`](_autosummary/i2.itypes.html.md#module-i2.itypes)                 | Types                                                                                                                                                                                                                                                                                                                       |
| [`key_path`](_autosummary/i2.key_path.html.md#module-i2.key_path)             | Flattening maps and manipulating key paths                                                                                                                                                                                                                                                                                  |
| [`multi_object`](_autosummary/i2.multi_object.html.md#module-i2.multi_object)     | A few fundamental tools to operate on a fixed collection of objects (e.g. functions).                                                                                                                                                                                                                                       |
| [`routing_forest`](_autosummary/i2.routing_forest.html.md#module-i2.routing_forest) | Tools to specify functions through trees and forests.                                                                                                                                                                                                                                                                       |
| [`signatures`](_autosummary/i2.signatures.html.md#module-i2.signatures)         | Signature calculus: Tools to make it easier to work with function's signatures.                                                                                                                                                                                                                                             |
| [`util`](_autosummary/i2.util.html.md#module-i2.util)                     | Misc util objects                                                                                                                                                                                                                                                                                                           |
| [`wrapper`](_autosummary/i2.wrapper.html.md#module-i2.wrapper)               | A wrapper object and tools to work with it                                                                                                                                                                                                                                                                                  |


# _autosummary/i2.io_trans.html.md

# i2.io_trans

Tools to make input and output transforming decorators.

Input value transformers can be conditioned on argument value and name, as well as the
wrapped function itself.

Output value tranformers can be conditioned on argument value and the wrapped function.

### Functions

| [`cast_to_jdict`](_autosummary/i2.io_trans.html.md#i2.io_trans.cast_to_jdict)(value)                 | Tries to cast to a json-friendly dictionary.                |
|---------------------------------------------------------------------------------------|-------------------------------------------------------------|
| [`cast_to_list`](_autosummary/i2.io_trans.html.md#i2.io_trans.cast_to_list)(value)                  | Tries to case to a list (with json friendly elements)       |
| [`identity_func`](_autosummary/i2.io_trans.html.md#i2.io_trans.identity_func)(x)                     | Return the input unchanged.                                 |
| [`pickle_out_trans`](_autosummary/i2.io_trans.html.md#i2.io_trans.pickle_out_trans)(self, argval, func) | Output transformer that pickles the value (`pickle.dumps`). |

### Classes

| [`AnnotAndDfltIoTrans`](_autosummary/i2.io_trans.html.md#i2.io_trans.AnnotAndDfltIoTrans)()                            | Transforms argument values using annotations and default type                                                  |
|---------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| [`ArgnameIoTrans`](_autosummary/i2.io_trans.html.md#i2.io_trans.ArgnameIoTrans)(argname_2_trans_func)             | Transforms argument values according to their names                                                            |
| [`IoTrans`](_autosummary/i2.io_trans.html.md#i2.io_trans.IoTrans)()                                        |                                                                                                                |
| [`JSONAnnotAndDfltIoTrans`](_autosummary/i2.io_trans.html.md#i2.io_trans.JSONAnnotAndDfltIoTrans)()                        | Transforms argument values using annotations and default type, including lists, iterables, dicts, and booleans |
| [`TypedBasedOutIoTrans`](_autosummary/i2.io_trans.html.md#i2.io_trans.TypedBasedOutIoTrans)([trans_func_for_type, ...]) | Transform output according to it's type.                                                                       |

### *class* i2.io_trans.AnnotAndDfltIoTrans

Bases: [`IoTrans`](_autosummary/i2.io_trans.html.md#i2.io_trans.IoTrans)

Transforms argument values using annotations and default type

```pycon
>>> def foo(a: int, b=1.0):
...     return a + b
>>>
>>> input_trans = AnnotAndDfltIoTrans()
>>> foo3 = input_trans(foo)
>>> assert foo3(3) == 4.0
>>> assert foo3(-2, 2) == 0.0
>>> assert foo3("3") == 4.0
>>> assert foo3("-2", "2") == 0.0
>>> assert signature(foo) == signature(foo3)
```

### *class* i2.io_trans.ArgnameIoTrans(argname_2_trans_func)

Bases: [`IoTrans`](_autosummary/i2.io_trans.html.md#i2.io_trans.IoTrans)

Transforms argument values according to their names

```pycon
>>> def foo(a, b=1.0):
...     return a + b
>>>
>>> input_trans = ArgnameIoTrans({'a': int, 'b': float})
>>> foo2 = input_trans(foo)
>>> assert foo2(3) == 4.0
>>> assert foo2(-2, 2) == 0.0
>>> assert foo2("3") == 4.0
>>> assert foo2("-2", "2") == 0.0
>>> assert signature(foo) == signature(foo2)
```

### *class* i2.io_trans.IoTrans

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

### *class* i2.io_trans.JSONAnnotAndDfltIoTrans

Bases: [`AnnotAndDfltIoTrans`](_autosummary/i2.io_trans.html.md#i2.io_trans.AnnotAndDfltIoTrans)

Transforms argument values using annotations and default type,
including lists, iterables, dicts, and booleans

```pycon
>>> def foo(a: dict, b=['dflt'], c=False):
...     return dict({}, a=a, b=b, c=c)
>>>
>>> input_trans = JSONAnnotAndDfltIoTrans()
>>> foo4 = input_trans(foo)
>>> assert foo4('{}') == {'a': {}, 'b': ['dflt'], 'c': False}
>>> assert foo4({'d': 'e'}, '["some_value"]', 'true') == {'a': {'d': 'e'}, 'b': ['some_value'], 'c': True}
>>> complex_types_result = foo4('{"None": null, "True": true, "False": false}', '[null, true, false]', 'false')
>>> assert complex_types_result == {'a': {'None': None, 'True': True, 'False': False}, 'b': [None, True, False], 'c': False}
>>> assert signature(foo) == signature(foo4)
```

### *class* i2.io_trans.TypedBasedOutIoTrans(trans_func_for_type=(), dflt_trans_func=None)

Bases: [`IoTrans`](_autosummary/i2.io_trans.html.md#i2.io_trans.IoTrans)

Transform output according to it’s type.

### i2.io_trans.cast_to_jdict(value)

Tries to cast to a json-friendly dictionary.

```pycon
>>> cast_to_jdict('3')
[3]
>>> cast_to_jdict("[3]")
[3]
>>> cast_to_jdict("[4,2]")
[4, 2]
>>> cast_to_jdict('[4, "string", ["another", "list"], {"nested": 10.2}]')
[4, 'string', ['another', 'list'], {'nested': 10.2}]
>>> cast_to_jdict('{"here": "is", "a": {"nested": "json"}, "with": [null, true, false, 1, 2.3]}')
{'here': 'is', 'a': {'nested': 'json'}, 'with': [None, True, False, 1, 2.3]}
```

And csvs too:

```pycon
>>> cast_to_jdict('1,2,3.4, "string" ,  null, true, false, ["a", "list"]')
[1, 2, 3.4, 'string', None, True, False, ['a', 'list']]
```

### i2.io_trans.cast_to_list(value)

Tries to case to a list (with json friendly elements)

```pycon
>>> cast_to_list('3')
[3]
>>> cast_to_list("[3]")
[3]
>>> cast_to_list("[4,2]")
[4, 2]
>>> cast_to_list('[4, "string", ["another", "list"], {"nested": 10.2}]')
[4, 'string', ['another', 'list'], {'nested': 10.2}]
```

And csvs too:

```pycon
>>> cast_to_list('1,2,3.4, "string" ,  null, true, false, ["a", "list"]')
[1, 2, 3.4, 'string', None, True, False, ['a', 'list']]
```

### i2.io_trans.identity_func(x)

Return the input unchanged.

### i2.io_trans.pickle_out_trans(self, argval, func)

Output transformer that pickles the value (`pickle.dumps`).


# _autosummary/i2.itypes.html.md

# i2.itypes

Types

### Functions

| [`dot_string_of_callable_typ`](_autosummary/i2.itypes.html.md#i2.itypes.dot_string_of_callable_typ)(typ)                   | A `inputs -> Callable -> output` string, with typing-generic names, for a parametrized Callable.    |
|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
| [`dot_strings_of_callable_types`](_autosummary/i2.itypes.html.md#i2.itypes.dot_strings_of_callable_types)(\*typs[, ...])      | Yield, for each parametrized Callable, its `dot_string_of_callable_typ` line and a node-shape line. |
| [`input_and_output_types`](_autosummary/i2.itypes.html.md#i2.itypes.input_and_output_types)(typ)                       | The `(input_types, output_type)` pair of a parametrized `typing.Callable`.                          |
| [`is_a_new_type`](_autosummary/i2.itypes.html.md#i2.itypes.is_a_new_type)(typ)                                | Whether `typ` is a `typing.NewType` (checked through its `__qualname__` and `__supertype__`).       |
| [`is_callable_kind`](_autosummary/i2.itypes.html.md#i2.itypes.is_callable_kind)(typ)                             |                                                                                                     |
| [`iterable_to_literal`](_autosummary/i2.itypes.html.md#i2.itypes.iterable_to_literal)(iterable)                     | Convert an iterable to a Literal type.                                                              |
| [`new_type`](_autosummary/i2.itypes.html.md#i2.itypes.new_type)(name, tp[, doc, aka, assign_to_globals]) | Make a new type with (optional) doc and (optional) aka, set of var names it often appears as        |
| [`typ_name`](_autosummary/i2.itypes.html.md#i2.itypes.typ_name)(typ)                                     | The name of a typing generic (its `_name`) or of a NewType (its `__name__`).                        |
| [`validate_literal`](_autosummary/i2.itypes.html.md#i2.itypes.validate_literal)(func)                            | Decorator to validate (Literal-annotated) argument values at call time.                             |

### Classes

| [`HasAttrs`](_autosummary/i2.itypes.html.md#i2.itypes.HasAttrs)()                  | Make a protocol to express the existence of specific attributes.                |
|------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`ObjectClassifier`](_autosummary/i2.itypes.html.md#i2.itypes.ObjectClassifier)(verifiers) | A general-purpose classifier for objects based on a set of verifying functions. |

### *class* i2.itypes.HasAttrs

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

Make a protocol to express the existence of specific attributes.

```pycon
>>> SizedAndAppendable = HasAttrs["__len__", "append"]
>>> assert isinstance([1, 2, 3], SizedAndAppendable)  # lists have both a length and an append
>>> assert not isinstance((1, 2, 3), SizedAndAppendable)  # tuples don't have an append
```

[Python Protocols](https://www.python.org/dev/peps/pep-0544/) are a way to be able to do
“behavior typing” (my bad terminology).
Basically, if you want your static analyzer
(the swingles in your IDE, or linter validation process…)
to check if you’re manipulating the expected types, except the types
(classes, subclasses, ABCs, abstract classes…) are too restrictive (they are!),
you can use Protocols to fill the gap.

Except writing them can sometimes be verbose.

With HasAttrs you can have the basic “does it have these attributes” cases covered.

```pycon
>>> assert isinstance(dict(), HasAttrs["items"])
>>> assert not isinstance(list(), HasAttrs["items"])
>>> assert not isinstance(dict(), HasAttrs["append"])
>>>
>>> class A:
...     prop = 2
...
...     def method(self):
...         pass
>>>
>>> a = A()
>>> assert isinstance(a, HasAttrs["method"])
>>> assert isinstance(a, HasAttrs["method", "prop"])
>>> assert not isinstance(a, HasAttrs["method", "prop", "this_attr_does_not_exist"])
```

### *class* i2.itypes.ObjectClassifier(verifiers)

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

A general-purpose classifier for objects based on a set of verifying functions.

Each “verifier” checks whether an object belongs to a certain kind (category).

Example usage:

```pycon
>>> from typing import Mapping, Iterable
>>>
>>> obj = "test"
>>> isa = lambda typ: lambda obj: isinstance(obj, typ)
>>> verifiers = {
...     'str': isa(str),
...     'mapping': isa(Mapping),
...     'iterable': isa(Iterable)
... }
>>> classifier = ObjectClassifier(verifiers)
```

Check if the object matches any kind

```pycon
>>> classifier.matches(obj)
True
```

Check if the object matches a specific kind

```pycon
>>> classifier.matches(obj, 'str')
True
>>> classifier.matches(obj, 'mapping')
False
```

Get all matches

```pycon
>>> classifier.all_matches(obj)
{'str': True, 'mapping': False, 'iterable': True}
```

Find all matching kinds

```pycon
>>> list(classifier.matching_kinds(obj))
['str', 'iterable']
```

Find the first matching kind (default is to ensure uniqueness, which will fail here)

```pycon
>>> classifier.matching_kind(obj)
Traceback (most recent call last):
  ...
ValueError: Multiple matches found: ['str', 'iterable']
```

Find the first matching kind without uniqueness check

```pycon
>>> classifier.matching_kind(obj, assert_unique=False)
'str'
```

#### all_matches(obj)

Returns a dictionary indicating if the object matches each kind.

* **Parameters:**
  **obj** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – The object to classify.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`KT`), [`bool`](https://docs.python.org/3/builtins/functions.html#bool)]
* **Returns:**
  A dictionary with kind names as keys and True/False as values.

#### matches(obj, kind=None)

Returns True if the object matches the given kind, or matches any kind
if kind is None.

* **Parameters:**
  * **obj** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – The object to classify.
  * **kind** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`KT`)]) – The specific kind (verifier key) to check.
* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)
* **Returns:**
  True if the object matches the given or any kind.

#### matching_kind(obj, , assert_unique=True)

Returns the first kind that matches the object. If assert_unique is True,
it asserts that only one match exists. Optionally, it can return the value instead of the key.

* **Parameters:**
  * **obj** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – The object to classify.
  * **assert_unique** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Ensures only one kind matches, if True.
* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`KT`)]
* **Returns:**
  The key of the first matching kind, or None if no match.

#### matching_kinds(obj)

Returns an iterator of kinds that match the object.

* **Parameters:**
  **obj** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – The object to classify.
* **Return type:**
  [`Iterator`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterator)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`KT`)]
* **Returns:**
  An iterator of matching kinds.

### i2.itypes.dot_string_of_callable_typ(typ)

A `inputs -> Callable -> output` string, with typing-generic names, for a parametrized Callable.

```pycon
>>> from typing import Callable, List, Dict
>>> dot_string_of_callable_typ(Callable[[List, Dict], List])
'List,Dict -> Callable -> List'
```

### i2.itypes.dot_strings_of_callable_types(\*typs, func_shape='box')

Yield, for each parametrized Callable, its `dot_string_of_callable_typ` line and a node-shape line.

### i2.itypes.input_and_output_types(typ)

The `(input_types, output_type)` pair of a parametrized `typing.Callable`.

```pycon
>>> from typing import Callable, Tuple
>>> input_types, output_type = input_and_output_types(Callable[[float, int], str])
>>> assert input_types == [float, int] and output_type == str
>>> input_types, output_type = input_and_output_types(Callable[[], str])
>>> assert input_types == [] and output_type == str
```

But will fail if `typ` isn’t a `Callable`:

```pycon
>>> input_and_output_types(Tuple[float, int, str])
Traceback (most recent call last):
  ...
AssertionError: Is not a typing.Callable kind: typing.Tuple[float, int, str]
```

Will also fail if `typ` is a Callable but not “parametrized”.

```pycon
>>> input_and_output_types(Callable)
Traceback (most recent call last):
  ...
AssertionError: Can only be used on a Callable[[...],...] kind: typing.Callable
```

### i2.itypes.is_a_new_type(typ)

Whether `typ` is a `typing.NewType` (checked through its `__qualname__` and `__supertype__`).

### i2.itypes.is_callable_kind(typ)

```pycon
>>> from typing import Callable, Tuple
>>> is_callable_kind(Callable)
True
>>> is_callable_kind(Callable[[int, float], str])
True
>>> is_callable_kind(Tuple[int, float, str])
False
```

### i2.itypes.iterable_to_literal(iterable)

Convert an iterable to a Literal type.

```pycon
>>> iterable_to_literal([1, 2, 3])
typing.Literal[1, 2, 3]
```

### i2.itypes.new_type(name, tp, doc=None, aka=None, assign_to_globals=False)

Make a new type with (optional) doc and (optional) aka, set of var names it often
appears as

* **Parameters:**
  * **name** – Name to give the variable
  * **tp** – type (see typing module)
  * **doc** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional string to put in \_\_doc_\_ attribute
  * **aka** ([`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Optional set (or any iterable) to put in \_aka attribute,
    meant to list names the variables of this type often appear as.
  * **assign_to_globals** – If True, also bind the new type to `name` in the
    globals of the `i2.itypes` module (not the caller’s).
* **Returns:**
  The new type.

```pycon
>>> from typing import Any, Union, List
>>> MyType = new_type('MyType', int)
>>> # TODO: Skipping the next part because outputs <class 'typing.NewType'> in 3.10
>>> type(MyType)
<class 'function'>
>>> Key = new_type('Key', Any, aka=['key', 'k'])
>>> sorted(Key._aka)
['k', 'key']
>>> Val = new_type(
... 'Val', Union[int, float, List[Union[int, float]]],
... doc="A number or list of numbers.")
>>> Val.__doc__
'A number or list of numbers.'
```

### i2.itypes.typ_name(typ)

The name of a typing generic (its `_name`) or of a NewType (its `__name__`).

### i2.itypes.validate_literal(func)

Decorator to validate (Literal-annotated) argument values at call time.

Wraps a function to add validation of the input arguments annotated with Literal
against the values listed by the literal. If the input argument is not one of the
literal values, a ValueError is raised.

```pycon
>>> @validate_literal
... def f(x: Literal[1, 2, 3]):
...     return x
>>> f(1)
1
>>> f(4)
Traceback (most recent call last):
    ...
ValueError: 4 is an invalid value for x. Values should be one of the following: (1, 2, 3)
```


# _autosummary/i2.key_path.html.md

# i2.key_path

Flattening maps and manipulating key paths

### Functions

| [`flatten_dict`](_autosummary/i2.key_path.html.md#i2.key_path.flatten_dict)(d[, sep, prefix])                    | Computes a "flat" dict from a nested one.                                        |
|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
| [`obj_to_str_path`](_autosummary/i2.key_path.html.md#i2.key_path.obj_to_str_path)(obj, \*[, sep, name_of_obj, ...]) | Get the dotpath reference for an object                                          |
| [`rollout_dict`](_autosummary/i2.key_path.html.md#i2.key_path.rollout_dict)(d[, sep, prefix])                    | Get the nested path of a flat (key path) dict.                                   |
| [`str_path_to_obj`](_autosummary/i2.key_path.html.md#i2.key_path.str_path_to_obj)(str_path, \*[, sep])              | Loads and returns the object referenced by the string DOTPATH_TO_MODULE.OBJ_NAME |
| [`trans_generator_output`](_autosummary/i2.key_path.html.md#i2.key_path.trans_generator_output)(trans)                     | Make a decorator that applies `trans` to every item a generator function yields. |

### Classes

| [`KeyPathMap`](_autosummary/i2.key_path.html.md#i2.key_path.KeyPathMap)([store, key_type, node_type, ...])   | Provides a key-path view to a nested mapping (by default, a dict).                                                                   |
|--------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| [`KeyPathTrans`](_autosummary/i2.key_path.html.md#i2.key_path.KeyPathTrans)([sep, node_type, mk_new_node])     | Doing what StrKeyPath but where the store that is being operated on is not included in the object, but given to the method as input. |
| [`NoDefault`](_autosummary/i2.key_path.html.md#i2.key_path.NoDefault)()                                     | Type of the `NO_DFLT` sentinel (no default value given).                                                                             |
| [`StrKeyPath`](_autosummary/i2.key_path.html.md#i2.key_path.StrKeyPath)([store, key_type, node_type, ...])   | A KeyPathMap, but where the key paths are expressed as string with a separator.                                                      |

### *class* i2.key_path.KeyPathMap(store=<class 'dict'>, key_type=None, node_type=None, auto_node_writes=False)

Bases: [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)

Provides a key-path view to a nested mapping (by default, a dict).
A nested mapping can be see as a tree, where if a value is itself a mapping, it is a non-terminal node,
leaves (or terminal) holding the “actual values”.

When wrapping a mapping in KeyPathMap, you can pretend that you have a flat mapping from (root to leaf) paths
instead of a nested structure, and do your mapping CRUD with that view.

```pycon
>>> d = {'a': {
...         'a': '2a',
...         'b': {'a': 'aba',
...               'b': 3}
...         },
...      'c': 3.14
...     }
>>> kp = KeyPathMap(d)
>>> list(kp.items())
[(('a', 'a'), '2a'), (('a', 'b', 'a'), 'aba'), (('a', 'b', 'b'), 3), (('c',), 3.14)]
>>> list(kp)
[('a', 'a'), ('a', 'b', 'a'), ('a', 'b', 'b'), ('c',)]
>>> len(kp)
4
>>> assert list(kp) == list(kp.keys())
>>> list(kp.values())
['2a', 'aba', 3, 3.14]
>>> kp['a']
{'a': '2a', 'b': {'a': 'aba', 'b': 3}}
>>> kp[('a',)]
{'a': '2a', 'b': {'a': 'aba', 'b': 3}}
>>> kp['a', 'a']
'2a'
>>> kp['a', 'b', 'b']
3
>>> ('a', 'new_key') in kp
False
>>> kp['a', 'new_key'] = 'new val'
>>> ('a', 'new_key') in kp
True
>>> kp['a', 'new_key']
'new val'
>>> len(kp)
5
>>> del kp['a', 'b', 'a']
>>> len(kp)
4
>>> list(kp.items())
[(('a', 'a'), '2a'), (('a', 'b', 'b'), 3), (('a', 'new_key'), 'new val'), (('c',), 3.14)]
>>>
>>> # By default, you can only write on already created nodes. But if auto_node_writes=True, you can do this:
>>> kp = KeyPathMap(auto_node_writes=True)
>>> kp
{}
>>> kp['a', 'b', 'c'] = 'hi world!'
>>> kp
{'a': {'b': {'c': 'hi world!'}}}
```

#### items() → a set-like object providing a view on D's items

### *class* i2.key_path.KeyPathTrans(sep='.', node_type=<class 'dict'>, mk_new_node=None)

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

Doing what StrKeyPath but where the store that is being operated on is not included in the object, but
given to the method as input.

#### extract_key_paths(d, key_paths, field_naming='full', use_default=False, default_val=None)

getting with a key list or “.”-separated string

* **Parameters:**
  * **d** – dict-like
  * **key_path** – list or “.”-separated string of keys
  * **field_naming** – ‘full’ (default) will use key_path strings as is, leaf will only use the last dot item
    (i.e. this.is.a.key.path will result in “path” being used)
* **Returns:**

```pycon
>>> kp = KeyPathTrans()
>>> d = {
...     'a': {
...         'a': 'a.a',
...         'b': 'a.b',
...         'c': {
...             'a': 'a.c.a'
...         }
...     },
...     'b': 'b',
...     'c': 3
... }
>>> kp.extract_key_paths(d, 'a')
{'a': {'a': 'a.a', 'b': 'a.b', 'c': {'a': 'a.c.a'}}}
>>> kp.extract_key_paths(d, 'a.a')
{'a.a': 'a.a'}
>>> kp.extract_key_paths(d, 'a.c')
{'a.c': {'a': 'a.c.a'}}
>>> kp.extract_key_paths(d, ['a.a', 'a.c'])
{'a.a': 'a.a', 'a.c': {'a': 'a.c.a'}}
>>> kp.extract_key_paths(d, ['a.a', 'something.thats.not.there'])  # missing key just won't be included
{'a.a': 'a.a'}
>>> kp.extract_key_paths(d, ['a.a', 'something.thats.not.there'], use_default=True, default_val=42)
{'a.a': 'a.a', 'something.thats.not.there': 42}
```

#### getitem(d, key_path, default_val=None)

getting with a key list or “.”-separated string

* **Parameters:**
  * **d** – dict-like
  * **key_path** – list or “.”-separated string of keys
* **Returns:**

#### items(d, key_path_prefix=None)

iterate through items of store recursively, yielding (key_path, val) pairs for all nested values that are not
store types.
That is, if a value is a store_type, it won’t generate a yield, but rather, will be iterated through
recursively.

* **Parameters:**
  * **d** – input store
  * **key_path_so_far** – string to be prepended to all key paths (for use in recursion, not meant for direct use)
* **Returns:**
  a (key_path, val) iterator

```pycon
>>> kp = KeyPathTrans()
>>> input_dict = {
...     'a': {
...         'a': 'a.a',
...         'b': 'a.b',
...         'c': {
...             'a': 'a.c.a'
...         }
...     },
...     'b': 'b',
...     'c': 3
... }
>>> list(kp.items(input_dict))
[('a.a', 'a.a'), ('a.b', 'a.b'), ('a.c.a', 'a.c.a'), ('b', 'b'), ('c', 3)]
```

#### setitem(d, key_path, val)

setting with a key list or “.”-separated string

* **Parameters:**
  * **d** – dict
  * **key_path** – list or “.”-separated string of keys
  * **val** – value to assign
* **Returns:**

#### setitem_recursive(d, key_path, val)

* **Parameters:**
  * **d**
  * **key_path**
  * **val**
* **Returns:**

```pycon
>>> kp = KeyPathTrans()
>>> input_dict = {
...   "a": {
...     "c": "val of a.c",
...     "b": 1,
...   },
...   "10": 10,
...   "b": {
...     "B": {
...       "AA": 3
...     }
...   }
... }
>>>
>>> kp.setitem_recursive(input_dict, 'new.key.path', 7)
>>> input_dict
{'a': {'c': 'val of a.c', 'b': 1}, '10': 10, 'b': {'B': {'AA': 3}}, 'new': {'key': {'path': 7}}}
>>> kp.setitem_recursive(input_dict, 'new.key.old.path', 8)
>>> input_dict
{'a': {'c': 'val of a.c', 'b': 1}, '10': 10, 'b': {'B': {'AA': 3}}, 'new': {'key': {'path': 7, 'old': {'path': 8}}}}
>>> kp.setitem_recursive(input_dict, 'new.key', 'new val')
>>> input_dict
{'a': {'c': 'val of a.c', 'b': 1}, '10': 10, 'b': {'B': {'AA': 3}}, 'new': {'key': 'new val'}}
```

### *class* i2.key_path.NoDefault

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

Type of the `NO_DFLT` sentinel (no default value given).

### *class* i2.key_path.StrKeyPath(store=<class 'dict'>, key_type=None, node_type=None, auto_node_writes=False, sep='.', prefix='')

Bases: [`KeyPathMap`](_autosummary/i2.key_path.html.md#i2.key_path.KeyPathMap)

A KeyPathMap, but where the key paths are expressed as string with a separator.
If sep = ‘.’, then instead of using (‘a’, ‘b’, ‘c’) as a key, you can use ‘a.b.c’.

```pycon
>>> d = {'a': {
...         'a': '2a',
...         'b': {'a': 'aba',
...               'b': 3}
...         },
...      'c': 3.14
...     }
>>> # Example with sep='/'
>>> kp = StrKeyPath(d, sep='/')
>>> list(kp.items())
[('a/a', '2a'), ('a/b/a', 'aba'), ('a/b/b', 3), ('c', 3.14)]
>>> # You can also add a prefix to the keys
>>> kp = StrKeyPath(d, sep='/', prefix="http://")
>>> list(kp.items())
[('http://a/a', '2a'), ('http://a/b/a', 'aba'), ('http://a/b/b', 3), ('http://c', 3.14)]
>>>
>>> # Default sep is '.', so we'll work with that:
>>> kp = StrKeyPath(d)
>>> kp
{'a': {'a': '2a', 'b': {'a': 'aba', 'b': 3}}, 'c': 3.14}
>>> list(kp.items())
[('a.a', '2a'), ('a.b.a', 'aba'), ('a.b.b', 3), ('c', 3.14)]
>>> list(kp)
['a.a', 'a.b.a', 'a.b.b', 'c']
>>> len(kp)
4
>>> assert list(kp) == list(kp.keys())
>>> list(kp.values())
['2a', 'aba', 3, 3.14]
>>> kp['a']
{'a': '2a', 'b': {'a': 'aba', 'b': 3}}
>>> kp['a.a']
'2a'
>>> kp['a.b.b']
3
>>> ('a.new_key') in kp
False
>>> kp['a.new_key'] = 'new val'
>>> 'a.new_key' in kp
True
>>> kp['a.new_key']
'new val'
>>> len(kp)
5
>>> del kp['a.b.a']
>>> len(kp)
4
>>> list(kp.items())
[('a.a', '2a'), ('a.b.b', 3), ('a.new_key', 'new val'), ('c', 3.14)]
>>>
>>> # By default, you can only write on already created nodes. But if auto_node_writes=True, you can do this:
>>> kp = StrKeyPath(auto_node_writes=True)
>>> kp
{}
>>> kp['a.b.c'] = 'hi world!'
>>> kp
{'a': {'b': {'c': 'hi world!'}}}
>>>
```

#### items() → a set-like object providing a view on D's items

### i2.key_path.flatten_dict(d, sep=None, prefix='')

Computes a “flat” dict from a nested one. A flat dict’s keys are the paths of the input dict.
These paths will be expressed as tuples of the original keys by defaults.
If these keys are strings though, you can use sep and prefix to get string representations of the paths.

* **Parameters:**
  * **d** – a nested dict
  * **sep** – The separator character (or string) in a string representation of the paths.
  * **prefix** – A string to prepend on all the paths
* **Returns:**
  A flat dict

```pycon
>>> d = {'a': {
...         'a': '2a',
...         'c': {'a': 'aca', 'u': 4}
...         },
...      'c': 3
...     }
>>> flatten_dict(d)
{('a', 'a'): '2a', ('a', 'c', 'a'): 'aca', ('a', 'c', 'u'): 4, ('c',): 3}
>>> flatten_dict(d, sep='.')
{'a.a': '2a', 'a.c.a': 'aca', 'a.c.u': 4, 'c': 3}
>>> flatten_dict(d, sep='/', prefix='/ROOT/')
{'/ROOT/a/a': '2a', '/ROOT/a/c/a': 'aca', '/ROOT/a/c/u': 4, '/ROOT/c': 3}
```

### i2.key_path.obj_to_str_path(obj, , sep='.', name_of_obj=operator.attrgetter('_\_qualname_\_'), path_of_module=operator.attrgetter('_\_module_\_'))

Get the dotpath reference for an object

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

`obj_to_str_path` is the inverse of `str_path_to_obj`

```pycon
>>> assert str_path_to_obj(obj_to_str_path(Signature.replace)) == Signature.replace
```

Let’s try with a different separator.

```pycon
>>> path = obj_to_str_path(Signature.replace, sep='/')
>>> path
'inspect/Signature.replace'
```

Remember to specify the same `sep` when you do the inverse!

```pycon
>>> assert str_path_to_obj(path, sep='/') == Signature.replace
```

You can also pass in your own `name_of_obj` and `path_of_module` functions.
For example you want a more permissive version of `name_of_obj` you may consider
`i2.name_of_obj`.
Note, thought, that `str_path_to_obj` might not work as an inverse for
custom `name_of_obj` and `path_of_module` functions.
You may have to write your own inverse function in this case.

### i2.key_path.rollout_dict(d, sep=None, prefix='')

Get the nested path of a flat (key path) dict. This is the inverse of flatten_dict.

* **Parameters:**
  * **d** – a flat dict (i.e. one whose keys are paths of a nested dict)
  * **sep** – If None (default), the paths should be key tuples. If a string, it it assumed to be
    the separator of string representations of the path
  * **prefix** – A string that has be prepended to all each key (path) of the input dict
    (and therefore should be removed)
* **Returns:**
  The corresponding nested path

```pycon
>>> flat_d = {('a', 'a'): '2a', ('a', 'c', 'a'): 'aca', ('a', 'c', 'u'): 4, ('c',): 3}
>>> rollout_dict(flat_d)
{'a': {'a': '2a', 'c': {'a': 'aca', 'u': 4}}, 'c': 3}
>>> flat_d = {'a.a': '2a', 'a.c.a': 'aca', 'a.c.u': 4, 'c': 3}
>>> rollout_dict(flat_d, sep='.')
{'a': {'a': '2a', 'c': {'a': 'aca', 'u': 4}}, 'c': 3}
>>> flat_d = {'/ROOT/a/a': '2a', '/ROOT/a/c/a': 'aca', '/ROOT/a/c/u': 4, '/ROOT/c': 3}
>>> rollout_dict(flat_d, sep='/', prefix='/ROOT/')
{'a': {'a': '2a', 'c': {'a': 'aca', 'u': 4}}, 'c': 3}
```

### i2.key_path.str_path_to_obj(str_path, , sep='.')

Loads and returns the object referenced by the string DOTPATH_TO_MODULE.OBJ_NAME

### i2.key_path.trans_generator_output(trans)

Make a decorator that applies `trans` to every item a generator function yields.


# _autosummary/i2.multi_object.html.md

# i2.multi_object

A few fundamental tools to operate on a fixed collection of objects (e.g. functions).

For functions you have:

- `Pipe`: To compose functions (output of one fed as the input of the next)
- `FuncFanout`: To apply multiple functions to the same inputs
- `FlexFuncFanout`: Like `FuncFanout` but where the application of inputs is flexible.

That is, the functions “draw” their inputs from the a common pool, but don’t choke
if there are extra unrecognized arguments.

- `ParallelFuncs`: To make a dict-to-dict function, applying a specific function for
  each input key (putting the result in that key in the output.

For context managers you have:

- `ContextFanout`: To hold multiple context managers as one (entering and exiting
  together)

![image](https://user-images.githubusercontent.com/1906276/138004878-bfe17115-c25f-4d22-9740-0fef983507c0.png)

### Functions

| [`ensure_iterable_of_callables`](_autosummary/i2.multi_object.html.md#i2.multi_object.ensure_iterable_of_callables)(x)                   | Assert that the input is an iterable of callables, or wrap a single callable in an iterable.   |
|----------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| [`flatten_pipe`](_autosummary/i2.multi_object.html.md#i2.multi_object.flatten_pipe)(pipe)                                | Unravel nested Pipes to get a flat 'sequence of functions' version of input.                   |
| [`iterable_of_callables_validation`](_autosummary/i2.multi_object.html.md#i2.multi_object.iterable_of_callables_validation)(funcs)           | Raise `TypeError` unless `funcs` is an iterable whose elements are all callable.               |
| [`merge_unnamed_and_named`](_autosummary/i2.multi_object.html.md#i2.multi_object.merge_unnamed_and_named)(\*unnamed, \*\*named)     | To merge unnamed and named arguments into a single (named) dict of arguments                   |
| [`name_of_obj`](_autosummary/i2.multi_object.html.md#i2.multi_object.name_of_obj)(o[, default])                         | Tries to find the (or "a") name for an object, even if `__name__` doesn't exist.               |
| [`pipes_are_equal`](_autosummary/i2.multi_object.html.md#i2.multi_object.pipes_are_equal)(p1, p2, \*[, func_equality, ...]) | Determine if two pipelines are equal.                                                          |
| [`truncate_string_with_marker`](_autosummary/i2.multi_object.html.md#i2.multi_object.truncate_string_with_marker)(s, \*[, ...])         | Return a string with a limited length.                                                         |
| [`uniquely_named_objects`](_autosummary/i2.multi_object.html.md#i2.multi_object.uniquely_named_objects)(objects[, ...])            | Generate (name, object) pairs from an iterable of objects                                      |

### Classes

| [`ContextFanout`](_autosummary/i2.multi_object.html.md#i2.multi_object.ContextFanout)(\*unnamed, \*\*named)              | Encapsulates multiple objects into a single context manager that will enter and exit all objects that are context managers themselves.   |
|---------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| [`FlexFuncFanout`](_autosummary/i2.multi_object.html.md#i2.multi_object.FlexFuncFanout)(\*unnamed_funcs, \*\*named_funcs) | Call multiple functions, using a pool of arguments that they will draw from.                                                             |
| [`FuncFanout`](_autosummary/i2.multi_object.html.md#i2.multi_object.FuncFanout)(\*unnamed_funcs, \*\*named_funcs)     | Applies multiple functions to the same argument(s) and returns a dict of results.                                                        |
| [`MultiFunc`](_autosummary/i2.multi_object.html.md#i2.multi_object.MultiFunc)(\*unnamed_funcs, \*\*named_funcs)      | A `MultiObj` that only accepts callables; the base of `Pipe`, `FuncFanout` and friends.                                                  |
| [`MultiObj`](_autosummary/i2.multi_object.html.md#i2.multi_object.MultiObj)(\*unnamed, \*\*named)                   | A base class that holds several named objects                                                                                            |
| [`ParallelFuncs`](_autosummary/i2.multi_object.html.md#i2.multi_object.ParallelFuncs)(\*unnamed_funcs, \*\*named_funcs)  | Make a multi-channel function from a {name: func, ...} specification.                                                                    |
| [`Pipe`](_autosummary/i2.multi_object.html.md#i2.multi_object.Pipe)(\*unnamed_funcs, \*\*named_funcs)           | Simple function composition.                                                                                                             |

### *class* i2.multi_object.ContextFanout(\*unnamed, \*\*named)

Bases: [`MultiObj`](_autosummary/i2.multi_object.html.md#i2.multi_object.MultiObj)

Encapsulates multiple objects into a single context manager that will enter and
exit all objects that are context managers themselves.

Context managers show up in situations where you need to have some setup and tear
down before performing some tasks. It’s what you get when you open a file to read
or write in it, or open a data-base connection, etc.

Sometimes you need to perform a task that involves more than one context managers,
or even some objects that may or may not be context managers.
What `ContextFanout` does for you is allow you to bundle all those (perhaps)
context managers together, and use them as one single context manager.

In python 3.10+ you can bundle contexts together by specifying a tuple of context
managers, as such:

```python
with (open('file.txt'), another_context_manager):
    ...
```

But

- Python will complain if one of the members of the tuple is not a context manager.
- A tuple of context managers is not a context manager itself, it’s just understood
  by the with (in python 3.10+).

As an example, let’s take two objects. One is a context manager, the other not.

```pycon
>>> from contextlib import contextmanager
>>> @contextmanager
... def some_context_manager(x):
...     print('open')
...     yield f'x + 1 = {x + 1}'
...     print('close')
...
>>> def not_a_context_manager(x):
...     return x - 1
...
```

```pycon
>>> c = ContextFanout(not_a_context_manager, some_context_manager(2))
>>> list(c)
['not_a_context_manager', '_GeneratorContextManager']
```

The name (chosen by `MultiObj.auto_namer`) ‘_GeneratorContextManager’ isn’t the best.
Let’s give an explicit name:

```pycon
>>> c = ContextFanout(not_a_context_manager, context=some_context_manager(2))
>>> list(c.objects)
['not_a_context_manager', 'context']
```

See from the prints that “with-ing” c triggers the enter and exit of ‘context’

```pycon
>>> with c:
...     pass
open
close
```

### *class* i2.multi_object.FlexFuncFanout(\*unnamed_funcs, \*\*named_funcs)

Bases: [`MultiFunc`](_autosummary/i2.multi_object.html.md#i2.multi_object.MultiFunc)

Call multiple functions, using a pool of arguments that they will draw from.

```pycon
>>> from i2.tests.objects_for_testing import formula1, sum_of_args, mult, add
>>> mf1 = FlexFuncFanout(formula1=formula1, mult=mult, add=add)
>>> kwargs_for_func = mf1.kwargs_for_func(w=1, x=2, z=3, a=4, b=5)
```

What’s this for? Well, the raison d’etre of `FlexFuncFanout` is to be able to do this:

```pycon
>>> assert add(a=4, b=5) == add(**kwargs_for_func['add'])
```

This wouldn’t work on all functions since some functions have position only arguments (e.g. `formula1`).
Therefore `FlexFuncFanout` holds a “normalized” form of the functions; namely one that handles such things as
postion only and varargs.

Not yet working (to do; right now it raises `TypeError: formula1() got some
positional-only arguments passed as keyword arguments: 'w'`):

```default
# >>> assert formula1(1, x=2, z=3) == mf1.normalized_funcs[formula1](**kwargs_for_func[formula1])
```

#### NOTE
In the following, it looks like `FlexFuncFanout` instances return dicts whose keys are strings.
This is not the case.

The keys are functions: The same functions that were input.
The reason for not using functions is that when printed, they include their hash, which invalidates the doctests.

```default
# >>> def print_dict(d):  # just a util for this doctest
# ...     from pprint import pprint
# ...     pprint({k.__name__: d[k] for k in sorted(d, key=lambda x: x.__name__)})
```

```pycon
>>> mf1 = FlexFuncFanout(formula1, mult=mult, addition=add)
>>> assert mf1.kwargs_for_func(w=1, x=2, z=3, a=4, b=5) == {
... 'formula1': {'w': 1, 'x': 2, 'z': 3},
... 'mult': {'x': 2},
... 'addition': {'a': 4, 'b': 5},
... }
```

Oh, and you can actually see the signature of kwargs_for_func:

```pycon
>>> from inspect import signature
>>> signature(mf1)
<Sig (w, x: float, a, y=1, z: int = 1, b: float = 0.0)>
```

```pycon
>>> mf2 = FlexFuncFanout(formula1, mult, addition=add, mysum=sum_of_args)
>>> assert mf2.kwargs_for_func(
...     w=1, x=2, z=3, a=4, b=5, args=(7,8), kwargs={'a': 42}, extra_stuff='ignore'
... ) == {
... 'formula1': {'w': 1, 'x': 2, 'z': 3},
... 'mult': {'x': 2},
... 'addition': {'a': 4, 'b': 5},
... 'mysum': {'args': (7, 8), 'kwargs': {'a': 42}}}
```

### *class* i2.multi_object.FuncFanout(\*unnamed_funcs, \*\*named_funcs)

Bases: [`MultiFunc`](_autosummary/i2.multi_object.html.md#i2.multi_object.MultiFunc)

Applies multiple functions to the same argument(s) and returns a dict of results.

You know how `map(func, iterable_of_inputs)` applies a same function to an iterable
of inputs.
`FuncFanout` (we could call it `pam`) is a sort of dual; used to apply multiple
functions to a same input.

```pycon
>>> def foo(a):
...     return a + 2
...
>>> def bar(a):
...     return a * 2
...
>>> groot = lambda a: 'I am groot'
>>> m = FuncFanout(foo, bar, groot)
>>>
>>> list(m(3))
[('foo', 5), ('bar', 6), ('_2', 'I am groot')]
>>> dict(m(3))
{'foo': 5, 'bar': 6, '_2': 'I am groot'}
```

Don’t like that `_2` name?
Well, If you specify names to the input functions, they’ll be used instead of the
ones found by the `MultObj.auto_namer`.

```pycon
>>> m = FuncFanout(foo, bar_results=bar, groot=groot)
>>> dict(m(10))
{'foo': 12, 'bar_results': 20, 'groot': 'I am groot'}
```

Or if you want your results as a tuple, you could do:

```pycon
>>> tuple(dict(m(10)).values())
(12, 20, 'I am groot')
```

The above, gather in a `dict` is one way to get your data, but what calling a
`FuncFanout` instance actually gives you is a generator that yields the
`(func_key, func_output)` pairs one at a time

Sometimes you may want/need more control though, and prefer to iterate through the
pairs yourself, and in that case use `call_generator` directly.

```pycon
>>> gen = m(10)
>>> next(gen)
('foo', 12)
>>> next(gen)
('bar_results', 20)
>>> next(gen)
('groot', 'I am groot')
```

So this gives you control on how you want your data.
Here’s a recipe: Say you want to make a function that gives you the data as a dict
automatically. You can do this, using `i2.Pipe`:

```pycon
>>> f = Pipe(m, dict)
>>> f(10)
{'foo': 12, 'bar_results': 20, 'groot': 'I am groot'}
```

Or if you want a tuple:

```pycon
>>> from operator import itemgetter, methodcaller
>>> from functools import partial
>>> f = Pipe(m, partial(map, itemgetter(1)), tuple)
>>> f(10)
(12, 20, 'I am groot')
```

### *class* i2.multi_object.MultiFunc(\*unnamed_funcs, \*\*named_funcs)

Bases: [`MultiObj`](_autosummary/i2.multi_object.html.md#i2.multi_object.MultiObj)

A `MultiObj` that only accepts callables; the base of `Pipe`, `FuncFanout` and friends.

```pycon
>>> mf = MultiFunc(len, up=str.upper)
>>> list(mf)
['len', 'up']
>>> mf.up("a")
'A'
>>> MultiFunc(len, 3)
Traceback (most recent call last):
  ...
TypeError: These were not callable: [3]
```

#### *property* funcs

Alias of .objects, for better readability in the context of MultiFunc

### *class* i2.multi_object.MultiObj(\*unnamed, \*\*named)

Bases: [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)

A base class that holds several named objects

```pycon
>>> from functools import partial
```

Let’s make a `MultiObj` with some miscellaneous objects.
(Note that `MultiObj` will usually be used for specific kinds of objects such as
callables or context managers. Here we chose the objects to demo what the
auto-naming does.)

```pycon
>>> mo = MultiObj([1], [1, 2], partial(print, sep=","), i='hi', ident=lambda x: x)
```

You now have a mapping and can do mapping things such as being able to list keys,
getting the length, seeing if a key is present, and getting the value for a key.

Note that the first and second list cannot be assigned the same name without creating a conflict.
In general one of the following will happen:
-you give it a name
-it tries to figure out a non conflicting name (if the object has a dunder name, etc)
-it falls back to a naming that is just the stringification of the argument’s positional index

```pycon
>>> list(mo) # not that the second item cannot be 'list', so a different name is given
['list', '_1', 'print', 'i', 'ident']
>>> len(mo)
5
>>> mo['_1']
[1, 2]
>>> 'list' in mo
True
>>> 'not a key of mo' in mo
False
```

When a key (always a string) is also a valid identifier, and in-so-far as
it doesn’t clash with other attributes, `MultiObj` will also give
you access to the names/keys of your objects via attributes.
(Note, this is similar to what `pandas.DataFrame` does with it’s columns names.)

```pycon
>>> mo.list
[1]
>>> mo.print
functools.partial(<built-in function print>, sep=',')
```

You can also specify an object mapping directly through a mapping:

```pycon
>>> mo = MultiObj({'this': [1], 'that': [1, 2]})
>>> dict(mo)
{'this': [1], 'that': [1, 2]}
```

You can specify an instance name and/or doc with the special (reserved) argument
names `__name__` and `__doc__` (which therefore can’t be used as object names:

```pycon
>>> mo = MultiObj(
... this=[1], that=[1, 2], __name__='this_and_that', __doc__='Nothing much'
... )
>>> dict(mo)
{'this': [1], 'that': [1, 2]}
>>> mo.__name__
'this_and_that'
>>> mo.__doc__
'Nothing much'
```

#### *static* auto_namer(exclude_names=(), obj_to_name=<function name_of_obj>, \*, name_for_position=())

Generate (name, object) pairs from an iterable of objects

* **Parameters:**
  * **objects** ([`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Obj`)]) – Objects to be named
  * **exclude_names** ([`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Names that can’t be used
  * **obj_to_name** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Obj`)], [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Function that tries to get/make a name from an object
  * **name_for_position** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – A `{position_idx: name,...}` mapping that instructs
    `uniquely_named_objects` to use a specific name for a given position.

```pycon
>>> from functools import partial
>>> objects = [map, [1], [1, 2], lambda x: x, partial(print, sep=",")]
>>> g = uniquely_named_objects(objects)
>>> names_and_objects = dict(g)
>>> list(names_and_objects)
['map', 'list', '_2', '_3', 'print']
```

That `'_2'` is there because both `[1]` and `[1, 2]` would be named `'list'`,
so to avoid that, a default name (revealing the position of the object in the
input `objects`) is given.
The ‘_3’ comes from the fact that the `lambda` function doesn’t have a proper
name (one that is a python identifier).

If we wanted the name for `[1]` to revert to the default positional name `'_1'`,
we can achieve this by forbidding the name `'list'`:

```pycon
>>> list(dict(uniquely_named_objects(objects, exclude_names={'list'})))
['map', '_1', '_2', '_3', 'print']
```

You could also acheive this by specifying this `'_1'` explicitly in the
`name_for_position` argument:

```pycon
>>> list(dict(uniquely_named_objects(objects, name_for_position={1: '_1'})))
['map', '_1', 'list', '_3', 'print']
```

The reason this `list` reappears as a name is that we didn’t exclude it, and
the name is not taken by the `[1]` argument anymore.
To get the desired effect with `name_for_position` we could therefore do this:

```pycon
>>> list(dict(uniquely_named_objects(objects, name_for_position={1: '_1', 2: '_2'})))
['map', '_1', '_2', '_3', 'print']
```

Obviously, `exclude_names` is the right argument for the problem above, but
what `name_for_position` does give you is the ability to explicitly chose the
names you want to assign to all or some of the elements of your iterable.

```pycon
>>> list(dict(uniquely_named_objects(
...     objects, name_for_position={1: 'first_list', 2: 'second_list', 3: 'lambda'}))
... )
['map', 'first_list', 'second_list', 'lambda', 'print']
```

Extra notes:

See what `uniquely_named_objects` offers as parametrization:

- You can provide an exclusion list (though the handing of a conflict is hardcoded
  and questionable)
- You can provide a `obj_to_name` function to control the naming of objects.

One trick to be aware of if objects have unique hashes: Make a
`d = {obj: name,...}` mapping and specify `obj_to_name=d.get`.

- Any controllable way to decide on a name based on the position of the function
  in the iterable (this could be useful!)

What you DO NOT have:

- Any way to choose names non-myopically: An object’s name cannot “see” the
  objects around it to decide on a name (it can only see the names use by those behind
  it through `exclude_names`).
- Any “retries” or “alternative naming logic” if a chosen name conflicts with
  `exclude_names`

### *class* i2.multi_object.ParallelFuncs(\*unnamed_funcs, \*\*named_funcs)

Bases: [`MultiFunc`](_autosummary/i2.multi_object.html.md#i2.multi_object.MultiFunc)

Make a multi-channel function from a {name: func, …} specification.

```pycon
>>> multi_func = ParallelFuncs(
...     say_hello=lambda x: f"hello {x}", say_goodbye=lambda x: f"goodbye {x}"
... )
>>> multi_func({'say_hello': 'world', 'say_goodbye': 'Lenin'})
{'say_hello': 'hello world', 'say_goodbye': 'goodbye Lenin'}
```

* **Parameters:**
  **spec** – A map between a name (str) and a function associated to that name
* **Returns:**
  A function that takes a dict as an (multi-channel) input and a dict as a
  (multi-channel) output

Q: Why can I specify the specs both with `named_funcs_dict` and `**named_funcs`?
A: Look at the `dict(...)` interface. You see the same thing there.
Different reason though (here we assert that the keys don’t overlap).
Usually named_funcs is more convenient, but if you need to use keys that are not
valid python variable names,
you can always use named_funcs_dict to express that!

```pycon
>>> multi_func = ParallelFuncs({
...     'x+y': lambda d: f"sum is {d}",
...     'x*y': lambda d: f"prod is {d}"}
... )
>>> multi_func({
...     'x+y': 5,
...     'x*y': 6
... })
{'x+y': 'sum is 5', 'x*y': 'prod is 6'}
```

You can also use both. Like with `dict(...)`.

Here’s a more significant example.

```pycon
>>> chunkers = {
...     'a': lambda x: x[0] + x[1],
...     'b': lambda x: x[0] * x[1]
... }
>>> featurizers = {
...     'a': lambda z: str(z),
...     'b': lambda z: [z] * 3
... }
>>> multi_chunker = ParallelFuncs(**chunkers)
>>> multi_chunker({'a': (1, 2), 'b': (3, 4)})
{'a': 3, 'b': 12}
>>> multi_featurizer = ParallelFuncs(**featurizers)
>>> multi_featurizer({'a': 3, 'b': 12})
{'a': '3', 'b': [12, 12, 12]}
>>> my_pipe = Pipe(multi_chunker, multi_featurizer)
>>> my_pipe({'a': (1, 2), 'b': (3, 4)})
{'a': '3', 'b': [12, 12, 12]}
```

#{‘a’: ‘(1, 2)’, ‘b’: [(3, 4), (3, 4), (3, 4)]}

### *class* i2.multi_object.Pipe(\*unnamed_funcs, \*\*named_funcs)

Bases: [`MultiFunc`](_autosummary/i2.multi_object.html.md#i2.multi_object.MultiFunc)

Simple function composition. That is, gives you a callable that implements

input -> f_1 -> … -> f_n -> output.

```pycon
>>> def foo(a, b=2):
...     return a + b
>>> f = Pipe(foo, lambda x: print(f"x: {x}"))
>>> f(3)
x: 5
```

You can name functions, but this would just be for documentation purposes.
The names are completely ignored.

```pycon
>>> g = Pipe(
...     add_numbers = lambda x, y: x + y,
...     multiply_by_2 = lambda x: x * 2,
...     stringify = str
... )
>>> g(2, 3)
'10'
```

### Notes

- Pipe instances don’t have a \_\_name_\_ etc. So some expectations of normal functions are not met.
- Pipe instance are pickalable (as long as the functions that compose them are)

You can specify a single functions:

```pycon
>>> Pipe(lambda x: x + 1)(1)
2
```

but

```pycon
>>> Pipe()
Traceback (most recent call last):
  ...
ValueError: You need to specify at least one function!
```

You can specify an instance name and/or doc with the special (reserved) argument
names `__name__` and `__doc__` (which therefore can’t be used as function names):

```pycon
>>> f = Pipe(map, add_it=sum, __name__='map_and_sum', __doc__='Apply func and add')
>>> f(lambda x: x * 10, [1, 2, 3])
60
>>> f.__name__
'map_and_sum'
>>> f.__doc__
'Apply func and add'
```

### i2.multi_object.ensure_iterable_of_callables(x)

Assert that the input is an iterable of callables,
or wrap a single callable in an iterable.

### i2.multi_object.flatten_pipe(pipe)

Unravel nested Pipes to get a flat ‘sequence of functions’ version of input.

```pycon
>>> def f(x): return x + 1
>>> def g(x): return x * 2
>>> def h(x): return x - 3
>>> a = Pipe(f, g, h)
>>> b = Pipe(f, Pipe(g, h))
>>> len(a)
3
>>> len(b)
2
>>> c = flatten_pipe(b)
>>> len(c)
3
>>> assert a(10) == b(10) == c(10) == 19
```

### i2.multi_object.iterable_of_callables_validation(funcs)

Raise `TypeError` unless `funcs` is an iterable whose elements are all callable.

### i2.multi_object.merge_unnamed_and_named(\*unnamed, \*\*named)

To merge unnamed and named arguments into a single (named) dict of arguments

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

```pycon
>>> merge_unnamed_and_named(10, 20, thirty=30, fourty=40)
{'_0': 10, '_1': 20, 'thirty': 30, 'fourty': 40}
```

### i2.multi_object.name_of_obj(o, default=None)

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

This is a basic implementation, and it’s not guaranteed to work for all objects.
For a more powerful, and customizable implementation,
see `i2.signatures.name_of_obj`.

* **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'
```

### i2.multi_object.pipes_are_equal(p1, p2, \*, func_equality=<built-in function eq>, verbose=False)

Determine if two pipelines are equal.

Pipelines are equal if their flattened versions have equal functions.
Function equality can be controlled by the `func_equality` argument.
The `verbose` argument will print some more information about why the pipelines
are not equal.

```pycon
>>> def f(x): return x + 1
>>> def g(x): return x * 2
>>> def h(x): return x - 3
>>> a = Pipe(f, g, h)
>>> b = Pipe(f, g, h)
>>> c = Pipe(f, Pipe(g, h))
>>> assert a(10) == b(10) == c(10) == 19
>>> pipes_are_equal(a, b)
True
>>> pipes_are_equal(a, c)
True
>>> pipes_are_equal(Pipe(f, g), Pipe(g, h))
False
>>> pipes_are_equal(Pipe(f, g), Pipe(f, g, h))
False
```

Get more information when pipes are not equal.

```pycon
>>> pipes_are_equal(Pipe(f, g), Pipe(f, g, h), verbose=True)
--> Flattened pipes do not have the same number of functions: len(p1)=2 != len(p2)=3
False
```

Change how functions are compared for equality:

```pycon
>>> pipes_are_equal(Pipe(lambda x: x), Pipe(lambda x: x))
False
>>> from inspect import getsource
>>> source_equality = lambda f, ff: getsource(f) == getsource(ff)
>>> pipes_are_equal(
...     Pipe(lambda x: x), Pipe(lambda x: x), func_equality=source_equality
... )
True
```

### i2.multi_object.truncate_string_with_marker(s, , left_limit=15, right_limit=15, middle_marker='...')

Return a string with a limited length.

If the string is longer than the sum of the left_limit and right_limit,
the string is truncated and the middle_marker is inserted in the middle.

If the string is shorter than the sum of the left_limit and right_limit,
the string is returned as is.

```pycon
>>> truncate_string_with_marker('1234567890')
'1234567890'
```

But if the string is longer than the sum of the limits, it is truncated:

```pycon
>>> truncate_string_with_marker('1234567890', left_limit=3, right_limit=3)
'123...890'
>>> truncate_string_with_marker('1234567890', left_limit=3, right_limit=0)
'123...'
>>> truncate_string_with_marker('1234567890', left_limit=0, right_limit=3)
'...890'
```

If you’re using a specific parametrization of the function often, you can
create a partial function with the desired parameters:

```pycon
>>> from functools import partial
>>> truncate_string = partial(truncate_string_with_marker, left_limit=2, right_limit=2, middle_marker='---')
>>> truncate_string('1234567890')
'12---90'
>>> truncate_string('supercalifragilisticexpialidocious')
'su---us'
```

### i2.multi_object.uniquely_named_objects(objects, exclude_names=(), obj_to_name=<function name_of_obj>, \*, name_for_position=())

Generate (name, object) pairs from an iterable of objects

* **Parameters:**
  * **objects** ([`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Obj`)]) – Objects to be named
  * **exclude_names** ([`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Names that can’t be used
  * **obj_to_name** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Obj`)], [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Function that tries to get/make a name from an object
  * **name_for_position** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – A `{position_idx: name,...}` mapping that instructs
    `uniquely_named_objects` to use a specific name for a given position.

```pycon
>>> from functools import partial
>>> objects = [map, [1], [1, 2], lambda x: x, partial(print, sep=",")]
>>> g = uniquely_named_objects(objects)
>>> names_and_objects = dict(g)
>>> list(names_and_objects)
['map', 'list', '_2', '_3', 'print']
```

That `'_2'` is there because both `[1]` and `[1, 2]` would be named `'list'`,
so to avoid that, a default name (revealing the position of the object in the
input `objects`) is given.
The ‘_3’ comes from the fact that the `lambda` function doesn’t have a proper
name (one that is a python identifier).

If we wanted the name for `[1]` to revert to the default positional name `'_1'`,
we can achieve this by forbidding the name `'list'`:

```pycon
>>> list(dict(uniquely_named_objects(objects, exclude_names={'list'})))
['map', '_1', '_2', '_3', 'print']
```

You could also acheive this by specifying this `'_1'` explicitly in the
`name_for_position` argument:

```pycon
>>> list(dict(uniquely_named_objects(objects, name_for_position={1: '_1'})))
['map', '_1', 'list', '_3', 'print']
```

The reason this `list` reappears as a name is that we didn’t exclude it, and
the name is not taken by the `[1]` argument anymore.
To get the desired effect with `name_for_position` we could therefore do this:

```pycon
>>> list(dict(uniquely_named_objects(objects, name_for_position={1: '_1', 2: '_2'})))
['map', '_1', '_2', '_3', 'print']
```

Obviously, `exclude_names` is the right argument for the problem above, but
what `name_for_position` does give you is the ability to explicitly chose the
names you want to assign to all or some of the elements of your iterable.

```pycon
>>> list(dict(uniquely_named_objects(
...     objects, name_for_position={1: 'first_list', 2: 'second_list', 3: 'lambda'}))
... )
['map', 'first_list', 'second_list', 'lambda', 'print']
```

Extra notes:

See what `uniquely_named_objects` offers as parametrization:

- You can provide an exclusion list (though the handing of a conflict is hardcoded
  and questionable)
- You can provide a `obj_to_name` function to control the naming of objects.

One trick to be aware of if objects have unique hashes: Make a
`d = {obj: name,...}` mapping and specify `obj_to_name=d.get`.

- Any controllable way to decide on a name based on the position of the function
  in the iterable (this could be useful!)

What you DO NOT have:

- Any way to choose names non-myopically: An object’s name cannot “see” the
  objects around it to decide on a name (it can only see the names use by those behind
  it through `exclude_names`).
- Any “retries” or “alternative naming logic” if a chosen name conflicts with
  `exclude_names`


# _autosummary/i2.routing_forest.html.md

# i2.routing_forest

Tools to specify functions through trees and forests.

Whaaa?!?

Well, you see, often – especially when writing transformers – you have a series of
if/then conditions nested into eachother, in code, where it gets ugly and un-reusable.

This module explores ways to objectivy this: That is, to give us the means to create
such nested conditions in a way that we can define the parts as reusable operable
components.

Think of the relationship between the for loop (code) and the iterator (object), along
with iterator tools (itertools).
This is what we’re trying to explore, but for if/then conditions.

I said explore. Some more work is needed here to make it robust and easily usable.

Let’s look at an example involving the three main actors of our play.
Each of these are `Iterable` and `Callable` (`Generator` to be precise).

- `CondNode`: implements the if/then (no else) logic
- `FinalNode`: Final – yields (both with call and iter) it’s single `.val` attribute.
- `RoutingForest`: An Iterable of `CondNode`

You’ll note that instances of these classes are all both callables and iterables,
and that when called, they return iterables.
It’s this aspect that makes us be able to nest conditions within conditions,
and further, control the flow of the iteration from outside.
A routing node (or forest) called on an object will yield all values that match the
conditions that were specified for it.
For example, if you need all matches, you can wrap it with `list`, if you need the
first match only, you can wrap it with `next`, if you have a default value,
you can wrap it in `next` with a default value.

```pycon
>>> import inspect
>>>
>>> def could_be_int(obj):
...     if isinstance(obj, int):
...         b = True
...     else:
...         try:
...             int(obj)
...             b = True
...         except ValueError:
...             b = False
...     if b:
...         print(f'{inspect.currentframe().f_code.co_name}')
...     return b
...
>>> def could_be_float(obj):
...     if isinstance(obj, float):
...         b = True
...     else:
...         try:
...             float(obj)
...             b = True
...         except ValueError:
...             b = False
...     if b:
...         print(f'{inspect.currentframe().f_code.co_name}')
...     return b
...
>>> print(
...     could_be_int(30),
...     could_be_int(30.3),
...     could_be_int('30.2'),
...     could_be_int('nope'),
... )
could_be_int
could_be_int
True True False False
>>> print(
...     could_be_float(30),
...     could_be_float(30.3),
...     could_be_float('30.2'),
...     could_be_float('nope'),
... )
could_be_float
could_be_float
could_be_float
True True True False
>>> assert could_be_int('30.2') is False
>>> assert could_be_float('30.2') is True
could_be_float
>>>
>>> st = RoutingForest(
...     [
...         CondNode(
...             cond=could_be_int,
...             then=RoutingForest(
...                 [
...                     CondNode(
...                         cond=lambda x: int(x) >= 10,
...                         then=FinalNode('More than a digit'),
...                     ),
...                     CondNode(
...                         cond=lambda x: (int(x) % 2) == 1,
...                         then=FinalNode("That's odd!"),
...                     ),
...                 ]
...             ),
...         ),
...         CondNode(cond=could_be_float, then=FinalNode('could be seen as a float')),
...     ]
... )
>>> assert list(st('nothing I can do with that')) == []
>>> assert list(st(8)) == ['could be seen as a float']
could_be_int
could_be_float
>>> assert list(st(9)) == ["That's odd!", 'could be seen as a float']
could_be_int
could_be_float
>>> assert list(st(10)) == ['More than a digit', 'could be seen as a float']
could_be_int
could_be_float
>>> assert list(st(11)) == [
...     'More than a digit',
...     "That's odd!",
...     'could be seen as a float',
... ]
could_be_int
could_be_float
>>>
>>> print(
...     '### RoutingForest ########################################################################################'
... )
### RoutingForest ########################################################################################
>>> rf = RoutingForest(
...     [
...         SwitchCaseNode(
...             switch=lambda x: x % 5,
...             cases={0: FinalNode('zero_mod_5'), 1: FinalNode('one_mod_5')},
...             default=FinalNode('default_mod_5'),
...         ),
...         SwitchCaseNode(
...             switch=lambda x: x % 2,
...             cases={0: FinalNode('even'), 1: FinalNode('odd')},
...             default=FinalNode('that is not an int'),
...         ),
...     ]
... )
>>>
>>> assert list(rf(5)) == ['zero_mod_5', 'odd']
>>> assert list(rf(6)) == ['one_mod_5', 'even']
>>> assert list(rf(7)) == ['default_mod_5', 'odd']
>>> assert list(rf(8)) == ['default_mod_5', 'even']
>>> assert list(rf(10)) == ['zero_mod_5', 'even']
>>>
```

### Functions

| [`identity`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.identity)(obj)                    | Return the input unchanged (the default leaf function).                           |
|-----------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|
| [`return_sentinel`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.return_sentinel)(obj[, sentinel]) | Return a constanc sentinel value when called.                                     |
| [`test_routing_forest`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.test_routing_forest)()            | Exercise the routing nodes end to end (kept here as a runnable example).          |
| [`wrap_leafs_with_final_node`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.wrap_leafs_with_final_node)(x)    | Yield the items of `x`, wrapping those that are not `RoutingNode` in `FinalNode`. |

### Classes

| [`CondNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.CondNode)(cond, then)                            | A RoutingNode that implements the if/then (no else) logic                                                                                                                                                              |
|--------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`DelegateToMappingAttrMixin`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.DelegateToMappingAttrMixin)()                    | A mixin to delegate `Mapping` methods to a mapping attribute called `mapping`                                                                                                                                          |
| [`FeatCondNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.FeatCondNode)(feat, feat_cond_thens)             | A RoutingNode that yields multiple routes, one for each of several conditions met, where the condition is computed implements computes a feature of the obj and according to an iterable of conditions on the feature. |
| [`FinalNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.FinalNode)(val)                                  | A RoutingNode that is final.                                                                                                                                                                                           |
| [`KeyFuncMapping`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.KeyFuncMapping)([mapping, key, default_factory]) | Implements a switch-case-like mapping with a callable key function.                                                                                                                                                    |
| `NoDefault`()                                                                                    |                                                                                                                                                                                                                        |
| [`RoutingForest`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.RoutingForest)(cond_nodes)                       |                                                                                                                                                                                                                        |
| [`RoutingNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.RoutingNode)()                                   | A RoutingNode instance needs to be callable on a single object, yielding an iterable or a final value                                                                                                                  |
| [`SwitchCaseNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.SwitchCaseNode)(switch, cases[, default])        | A RoutingNode that implements the switch/case/else logic.                                                                                                                                                              |

### *class* i2.routing_forest.CondNode(cond, then)

Bases: [`RoutingNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.RoutingNode)

A RoutingNode that implements the if/then (no else) logic

### *class* i2.routing_forest.DelegateToMappingAttrMixin

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

A mixin to delegate `Mapping` methods to a mapping attribute called `mapping`

### *class* i2.routing_forest.FeatCondNode(feat, feat_cond_thens)

Bases: [`RoutingNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.RoutingNode)

A RoutingNode that yields multiple routes, one for each of several conditions
met, where the condition is computed implements computes a feature of the obj and
according to an iterable of conditions on the feature.

```pycon
>>> fcn = FeatCondNode(
...     feat=lambda x: x % 5,
...     feat_cond_thens=[
...         (lambda x: x == 0, lambda x: 'zero_mod_5'),
...         (lambda x: x == 1, lambda x: 'one_mod_5'),
...         (lambda x: x == 2, lambda x: 'two_mod_5'),
...         (lambda x: x == 3, lambda x: 'three_mod_5'),
...         (lambda x: x == 4, lambda x: 'four_mod_5'),
...     ]
... )
>>> assert list(fcn(0)) == ['zero_mod_5']
>>> assert list(fcn(1)) == ['one_mod_5']
>>> assert list(fcn(2)) == ['two_mod_5']
>>> assert list(fcn(3)) == ['three_mod_5']
>>> assert list(fcn(4)) == ['four_mod_5']
>>> assert list(fcn(5)) == ['zero_mod_5']
>>> assert list(fcn(6)) == ['one_mod_5']
```

#### *classmethod* from_feature_val_map(feat, feat_cond_thens)

A FeatCondNode where the conditions are equality checks on the feature value

# >>> fvn = FeatCondNode.from_feature_val_map(
# …     feat=lambda x: x % 3,
# …     feat_cond_thens={
# …         0: lambda x: ‘zero_mod_3’,
# …         1: lambda x: ‘one_mod_3’,
# …         2: lambda x: ‘two_mod_3’,
# …     }
# … )
# >>> list(fvn(0))
#
# >>> assert list(fvn(0)) == [‘zero_mod_3’]
# >>> assert list(fvn(1)) == [‘one_mod_3’]
# >>> assert list(fvn(2)) == [‘two_mod_3’]
#

### *class* i2.routing_forest.FinalNode(val)

Bases: [`RoutingNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.RoutingNode)

A RoutingNode that is final.
It yields (both with call and iter) it’s single `.val` attribute.

### *class* i2.routing_forest.KeyFuncMapping(mapping=None, key=<function identity>, default_factory=<function return_sentinel>)

Bases: [`DelegateToMappingAttrMixin`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.DelegateToMappingAttrMixin), [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)

Implements a switch-case-like mapping with a callable key function.

The purpose of `KeyFuncMapping` is to  allow switch-case logic to be
given as a plugin specification.

```pycon
>>> from i2.routing_forest import KeyFuncMapping
>>>
>>> get_extension = lambda x: x.split('.')[-1]
>>>
>>> data_type = KeyFuncMapping(
...     {'csv': 'table', 'xls': 'table', 'wav': 'audio'}, key=get_extension
... )
```

Calling a `KeyFuncMapping` instance will call the `key` function on the input,
then look up the result in the `mapping`.

```pycon
>>> data_type('my_file.csv')
'table'
>>> data_type('another_file.xls')
'table'
>>> data_type('sound.wav')
'audio'
```

If the key is not found in the mapping, the `default_factory` is **called**
with the input and the result is returned. The default `default_factory` is
`return_sentinel`, which by default returns `None`

```pycon
>>> assert data_type('poem.txt') is None
```

Note that instances of `KeyFuncMapping` are also `Mapping``s, so all ``Mapping`
methods can be used.

```pycon
>>> list(data_type)
['csv', 'xls', 'wav']
>>> dict(data_type)
{'csv': 'table', 'xls': 'table', 'wav': 'audio'}
```

Including `update`, which constitutes a convenient way to extend the mapping.

```pycon
>>> data_type.update(txt='text')
>>> data_type('poem.txt')
'text'
```

The `default_factory` can be set to any callable, including a
`KeyFuncMapping` itself, which enables us to define an `else` for the
switch-case logic that a `KeyFuncMapping` implements.
Say, for example, if no handled extension is found, we want to check the protocol
of the input string instead. This is not only a new mapping, but also a new key
function. We can do it as such:

```pycon
>>> get_protocol = lambda x: x.split('://')[0]
>>> protocol = KeyFuncMapping({'https': 'url'}, get_protocol)
>>> new_data_type = KeyFuncMapping(
...     data_type.mapping, data_type.key, default_factory=protocol
... )
>>> new_data_type('notes.txt')
'text'
>>> new_data_type('https://www.python.org/')
'url'
```

Given how useful this pattern is, we made the `+` operator implement this.
Note that here, `+` is not associative or commutative (as with numbers).
It should be understood to function more like the `+` for iterables like `list`
and `tuple`.

```pycon
>>> nested = data_type + protocol
>>> nested('https://www.python.org/')
'url'
>>> nested('jazz.wav')
'audio'
```

#### default_factory(sentinel=None)

Return a constanc sentinel value when called. Use partial to set sentinel

#### key()

Return the input unchanged (the default leaf function).

### *class* i2.routing_forest.RoutingForest(cond_nodes)

Bases: [`RoutingNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.RoutingNode)

```pycon
>>> rf = RoutingForest([
...     CondNode(cond=lambda x: isinstance(x, int),
...              then=RoutingForest([
...                  CondNode(cond=lambda x: int(x) >= 10, then=FinalNode('More than a digit')),
...                  CondNode(cond=lambda x: (int(x) % 2) == 1, then=FinalNode("That's odd!"))])
...             ),
...     CondNode(cond=lambda x: isinstance(x, (int, float)),
...              then=FinalNode('could be seen as a float')),
... ])
>>> assert list(rf('nothing I can do with that')) == []
>>> assert list(rf(8)) == ['could be seen as a float']
>>> assert list(rf(9)) == ["That's odd!", 'could be seen as a float']
>>> assert list(rf(10)) == ['More than a digit', 'could be seen as a float']
>>> assert list(rf(11)) == ['More than a digit', "That's odd!", 'could be seen as a float']
```

### *class* i2.routing_forest.RoutingNode

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

A RoutingNode instance needs to be callable on a single object,
yielding an iterable or a final value

#### *static* from_object(x, mini_lang=<function \_default_mini_lang>)

Converts an object to a RoutingNode instance.
Enables mini-languages to be developed for defining routing trees.

### *class* i2.routing_forest.SwitchCaseNode(switch, cases, default=<i2.routing_forest.NoDefault object>)

Bases: [`RoutingNode`](_autosummary/i2.routing_forest.html.md#i2.routing_forest.RoutingNode)

A RoutingNode that implements the switch/case/else logic.
It’s just a specialization (enhanced with a “default” option) of the FeatCondNode
class to a situation where the cond function of feat_cond_thens is equality,
therefore the routing can be
implemented with a {value_to_compare_to_feature: then_node} map.

* **Parameters:**
  * **switch** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)) – A function returning the feature of an object we want to switch on
  * **cases** ([`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)) – The mapping from feature to RoutingNode that should be yield for that
    feature. It is often a dict, but only requirement is that it implements the
    `cases.get(val, default)` method.
  * **default** ([`Any`](https://docs.python.org/3/library/typing.html#typing.Any)) – Default RoutingNode to yield if no

```pycon
>>> rf = RoutingForest([
...     SwitchCaseNode(switch=lambda x: x % 5,
...                    cases={0: FinalNode('zero_mod_5'), 1: FinalNode('one_mod_5')},
...                    default=FinalNode('default_mod_5')),
...     SwitchCaseNode(switch=lambda x: x % 2,
...                    cases={0: FinalNode('even'), 1: FinalNode('odd')},
...                    default=FinalNode('that is not an int')),
... ])
>>>
>>> assert(list(rf(5)) == ['zero_mod_5', 'odd'])
>>> assert(list(rf(6)) == ['one_mod_5', 'even'])
>>> assert(list(rf(7)) == ['default_mod_5', 'odd'])
>>> assert(list(rf(8)) == ['default_mod_5', 'even'])
>>> assert(list(rf(10)) == ['zero_mod_5', 'even'])
```

### i2.routing_forest.identity(obj)

Return the input unchanged (the default leaf function).

### i2.routing_forest.return_sentinel(obj, sentinel=None)

Return a constanc sentinel value when called. Use partial to set sentinel

### i2.routing_forest.test_routing_forest()

Exercise the routing nodes end to end (kept here as a runnable example).

### i2.routing_forest.wrap_leafs_with_final_node(x)

Yield the items of `x`, wrapping those that are not `RoutingNode` in `FinalNode`.


# _autosummary/i2.signatures.html.md

# i2.signatures

Signature calculus: Tools to make it easier to work with function’s signatures.

How to:

> - get names, kinds, defaults, annotations
> - make signatures flexibly
> - merge two or more signatures
> - give a function a specific signature (with a choice of validations)
> - get an equivalent function with a different order of arguments
> - get an equivalent function with a subset of arguments (like partial)
> - get an equivalent function but with variadic `*args` and/or `**kwargs` replaced with
>   non-variadic args (tuple) and kwargs (dict)
> - make an f(a) function in to a f(a, b=None) function with b ignored

Get names, kinds, defaults, annotations:

```pycon
>>> def func(z, a: float=1.0, /, b=2, *, c: int=3):
...     pass
>>> sig = Sig(func)
>>> sig.names
['z', 'a', 'b', 'c']
>>> from inspect import Parameter
>>> assert sig.kinds == {
...     'z': Parameter.POSITIONAL_ONLY,
...     'a': Parameter.POSITIONAL_ONLY,
...     'b': Parameter.POSITIONAL_OR_KEYWORD,
...     'c': Parameter.KEYWORD_ONLY
... }
>>> # Note z is not in there (only defaulted params are included)
>>> sig.defaults
{'a': 1.0, 'b': 2, 'c': 3}
>>> sig.annotations
{'a': <class 'float'>, 'c': <class 'int'>}
```

Make signatures flexibly:

```pycon
>>> Sig(func)
<Sig (z, a: float = 1.0, /, b=2, *, c: int = 3)>
>>> Sig(['a', 'b'])
<Sig (a, b)>
>>> Sig('x y z')
<Sig (x, y, z)>
```

Merge signatures.

```pycon
>>> def foo(x): pass
>>> def bar(y: int, *, z=2): pass  # note the * (keyword only) will be lost!
>>> Sig(foo) + ['a', 'b'] + Sig(bar)
<Sig (x, a, b, y: int, z=2)>
```

Give a function a signature.

```pycon
>>> @Sig('a b c')
... def func(*args, **kwargs):
...     print(args, kwargs)
>>> Sig(func)
<Sig (a, b, c)>
```

**Notes to the reader**

Both in the code and in the docs, we’ll use short hands for parameter (argument) kind.

> - PK = Parameter.POSITIONAL_OR_KEYWORD
> - VP = Parameter.VAR_POSITIONAL
> - VK = Parameter.VAR_KEYWORD
> - PO = Parameter.POSITIONAL_ONLY
> - KO = Parameter.KEYWORD_ONLY

### Module Attributes

| [`PYTHON_DEFINED_CALLABLE_TYPES`](_autosummary/i2.signatures.html.md#i2.signatures.PYTHON_DEFINED_CALLABLE_TYPES)   | Callable kinds that are defined in Python (as opposed to C-level builtins) and therefore always carry authoritative signature information of their own.   |
|----------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|

### Functions

| [`all_pk_signature`](_autosummary/i2.signatures.html.md#i2.signatures.all_pk_signature)(callable_or_signature)               | Changes all (non-variadic) arguments to be of the PK (POSITION_OR_KEYWORD) kind.                                                  |
|--------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
| [`assure_callable`](_autosummary/i2.signatures.html.md#i2.signatures.assure_callable)(obj)                                  | Return `obj` if callable, else an empty function carrying the signature `obj` specifies.                                          |
| [`assure_params`](_autosummary/i2.signatures.html.md#i2.signatures.assure_params)([obj])                                  | Get an interable of Parameter instances from an object.                                                                           |
| [`assure_signature`](_autosummary/i2.signatures.html.md#i2.signatures.assure_signature)(obj)                                 | Make an `inspect.Signature` from a signature, callable, parameter, iterable of parameter specs, or `None` (empty signature).      |
| [`call_forgivingly`](_autosummary/i2.signatures.html.md#i2.signatures.call_forgivingly)(func, \*args, \*\*kwargs)            | Call function on given args and kwargs, but only taking what the function needs (not choking if they're extras variables)         |
| [`call_somewhat_forgivingly`](_autosummary/i2.signatures.html.md#i2.signatures.call_somewhat_forgivingly)(func, args, kwargs)         | Call function on given args and kwargs, but with controllable argument leniency.                                                  |
| [`ch_func_to_all_pk`](_autosummary/i2.signatures.html.md#i2.signatures.ch_func_to_all_pk)(func)                               | Returns a decorated function where all arguments are of the PK kind.                                                              |
| [`ch_signature_to_all_pk`](_autosummary/i2.signatures.html.md#i2.signatures.ch_signature_to_all_pk)(callable_or_signature)         | Changes all (non-variadic) arguments to be of the PK (POSITION_OR_KEYWORD) kind.                                                  |
| [`ch_variadics_to_non_variadic_kind`](_autosummary/i2.signatures.html.md#i2.signatures.ch_variadics_to_non_variadic_kind)(func, \*[, ...])    | Replace a function's variadic parameters with a tuple and a dict parameter of the same names, returning an equivalent function.   |
| [`common_and_diff_argnames`](_autosummary/i2.signatures.html.md#i2.signatures.common_and_diff_argnames)(func1, func2)                | Get list of argument names that are common to two functions, as well as the two lists of names that are different                 |
| [`compare_signatures`](_autosummary/i2.signatures.html.md#i2.signatures.compare_signatures)(func1, func2[, ...])               | Compare the `Sig` of two callables with `signature_comparator` (equality by default).                                             |
| [`convert_to_PK`](_autosummary/i2.signatures.html.md#i2.signatures.convert_to_PK)(kinds)                                  | A `{name: POSITIONAL_OR_KEYWORD}` dict for every name in `kinds` (a `kinds_modifier`).                                            |
| [`copy_func`](_autosummary/i2.signatures.html.md#i2.signatures.copy_func)(f)                                          | Copy a function (not sure it works with all types of callables).                                                                  |
| [`defaults_are_the_same_when_not_empty`](_autosummary/i2.signatures.html.md#i2.signatures.defaults_are_the_same_when_not_empty)(dflt1, ...)      | Check if two defaults are the same when they are not empty.                                                                       |
| [`deprecation_of`](_autosummary/i2.signatures.html.md#i2.signatures.deprecation_of)(func, old_name)                        | Wrap `func` so that calling it warns that `old_name` is deprecated in favour of `func`.                                           |
| [`dflt1_is_empty_or_dflt2_is_not`](_autosummary/i2.signatures.html.md#i2.signatures.dflt1_is_empty_or_dflt2_is_not)(dflt1, dflt2)          | Why such a strange default comparison function?                                                                                   |
| [`dict_of_attribute_signatures`](_autosummary/i2.signatures.html.md#i2.signatures.dict_of_attribute_signatures)(cls)                     | Extract the signatures of all callable attributes of a class, as a `{name: signature}` dict.                                      |
| [`ensure_callable`](_autosummary/i2.signatures.html.md#i2.signatures.ensure_callable)(obj)                                  | Return `obj` if callable, else an empty function carrying the signature `obj` specifies.                                          |
| [`ensure_param`](_autosummary/i2.signatures.html.md#i2.signatures.ensure_param)(p)                                       | Make a `Param` from a parameter, a name, a `(name, default[, annotation])` tuple, or a dict of `Param` keyword arguments.         |
| [`ensure_params`](_autosummary/i2.signatures.html.md#i2.signatures.ensure_params)([obj])                                  | Get an interable of Parameter instances from an object.                                                                           |
| [`ensure_signature`](_autosummary/i2.signatures.html.md#i2.signatures.ensure_signature)(obj)                                 | Make an `inspect.Signature` from a signature, callable, parameter, iterable of parameter specs, or `None` (empty signature).      |
| [`expand_nested_key`](_autosummary/i2.signatures.html.md#i2.signatures.expand_nested_key)(d, k)                               | Items of `d`, except that a lone `{k: {k: ...}}` nesting is unwrapped first.                                                      |
| [`extract_arguments`](_autosummary/i2.signatures.html.md#i2.signatures.extract_arguments)(params, \*[, ...])                  | Extract arguments needed to satisfy the params of a callable, dealing with the dirty details.                                     |
| [`flatten_if_var_kw`](_autosummary/i2.signatures.html.md#i2.signatures.flatten_if_var_kw)(kvs, var_kw_name)                   | Yield `(key, value)` pairs, replacing a `(var_kw_name, {var_kw_name: d})` pair by the items of `d`.                               |
| [`function_caller`](_autosummary/i2.signatures.html.md#i2.signatures.function_caller)(func, args, kwargs)                   | Call `func(*args, **kwargs)`; the default "caller" of the wrapping tools.                                                         |
| [`has_signature`](_autosummary/i2.signatures.html.md#i2.signatures.has_signature)(obj[, robust])                          | Check if an object has a signature -- i.e. is callable and inspect.signature( obj) returns something.                             |
| [`ignore_any_differences`](_autosummary/i2.signatures.html.md#i2.signatures.ignore_any_differences)(x, y)                          | A comparator that always returns `True` (used to ignore a parameter attribute).                                                   |
| [`insert_annotations`](_autosummary/i2.signatures.html.md#i2.signatures.insert_annotations)(s, /, \*, ...)                     | Insert annotations in a signature.                                                                                                |
| [`is_call_compatible_with`](_autosummary/i2.signatures.html.md#i2.signatures.is_call_compatible_with)(sig1, sig2, \*[, ...])        | Return True if `sig1` is compatible with `sig2`.                                                                                  |
| [`is_signature_error`](_autosummary/i2.signatures.html.md#i2.signatures.is_signature_error)(e)                                 | Check if an exception is a signature error                                                                                        |
| [`keyed_comparator`](_autosummary/i2.signatures.html.md#i2.signatures.keyed_comparator)(comparator, key)                     | Create a key-function enabled binary operator.                                                                                    |
| [`kind_forgiving_func`](_autosummary/i2.signatures.html.md#i2.signatures.kind_forgiving_func)(func[, kinds_modifier])           | Wraps the func, changing the argument kinds according to kinds_modifier.                                                          |
| [`maybe_first`](_autosummary/i2.signatures.html.md#i2.signatures.maybe_first)(items)                                    | The first item of `items`, or `None` if there is none.                                                                            |
| [`mk_func_comparator_based_on_signature_comparator`](_autosummary/i2.signatures.html.md#i2.signatures.mk_func_comparator_based_on_signature_comparator)(...) | Make a function comparator that compares two callables through their signatures.                                                  |
| [`mk_sig_from_args`](_autosummary/i2.signatures.html.md#i2.signatures.mk_sig_from_args)(\*args_without_default, ...)         | Make a Signature instance by specifying args_without_default and args_with_defaults.                                              |
| [`name_of_obj`](_autosummary/i2.signatures.html.md#i2.signatures.name_of_obj)(o, \*[, base_name_of_obj, ...])           | Tries to find the (or "a") name for an object, even if `__name__` doesn't exist.                                                  |
| [`name_of_var_kw_argument`](_autosummary/i2.signatures.html.md#i2.signatures.name_of_var_kw_argument)(sig)                          | The name of the VAR_KEYWORD parameter of `sig`, or `None` if it has none.                                                         |
| [`normalized_func`](_autosummary/i2.signatures.html.md#i2.signatures.normalized_func)(func)                                 | Wrap `func` so its call arguments are re-bound through `func`'s own signature.                                                    |
| [`param_attribute_dict`](_autosummary/i2.signatures.html.md#i2.signatures.param_attribute_dict)(...)                             | Zip four comparison results into a `{name, kind, default, annotation}` dict (an `aggreg`).                                        |
| [`param_binary_func`](_autosummary/i2.signatures.html.md#i2.signatures.param_binary_func)(param1, param2, \*[, name, ...])    | Compare two parameters.                                                                                                           |
| [`param_comparator`](_autosummary/i2.signatures.html.md#i2.signatures.param_comparator)(param1, param2, \*[, name, ...])     | Compare two parameters.                                                                                                           |
| [`param_differences_dict`](_autosummary/i2.signatures.html.md#i2.signatures.param_differences_dict)(param1, param2, \*[, ...])     | Makes a dictionary exibiting the differences between two parameters.                                                              |
| [`param_for_kind`](_autosummary/i2.signatures.html.md#i2.signatures.param_for_kind)([name, kind, with_default])            | Make an `inspect.Parameter` of a given kind, with a generated name and default if not given (handy in tests).                     |
| [`param_has_default_or_is_var_kind`](_autosummary/i2.signatures.html.md#i2.signatures.param_has_default_or_is_var_kind)(p)                   | Whether the parameter is optional in a call: it has a default or is variadic.                                                     |
| [`parameter_to_dict`](_autosummary/i2.signatures.html.md#i2.signatures.parameter_to_dict)(p)                                  | The `name`, `kind`, `default` and `annotation` of a parameter, as a dict.                                                         |
| [`params_of`](_autosummary/i2.signatures.html.md#i2.signatures.params_of)(obj)                                        | The list of `Parameter` objects of a signature, a name-to-parameter mapping, or a callable.                                       |
| [`postprocess`](_autosummary/i2.signatures.html.md#i2.signatures.postprocess)(egress)                                   | Make a decorator that applies `egress` to the output of the wrapped function.                                                     |
| [`replace_kwargs_using`](_autosummary/i2.signatures.html.md#i2.signatures.replace_kwargs_using)(sig)                             | Decorator that replaces the variadic keyword argument of the target function using the `sig`, the signature of a source function. |
| [`resolve_function`](_autosummary/i2.signatures.html.md#i2.signatures.resolve_function)(obj)                                 | Get the underlying function of a property or cached_property                                                                      |
| [`return_tuple`](_autosummary/i2.signatures.html.md#i2.signatures.return_tuple)(x, y)                                    | A comparator that returns the `(x, y)` pair itself instead of a verdict.                                                          |
| [`set_signature_of_func`](_autosummary/i2.signatures.html.md#i2.signatures.set_signature_of_func)(func, parameters, \*, ...)      | Set the signature of a function, with sugar.                                                                                      |
| [`sig_to_dataclass`](_autosummary/i2.signatures.html.md#i2.signatures.sig_to_dataclass)(sig, \*[, cls_name, bases, ...])     | Make a `class` (through `make_dataclass`) from the given signature.                                                               |
| [`sort_params`](_autosummary/i2.signatures.html.md#i2.signatures.sort_params)(params)                                   |                                                                                                                                   |
| [`use_interface`](_autosummary/i2.signatures.html.md#i2.signatures.use_interface)(interface_sig)                          | Use interface_sig as (enforced/validated) signature of the decorated function.                                                    |
| [`validate_signature`](_autosummary/i2.signatures.html.md#i2.signatures.validate_signature)(func)                              | Validates the signature of a function.                                                                                            |

### Classes

| [`MissingArgValFor`](_autosummary/i2.signatures.html.md#i2.signatures.MissingArgValFor)(argname)                    | A simple class to wrap an argument name, indicating that it was missing somewhere.                                                                                     |
|-----------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`P`](_autosummary/i2.signatures.html.md#i2.signatures.P)                                            |                                                                                                                                                                        |
| [`Param`](_autosummary/i2.signatures.html.md#i2.signatures.Param)(name[, kind])                          | A thin wrap of Parameters: Adds shorter aliases to argument kinds and a POSITIONAL_OR_KEYWORD default to the argument kind to make it faster to make Parameter objects |
| [`Sig`](_autosummary/i2.signatures.html.md#i2.signatures.Sig)([obj, name, \_\_validate_parameters_\_]) | A subclass of inspect.Signature that has a lot of extra api sugar, such as                                                                                             |
| [`SigPair`](_autosummary/i2.signatures.html.md#i2.signatures.SigPair)(sig1, sig2)                          | Compare two signatures: shared and missing names, and per-parameter differences.                                                                                       |

### Exceptions

| [`FuncCallNotMatchingSignature`](_autosummary/i2.signatures.html.md#i2.signatures.FuncCallNotMatchingSignature)                 | Raise when the call signature is not valid   |
|-----------------------------------------------------------------------------------------------|----------------------------------------------|
| [`IncompatibleSignatures`](_autosummary/i2.signatures.html.md#i2.signatures.IncompatibleSignatures)(\*args[, sig1, sig2]) |                                              |
| [`InvalidSignature`](_autosummary/i2.signatures.html.md#i2.signatures.InvalidSignature)                             | Raise when a signature is not valid          |

### *exception* i2.signatures.FuncCallNotMatchingSignature

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

Raise when the call signature is not valid

### *exception* i2.signatures.IncompatibleSignatures(\*args, sig1=None, sig2=None, \*\*kwargs)

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

#### pformat(indent=1, width=80, depth=None, , compact=False, sort_dicts=True, underscore_numbers=False)

Format a Python object into a pretty-printed representation.

### *exception* i2.signatures.InvalidSignature

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

Raise when a signature is not valid

### *class* i2.signatures.MissingArgValFor(argname)

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

A simple class to wrap an argument name, indicating that it was missing somewhere.

```pycon
>>> MissingArgValFor("argname")
MissingArgValFor("argname")
```

### i2.signatures.P

alias of [`Param`](_autosummary/i2.signatures.html.md#i2.signatures.Param)

### i2.signatures.PYTHON_DEFINED_CALLABLE_TYPES *= (<class 'function'>, <class 'method'>)*

Callable kinds that are defined in Python (as opposed to C-level builtins) and
therefore always carry authoritative signature information of their own.

### *class* i2.signatures.Param(name, kind=\_ParameterKind.POSITIONAL_OR_KEYWORD, , default, annotation)

Bases: [`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)

A thin wrap of Parameters: Adds shorter aliases to argument kinds and
a POSITIONAL_OR_KEYWORD default to the argument kind to make it faster to make
Parameter objects

```pycon
>>> list(map(Param, 'some quick arg params'.split()))
[<Param "some">, <Param "quick">, <Param "arg">, <Param "params">]
>>> from inspect import Signature
>>> P = Param
>>> Signature([P('x', P.PO), P('y', default=42, annotation=int), P('kw', P.KO)])
<Signature (x, /, y: int = 42, *, kw)>
```

### *class* i2.signatures.Sig(obj=None, , name=None, return_annotation, \_\_validate_parameters_\_=True)

Bases: [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature), [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)

A subclass of inspect.Signature that has a lot of extra api sugar,
such as

> - making a signature for a variety of input types (callable,
>   iterable of callables, parameter lists, strings, etc.)
> - has a dict-like interface
> - signature merging (with operator interfaces)
> - quick access to signature data
> - positional/keyword argument mapping.

**Positional/Keyword argument mapping**

In python, arguments can be positional (args) or keyword (kwargs).
… sometimes both, sometimes a single one is imposed.
… and you have variadic versions of both.
… and you can have defaults or not.
… and all these different kinds have a particular order they must be in.
It’s is mess really. The flexibility is nice – but still; a mess.

You only really feel the mess if you try to do some meta-programming with your
functions.
Then, methods like `normalize_kind` can help you out, since you can enforce, and
then assume, some stable interface to your functions.

Two of the base methods for dealing with positional (args) and keyword (kwargs)
inputs are:

> - `map_arguments`: Map some args/kwargs input to a keyword-only
>   expression of the inputs. This is useful if you need to do some processing
>   based on the argument names.
> - `mk_args_and_kwargs`: Translate a fully keyword expression of some
>   inputs into an (args, kwargs) pair that can be used to call the function.
>   (Remember, your function can have constraints, so you may need to do this.

The usual pattern of use of these methods is to use `map_arguments`
to map all the inputs to their corresponding name, do what needs to be done with
that (example, validation, transformation, decoration…) and then map back to an
(args, kwargs) pair than can actually be used to call the function.

Examples of methods and functions using these:
`call_forgivingly`, `tuple_the_args`, `map_arguments_from_variadics`, `extract_args_and_kwargs`,
`source_arguments`, and `source_args_and_kwargs`.

**Making a signature**

You can construct a `Sig` object from a callable,

```pycon
>>> def f(w, /, x: float = 1, y=1, *, z: int = 1):
...     ...
>>> Sig(f)
<Sig (w, /, x: float = 1, y=1, *, z: int = 1)>
```

but also from any “ParamsAble” object. Such as…
an iterable of Parameter instances, strings, tuples, or dicts:

```pycon
>>> Sig(
...     [
...         "a",
...         ("b", Parameter.empty, int),
...         ("c", 2),
...         ("d", 1.0, float),
...         dict(name="special", kind=Parameter.KEYWORD_ONLY, default=0),
...     ]
... )
<Sig (a, b: int, c=2, d: float = 1.0, *, special=0)>
>>>
>>> Sig(
...     [
...         "a",
...         "b",
...         dict(name="args", kind=Parameter.VAR_POSITIONAL),
...         dict(name="kwargs", kind=Parameter.VAR_KEYWORD),
...     ]
... )
<Sig (a, b, *args, **kwargs)>
```

The parameters of a signature are like a matrix whose rows are the parameters,
and the 4 columns are their properties: name, kind, default, and annotation
(the two laste ones being optional).
You get a row view when doing `Sig(...).parameters.values()`,
but what if you want a column-view?
Here’s how:

```pycon
>>> def f(w, /, x: float = 1, y=2, *, z: int = 3):
...     ...
>>>
>>> s = Sig(f)
>>> s.kinds
{'w': <_ParameterKind.POSITIONAL_ONLY: 0>,
'x': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'y': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'z': <_ParameterKind.KEYWORD_ONLY: 3>}
```

```pycon
>>> s.annotations
{'x': <class 'float'>, 'z': <class 'int'>}
>>> assert (
...     s.annotations == f.__annotations__
... )  # same as what you get in `__annotations__`
>>>
>>> s.defaults
{'x': 1, 'y': 2, 'z': 3}
>>> # Note that it's not the same as you get in __defaults__ though:
>>> assert (
...     s.defaults != f.__defaults__ == (1, 2)
... )  # not 3, since __kwdefaults__ has that!
```

We can sum (i.e. merge) and subtract (i.e. remove arguments) Sig instances.
Also, Sig instance is callable. It has the effect of inserting it’s signature in
the input
(in `__signature__`, but also inserting the resulting `__defaults__` and
`__kwdefaults__`).
One of the intents is to be able to do things like:

```pycon
>>> import inspect
>>> def f(w, /, x: float = 1, y=1, *, z: int = 1):
...     ...
>>> def g(i, w, /, j=2):
...     ...
...
>>>
>>> @Sig.from_objs(f, g, ["a", ("b", 3.14), ("c", 42, int)])
... def some_func(*args, **kwargs):
...     ...
>>> inspect.signature(some_func)
<Sig (w, i, /, a, x: float = 1, y=1, j=2, b=3.14, c: int = 42, *, z: int = 1)>
>>>
>>> sig = Sig(f) + g + ["a", ("b", 3.14), ("c", 42, int)] - "b" - ["a", "z"]
>>> @sig
... def some_func(*args, **kwargs):
...     ...
>>> inspect.signature(some_func)
<Sig (w, i, x: float = 1, y=1, j=2, c: int = 42)>
```

#### add_optional_keywords(kwarg_and_defaults=None, kwarg_annotations=None)

Add optional keyword arguments to a signature.

```pycon
>>> @Sig.add_optional_keywords({"c": 2, "d": 3}, {"c": int})
... def foo(a, *, b=1, **kwargs):
...     return f"{a=}, {b=}, {kwargs=}"
...
```

You can still call the function as before, and like before, any “extra” keyword
arguments will be passed to kwargs:

```pycon
>>> foo(0, d=10)
"a=0, b=1, kwargs={'d': 10}"
```

The difference is that now the signature of `foo` now has `c` and `d`:

```pycon
>>> str(Sig(foo))
'(a, *, c: int = 2, d=3, b=1, **kwargs)'
```

#### add_params(params)

Creates a new instance of Sig after merging the parameters of this signature
with a list of new parameters. The new list of parameters is automatically
sorted based on signature constraints given by kinds and default values.
See Python native signature documentation for more details.

```pycon
>>> s = Sig('(a, /, b, *, c)')
>>> s.add_params([
...     Param('kwargs', VK),
...     dict(name='d', kind=KO),
...     Param('args', VP),
...     'e',
...     Param('f', PO),
... ])
<Sig (a, f, /, b, e, *args, c, d, **kwargs)>
```

#### *property* annotations

annotation, …} dict of annotations of the signature.
What `func.__annotations__` would give you.

* **Type:**
  {arg_name

#### args_and_kwargs_from_kwargs(arguments, , apply_defaults=False, allow_partial=False, allow_excess=False, ignore_kind=False, args_limit=0)

Extract args and kwargs such that `func(*args, **kwargs)` can be called,
where func has instance’s signature.

* **Parameters:**
  * **arguments** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The {param_name: arg_val,…} dict to process
  * **args_limit** ([`int`](https://docs.python.org/3/builtins/functions.html#int) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – 

    How “far” in the params should args (positional arguments)
    be searched for.
    - args_limit==0: Take the minimum number possible of args (positional
      arguments). Only those that are position only or before a var-positional.
    - args_limit is None: Take the maximum number of args (positional arguments).
      The only kwargs (keyword arguments) you should have are keyword-only
      and var-keyword arguments.
    - args_limit positive integer: Take the args_limit first argument names
      (of signature) as args, and the rest as kwargs.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

```pycon
>>> def foo(w, /, x: float, y=1, *, z: int = 1):
...     return ((w + x) * y) ** z
>>> foo_sig = Sig(foo)
>>> args, kwargs = foo_sig.mk_args_and_kwargs(
...     dict(w=4, x=3, y=2, z=1)
... )
>>> assert (args, kwargs) == ((4,), {"x": 3, "y": 2, "z": 1})
>>> assert foo(*args, **kwargs) == foo(4, 3, 2, z=1) == 14
```

What about variadics?

```pycon
>>> def bar(a, /, b, *args, c=2, **kwargs):
...     pass
>>> Sig(bar).mk_args_and_kwargs(
...     dict(a=1, b=2, args=(3,4), c=5, kwargs=dict(d=6, e=7))
... )
((1, 2, 3, 4), {'c': 5, 'd': 6, 'e': 7})
```

You can also give the arguments in a different order:

```pycon
>>> Sig(bar).mk_args_and_kwargs(
...     dict(args=(3,4), kwargs=dict(d=6, e=7), b=2, c=5, a=1)
... )
((1, 2, 3, 4), {'c': 5, 'd': 6, 'e': 7})
```

The `args_limit` begs explanation.
Consider the signature of `def foo(w, /, x: float, y=1, *, z: int = 1): ...`
for instance. We could call the function with the following (args, kwargs) pairs:

- ((1,), {‘x’: 2, ‘y’: 3, ‘z’: 4})
- ((1, 2), {‘y’: 3, ‘z’: 4})
- ((1, 2, 3), {‘z’: 4})
  The two other combinations (empty args or empty kwargs) are not valid
  because of the / and \* constraints.

But when asked for an (args, kwargs) pair, which of the three valid options
should be returned? This is what the `args_limit` argument controls.

If `args_limit == 0`, the least args (positional arguments) will be returned.
It’s the default.

```pycon
>>> arguments = dict(w=4, x=3, y=2, z=1)
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=0)
((4,), {'x': 3, 'y': 2, 'z': 1})
```

If `args_limit is None`, the least kwargs (keyword arguments) will be returned.

```pycon
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=None)
((4, 3, 2), {'z': 1})
```

If `args_limit` is a positive integer, the first `[args_limit]` arguments
will be returned (not checking at all if this is valid!).

```pycon
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=1)
((4,), {'x': 3, 'y': 2, 'z': 1})
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=2)
((4, 3), {'y': 2, 'z': 1})
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=3)
((4, 3, 2), {'z': 1})
```

Note that if you specify `args_limit` to be greater than the maximum of
positional arguments, it behaves as if `args_limit` was `None`:

```pycon
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=4)
((4, 3, 2), {'z': 1})
```

Note that ‘args_limit’’s behavior is consistent with list behvior in the sense
that:

```pycon
>>> args = (0, 1, 2, 3)
>>> args[:0]
()
>>> args[:None]
(0, 1, 2, 3)
>>> args[2]
2
```

If variable positional arguments are present, `args_limit` is ignored and
all positional arguments are returned as args.

```pycon
>>> Sig(bar).mk_args_and_kwargs(
...     dict(a=1, b=2, args=(3,4), c=5, kwargs=dict(d=6, e=7)),
...     args_limit=1
... )
((1, 2, 3, 4), {'c': 5, 'd': 6, 'e': 7})
```

By default, only the arguments that were given in the `arguments` input will be
returned in the (args, kwargs) output.
If you also want to get those that have defaults (according to signature),
you need to specify it with the `apply_defaults=True` argument.

```pycon
>>> foo_sig.mk_args_and_kwargs(dict(w=4, x=3))
((4,), {'x': 3})
>>> foo_sig.mk_args_and_kwargs(dict(w=4, x=3), apply_defaults=True)
((4,), {'x': 3, 'y': 1, 'z': 1})
```

By default, all required arguments must be given.
Not doing so will lead to a `TypeError`.
If you want to process your arguments anyway, specify `allow_partial=True`.

```pycon
>>> foo_sig.mk_args_and_kwargs(dict(w=4))
Traceback (most recent call last):
  ...
TypeError: missing a required argument: 'x'
>>> foo_sig.mk_args_and_kwargs(dict(w=4), allow_partial=True)
((4,), {})
```

Specifying argument names that are not recognized by the signature will
lead to a `TypeError`.
If you want to avoid this (and just take from the input `kwargs` what ever you
can), specify this with `allow_excess=True`.

```pycon
>>> foo_sig.mk_args_and_kwargs(dict(w=4, x=3, extra='stuff'))
Traceback (most recent call last):
    ...
TypeError: Got unexpected keyword arguments: extra
>>> foo_sig.mk_args_and_kwargs(dict(w=4, x=3, extra='stuff'),
...     allow_excess=True)
((4,), {'x': 3})
```

See `map_arguments` (namely for the description of the arguments).

#### ch_annotations(\*\*changes_for_name)

Change parameter annotations, given as `name=annotation` pairs (see `ch_param_attrs`).

#### ch_defaults(\_allow_reordering=True, \*\*changes_for_name)

Change parameter defaults, given as `name=default` pairs (see `ch_param_attrs`).

#### ch_kinds(\_allow_reordering=True, \*\*changes_for_name)

Change parameter kinds, given as `name=kind` pairs (see `ch_param_attrs`).

#### ch_kinds_to_position_or_keyword()

Return the signature with all non-variadic kinds set to POSITIONAL_OR_KEYWORD.

#### ch_names(\*\*changes_for_name)

Rename parameters, given as `old_name=new_name` pairs.

```pycon
>>> Sig(lambda a, b: None).ch_names(a="x")
<Sig (x, b)>
```

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If a name to change is not in the signature.

#### ch_param_attrs(param_attr, \*arg_new_vals, \_allow_reordering=False, \*\*kwargs_new_vals)

Change a specific attribute of the params, returning a modified signature.
This is a convenience method for the modified method when we’re targetting
a fixed param attribute: ‘name’, ‘kind’, ‘default’, or ‘annotation’

Instead of having to do this

```pycon
>>> def foo(a, *b, **c): ...
>>> Sig(foo).modified(a={'name': 'A'}, b={'name': 'B'}, c={'name': 'C'})
<Sig (A, *B, **C)>
```

We can simply do this

```pycon
>>> Sig(foo).ch_param_attrs('name', a='A', b='B', c='C')
<Sig (A, *B, **C)>
```

One quite useful thing you can do with this is to set defaults, or set defaults
where there are none. If you wrap your function with such a modified signature,
you get a “curried” version of your function (called “partial” in python).
(Note that the `functools.wraps` won’t deal with defaults “correctly”, but
wrapping with `Sig` objects takes care of that oversight!)

```pycon
>>> def foo(a, b, c):
...     return a + b * c
>>> special_foo = Sig(foo).ch_param_attrs('default', b=2, c=3)(foo)
>>> Sig(special_foo)
<Sig (a, b=2, c=3)>
>>> special_foo(5)  # should be 5 + 2 * 3 == 11
11
```

#### *property* defaults

A `{name: default,...}` dict of defaults (regardless of kind)

#### detail_names_by_kind()

Names grouped by kind: `(po_names, pk_names, vp_name, ko_names, vk_name)`, the
variadic ones as a single name or `None`.

```pycon
>>> Sig(lambda a, /, b, *args, c=1, **kw: None).detail_names_by_kind()
(('a',), ('b',), 'args', ('c',), 'kw')
```

#### extract_args_and_kwargs(\*args, \_ignore_kind=True, \_allow_partial=False, \_allow_excess=True, \_apply_defaults=False, \_args_limit=0, \*\*kwargs)

Source the (args, kwargs) for the signature instance, ignoring excess
arguments.

```pycon
>>> def foo(w, /, x: float, y=2, *, z: int = 1):
...     return w + x * y ** z
>>> args, kwargs = Sig(foo).extract_args_and_kwargs(4, x=3, y=2)
>>> (args, kwargs) == ((4,), {"x": 3, "y": 2})
True
```

The difference with map_arguments_from_variadics is that here the output is
ready to be called by the function whose signature we have, since the
position-only arguments will be returned as args.

```pycon
>>> foo(*args, **kwargs)
10
```

Note that though `w` is a position only argument, you can specify `w=4` as a
keyword argument too (by default):

```pycon
>>> args, kwargs = Sig(foo).extract_args_and_kwargs(w=4, x=3, y=2)
>>> (args, kwargs) == ((4,), {"x": 3, "y": 2})
True
```

If you don’t want to allow that, you can say `_ignore_kind=False`

```pycon
>>> Sig(foo).extract_args_and_kwargs(w=4, x=3, y=2, _ignore_kind=False)
Traceback (most recent call last):
  ...
TypeError:...'w'...
```

You can use `_allow_partial` that will allow you, if
set to `True`, to underspecify the params of a function (in view of being
completed later).

```pycon
>>> Sig(foo).extract_args_and_kwargs(x=3, y=2)
Traceback (most recent call last):
  ...
TypeError:...'w'...
```

But if you specify `_allow_partial=True`…

```pycon
>>> args, kwargs = Sig(foo).extract_args_and_kwargs(
...     x=3, y=2, _allow_partial=True
... )
>>> (args, kwargs) == ((), {"x": 3, "y": 2})
True
```

By default, `_apply_defaults=False`, which will lead to only get those
arguments you input.

```pycon
>>> args, kwargs = Sig(foo).extract_args_and_kwargs(4, x=3, y=2)
>>> (args, kwargs) == ((4,), {"x": 3, "y": 2})
True
```

But if you specify `_apply_defaults=True` non-specified non-require arguments
will be returned with their defaults:

```pycon
>>> args, kwargs = Sig(foo).extract_args_and_kwargs(
...     4, x=3, y=2, _apply_defaults=True
... )
>>> (args, kwargs) == ((4,), {"x": 3, "y": 2, "z": 1})
True
```

#### extract_kwargs(\*args, \_apply_defaults=False, \_allow_partial=False, \_allow_excess=False, \_ignore_kind=False, \*\*kwargs)

Convenience method that calls map_arguments from variadics

```pycon
>>> def foo(w, /, x: float, y="YY", *, z: str = "ZZ"):
...     ...
>>> sig = Sig(foo)
>>> assert (
...     sig.map_arguments_from_variadics(1, 2, 3, z=4)
...     == sig.map_arguments_from_variadics(1, 2, y=3, z=4)
...     == {"w": 1, "x": 2, "y": 3, "z": 4}
... )
```

What about var positional and var keywords?

```pycon
>>> def bar(*args, **kwargs):
...     ...
...
>>> Sig(bar).map_arguments_from_variadics(1, 2, y=3, z=4)
{'args': (1, 2), 'kwargs': {'y': 3, 'z': 4}}
```

Note that though `w` is a position only argument, you can specify `w=11` as
a keyword argument too, using `_ignore_kind=True`:

```pycon
>>> Sig(foo).map_arguments_from_variadics(w=11, x=22, _ignore_kind=True)
{'w': 11, 'x': 22}
```

You can use `_allow_partial` that will allow you, if
set to `True`, to underspecify the params of a function
(in view of being completed later).

```pycon
>>> Sig(foo).map_arguments_from_variadics(x=3, y=2)
Traceback (most recent call last):
  ...
TypeError: missing a required argument: 'w'
```

But if you specify `_allow_partial=True`…

```pycon
>>> Sig(foo).map_arguments_from_variadics(x=3, y=2, _allow_partial=True)
{'x': 3, 'y': 2}
```

By default, `_apply_defaults=False`, which will lead to only get those arguments
you input.

```pycon
>>> Sig(foo).map_arguments_from_variadics(4, x=3, y=2)
{'w': 4, 'x': 3, 'y': 2}
```

But if you specify `_apply_defaults=True` non-specified non-require arguments
will be returned with their defaults:

```pycon
>>> Sig(foo).map_arguments_from_variadics(4, x=3, y=2, _apply_defaults=True)
{'w': 4, 'x': 3, 'y': 2, 'z': 'ZZ'}
```

#### *classmethod* from_objs(\*objs, default_conflict_method='strict', return_annotation, \*\*name_and_dflts)

Merge signatures of several objects into one; `name=default` kwargs add PK params.

```pycon
>>> Sig.from_objs(lambda a: None, ["b"], c=2)
<Sig (a, b, c=2)>
```

#### *classmethod* from_params(params)

Make a `Sig` from a `Parameter` or an iterable of parameter specs.

#### get_names(spec, , conserve_sig_order=True, allow_excess=False)

Return a tuple of names corresponding to the given spec.

* **Parameters:**
  * **spec** – An integer, string, or iterable of intergers and strings
  * **conserve_sig_order** – Whether to order according to the signature
  * **allow_excess** – Whether to allow items in spec that are not in signature

```pycon
>>> sig = Sig('a b c d e')
>>> sig.get_names(0)
('a',)
>>> sig.get_names([0, 2])
('a', 'c')
>>> sig.get_names('b')
('b',)
>>> sig.get_names([0, 'c', -1])
('a', 'c', 'e')
```

See that by default the order of the signature is conserved:

```pycon
>>> sig.get_names('b e d')
('b', 'd', 'e')
```

But you can change that default to conserve the order of the `spec` instead:

```pycon
>>> sig.get_names('b e d', conserve_sig_order=False)
('b', 'e', 'd')
```

By default, you can’t mention names that are not in signature.
To allow this (making `spec` have “extract these” interpretation),
set `allow_excess=True`:

```pycon
>>> sig.get_names(['a', 'c', 'e', 'g', 'h'], allow_excess=True)
('a', 'c', 'e')
```

#### *property* has_var_keyword

Use index_of_var_keyword or var_keyword_name directly when needing that
information as well. This will avoid having to check the kinds list twice.

#### *property* has_var_kinds

Whether the signature has a VAR_POSITIONAL or VAR_KEYWORD parameter.

```pycon
>>> Sig(lambda x, *, y: None).has_var_kinds
False
>>> Sig(lambda x, *y: None).has_var_kinds
True
>>> Sig(lambda x, **y: None).has_var_kinds
True
```

#### *property* has_var_positional

Use index_of_var_positional or var_keyword_name directly when needing that
information as well. This will avoid having to check the kinds list twice.

#### *property* index_of_var_keyword

The index of a VAR_KEYWORD param kind if any, and None if not.
See also, Sig.index_of_var_positional

```pycon
>>> assert Sig(lambda **kwargs: 0).index_of_var_keyword == 0
>>> assert Sig(lambda a, **kwargs: 0).index_of_var_keyword == 1
>>> assert Sig(lambda a, *args, **kwargs: 0).index_of_var_keyword == 2
```

And if there’s none…

```pycon
>>> assert Sig(lambda a, *args, b=1: 0).index_of_var_keyword is None
```

#### *property* index_of_var_positional

The index of the VAR_POSITIONAL param kind if any, and None if not.
See also, Sig.index_of_var_keyword

```pycon
>>> assert Sig(lambda x, *y, z: 0).index_of_var_positional == 1
>>> assert Sig(lambda x, /, y, **z: 0).index_of_var_positional == None
```

#### *property* inject_into_keyword_variadic

Decorator that uses signature to source the keyword variadic of target function.

See replace_kwargs_using function for more details, including examples.

```pycon
>>> def apple(a, x: int, y=2, *, z=3, **extra_apple_options):
...     return a + x + y + z
>>> @Sig(apple).inject_into_keyword_variadic
... def sauce(a, b, c, **sauce_kwargs):
...     return b * c + apple(a, **sauce_kwargs)
```

The function will works:

```pycon
>>> sauce(1, 2, 3, x=4, z=5)  # func still works? Should be: 1 + 4 + 2 + 5 + 2 * 3
18
```

But the signature now doesn’t have the `**sauce_kwargs`, but more informative
signature elements sourced from `apple`:

```pycon
>>> Sig(sauce)
<Sig (a, b, c, *, x: int, y=2, z=3, **extra_apple_options)>
```

#### is_call_compatible_with(other_sig, , param_comparator=None)

Return True if the signature is compatible with `other_sig`. Meaning that
all valid ways to call the signature are valid for `other_sig`.

#### *property* keyword_names

Names of the parameters that can be given by keyword (PK and KO kinds).

#### *property* kinds

A `{name: kind}` dict of the parameters.

#### kwargs_from_args_and_kwargs(args=None, kwargs=None, , apply_defaults=False, allow_partial=False, allow_excess=False, ignore_kind=False)

Map arguments (args and kwargs) to the parameters of function’s signature.

When you need to manage how the arguments of a function are specified,
you need to take care of
multiple cases depending on whether they were specified as positional arguments
(`args`) or keyword arguments (`kwargs`).

The `map_arguments` (and it’s sorta-inverse inverse,
`mk_args_and_kwargs`)
are there to help you manage this.

If you could rely on the the fact that only `kwargs` were given it would
reduce the complexity of your code.
This is why we have the `all_pk_signature` function in `signatures.py`.

We also need to have a means to make a `kwargs` only from the actual `(*args,
**kwargs)` used at runtime.
We have `Signature.bind` (and `bind_partial`) for that.

But these methods will fail if there is extra stuff in the `kwargs`.
Yet sometimes we’d like to have a `dict` that services several functions that
will extract their needs from it.

That’s where  `Sig.map_arguments_from_variadics(*args, **kwargs)` is needed.

* **Parameters:**
  * **args** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)) – The args the function will be called with.
  * **kwargs** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The kwargs the function will be called with.
  * **apply_defaults** – (bool) Whether to apply signature defaults to the
    non-specified argument names
  * **allow_partial** – (bool) True iff you want to allow partial signature
    fulfillment.
  * **allow_excess** – (bool) Set to True iff you want to allow extra kwargs
    items to be ignored.
  * **ignore_kind** – (bool) Set to True iff you want to ignore the position and
    keyword only kinds,
    in order to be able to accept args and kwargs in such a way that there can
    be cross-over
    (args that are supposed to be keyword only, and kwargs that are supposed
    to be positional only)
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  An {param_name: arg_val, …} dict

See also the sorta-inverse of this function: mk_args_and_kwargs

```pycon
>>> def foo(w, /, x: float, y="YY", *, z: str = "ZZ"):
...     ...
>>> sig = Sig(foo)
>>> assert (
...     sig.map_arguments((11, 22, "you"), dict(z="zoo"))
...     == sig.map_arguments((11, 22), dict(y="you", z="zoo"))
...     == {"w": 11, "x": 22, "y": "you", "z": "zoo"}
... )
```

By default, `apply_defaults=False`, which will lead to only get those
arguments you input.

```pycon
>>> sig.map_arguments(args=(11,), kwargs={"x": 22})
{'w': 11, 'x': 22}
```

But if you specify `apply_defaults=True` non-specified non-require arguments
will be returned with their defaults:

```pycon
>>> sig.map_arguments(
...     args=(11,), kwargs={"x": 22}, apply_defaults=True
... )
{'w': 11, 'x': 22, 'y': 'YY', 'z': 'ZZ'}
```

By default, `ignore_excess=False`, so specifying kwargs that are not in the
signature will lead to an exception.

```pycon
>>> sig.map_arguments(
...     args=(11,), kwargs={"x": 22, "not_in_sig": -1}
... )
Traceback (most recent call last):
    ...
TypeError: got an unexpected keyword argument 'not_in_sig'
```

Specifying `allow_excess=True` will ignore such excess fields of kwargs.
This is useful when you want to source several functions from a same dict.

```pycon
>>> sig.map_arguments(
...     args=(11,), kwargs={"x": 22, "not_in_sig": -1}, allow_excess=True
... )
{'w': 11, 'x': 22}
```

On the other side of `ignore_excess` you have `allow_partial` that will allow
you, if
set to `True`, to underspecify the params of a function (in view of being
completed later).

```pycon
>>> sig.map_arguments(args=(), kwargs={"x": 22})
Traceback (most recent call last):
...
TypeError: missing a required argument: 'w'
```

But if you specify `allow_partial=True`…

```pycon
>>> sig.map_arguments(
...     args=(), kwargs={"x": 22}, allow_partial=True
... )
{'x': 22}
```

That’s a lot of control (eight combinations total), but not everything is
controllable here:
Position only and keyword only kinds need to be respected:

```pycon
>>> sig.map_arguments(args=(1, 2, 3, 4), kwargs={})
Traceback (most recent call last):
...
TypeError: too many positional arguments
>>> sig.map_arguments(args=(), kwargs=dict(w=1, x=2, y=3, z=4))
Traceback (most recent call last):
...
TypeError:...'w'...
```

But if you want to ignore the kind of parameter, just say so:

```pycon
>>> sig.map_arguments(
...     args=(1, 2, 3, 4), kwargs={}, ignore_kind=True
... )
{'w': 1, 'x': 2, 'y': 3, 'z': 4}
>>> sig.map_arguments(
...     args=(), kwargs=dict(w=1, x=2, y=3, z=4), ignore_kind=True
... )
{'w': 1, 'x': 2, 'y': 3, 'z': 4}
```

#### map_arguments(args=None, kwargs=None, , apply_defaults=False, allow_partial=False, allow_excess=False, ignore_kind=False)

Map arguments (args and kwargs) to the parameters of function’s signature.

When you need to manage how the arguments of a function are specified,
you need to take care of
multiple cases depending on whether they were specified as positional arguments
(`args`) or keyword arguments (`kwargs`).

The `map_arguments` (and it’s sorta-inverse inverse,
`mk_args_and_kwargs`)
are there to help you manage this.

If you could rely on the the fact that only `kwargs` were given it would
reduce the complexity of your code.
This is why we have the `all_pk_signature` function in `signatures.py`.

We also need to have a means to make a `kwargs` only from the actual `(*args,
**kwargs)` used at runtime.
We have `Signature.bind` (and `bind_partial`) for that.

But these methods will fail if there is extra stuff in the `kwargs`.
Yet sometimes we’d like to have a `dict` that services several functions that
will extract their needs from it.

That’s where  `Sig.map_arguments_from_variadics(*args, **kwargs)` is needed.

* **Parameters:**
  * **args** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)) – The args the function will be called with.
  * **kwargs** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The kwargs the function will be called with.
  * **apply_defaults** – (bool) Whether to apply signature defaults to the
    non-specified argument names
  * **allow_partial** – (bool) True iff you want to allow partial signature
    fulfillment.
  * **allow_excess** – (bool) Set to True iff you want to allow extra kwargs
    items to be ignored.
  * **ignore_kind** – (bool) Set to True iff you want to ignore the position and
    keyword only kinds,
    in order to be able to accept args and kwargs in such a way that there can
    be cross-over
    (args that are supposed to be keyword only, and kwargs that are supposed
    to be positional only)
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  An {param_name: arg_val, …} dict

See also the sorta-inverse of this function: mk_args_and_kwargs

```pycon
>>> def foo(w, /, x: float, y="YY", *, z: str = "ZZ"):
...     ...
>>> sig = Sig(foo)
>>> assert (
...     sig.map_arguments((11, 22, "you"), dict(z="zoo"))
...     == sig.map_arguments((11, 22), dict(y="you", z="zoo"))
...     == {"w": 11, "x": 22, "y": "you", "z": "zoo"}
... )
```

By default, `apply_defaults=False`, which will lead to only get those
arguments you input.

```pycon
>>> sig.map_arguments(args=(11,), kwargs={"x": 22})
{'w': 11, 'x': 22}
```

But if you specify `apply_defaults=True` non-specified non-require arguments
will be returned with their defaults:

```pycon
>>> sig.map_arguments(
...     args=(11,), kwargs={"x": 22}, apply_defaults=True
... )
{'w': 11, 'x': 22, 'y': 'YY', 'z': 'ZZ'}
```

By default, `ignore_excess=False`, so specifying kwargs that are not in the
signature will lead to an exception.

```pycon
>>> sig.map_arguments(
...     args=(11,), kwargs={"x": 22, "not_in_sig": -1}
... )
Traceback (most recent call last):
    ...
TypeError: got an unexpected keyword argument 'not_in_sig'
```

Specifying `allow_excess=True` will ignore such excess fields of kwargs.
This is useful when you want to source several functions from a same dict.

```pycon
>>> sig.map_arguments(
...     args=(11,), kwargs={"x": 22, "not_in_sig": -1}, allow_excess=True
... )
{'w': 11, 'x': 22}
```

On the other side of `ignore_excess` you have `allow_partial` that will allow
you, if
set to `True`, to underspecify the params of a function (in view of being
completed later).

```pycon
>>> sig.map_arguments(args=(), kwargs={"x": 22})
Traceback (most recent call last):
...
TypeError: missing a required argument: 'w'
```

But if you specify `allow_partial=True`…

```pycon
>>> sig.map_arguments(
...     args=(), kwargs={"x": 22}, allow_partial=True
... )
{'x': 22}
```

That’s a lot of control (eight combinations total), but not everything is
controllable here:
Position only and keyword only kinds need to be respected:

```pycon
>>> sig.map_arguments(args=(1, 2, 3, 4), kwargs={})
Traceback (most recent call last):
...
TypeError: too many positional arguments
>>> sig.map_arguments(args=(), kwargs=dict(w=1, x=2, y=3, z=4))
Traceback (most recent call last):
...
TypeError:...'w'...
```

But if you want to ignore the kind of parameter, just say so:

```pycon
>>> sig.map_arguments(
...     args=(1, 2, 3, 4), kwargs={}, ignore_kind=True
... )
{'w': 1, 'x': 2, 'y': 3, 'z': 4}
>>> sig.map_arguments(
...     args=(), kwargs=dict(w=1, x=2, y=3, z=4), ignore_kind=True
... )
{'w': 1, 'x': 2, 'y': 3, 'z': 4}
```

#### map_arguments_from_variadics(\*args, \_apply_defaults=False, \_allow_partial=False, \_allow_excess=False, \_ignore_kind=False, \*\*kwargs)

Convenience method that calls map_arguments from variadics

```pycon
>>> def foo(w, /, x: float, y="YY", *, z: str = "ZZ"):
...     ...
>>> sig = Sig(foo)
>>> assert (
...     sig.map_arguments_from_variadics(1, 2, 3, z=4)
...     == sig.map_arguments_from_variadics(1, 2, y=3, z=4)
...     == {"w": 1, "x": 2, "y": 3, "z": 4}
... )
```

What about var positional and var keywords?

```pycon
>>> def bar(*args, **kwargs):
...     ...
...
>>> Sig(bar).map_arguments_from_variadics(1, 2, y=3, z=4)
{'args': (1, 2), 'kwargs': {'y': 3, 'z': 4}}
```

Note that though `w` is a position only argument, you can specify `w=11` as
a keyword argument too, using `_ignore_kind=True`:

```pycon
>>> Sig(foo).map_arguments_from_variadics(w=11, x=22, _ignore_kind=True)
{'w': 11, 'x': 22}
```

You can use `_allow_partial` that will allow you, if
set to `True`, to underspecify the params of a function
(in view of being completed later).

```pycon
>>> Sig(foo).map_arguments_from_variadics(x=3, y=2)
Traceback (most recent call last):
  ...
TypeError: missing a required argument: 'w'
```

But if you specify `_allow_partial=True`…

```pycon
>>> Sig(foo).map_arguments_from_variadics(x=3, y=2, _allow_partial=True)
{'x': 3, 'y': 2}
```

By default, `_apply_defaults=False`, which will lead to only get those arguments
you input.

```pycon
>>> Sig(foo).map_arguments_from_variadics(4, x=3, y=2)
{'w': 4, 'x': 3, 'y': 2}
```

But if you specify `_apply_defaults=True` non-specified non-require arguments
will be returned with their defaults:

```pycon
>>> Sig(foo).map_arguments_from_variadics(4, x=3, y=2, _apply_defaults=True)
{'w': 4, 'x': 3, 'y': 2, 'z': 'ZZ'}
```

#### merge_with_sig(sig, ch_to_all_pk=False, , default_conflict_method='strict')

Return a signature obtained by merging self signature with another signature.
Insofar as it can, given the kind precedence rules, the arguments of self will
appear first.

* **Parameters:**
  * **sig** (`Union`[[`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature), [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The signature to merge with.
  * **ch_to_all_pk** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether to change all kinds of both signatures to PK (
    POSITIONAL_OR_KEYWORD)
* **Returns:**

```pycon
>>> def func(a=None, *, b=1, c=2):
...     ...
...
>>>
>>> s = Sig(func)
>>> s
<Sig (a=None, *, b=1, c=2)>
```

Observe where the new arguments `d` and `e` are placed,
according to whether they have defaults and what their kind is:

```pycon
>>> s.merge_with_sig(["d", "e"])
<Sig (d, e, a=None, *, b=1, c=2)>
>>> s.merge_with_sig(["d", ("e", 4)])
<Sig (d, a=None, e=4, *, b=1, c=2)>
>>> s.merge_with_sig(["d", dict(name="e", kind=KO, default=4)])
<Sig (d, a=None, *, b=1, c=2, e=4)>
>>> s.merge_with_sig(
...     [dict(name="d", kind=KO), dict(name="e", kind=KO, default=4)]
... )
<Sig (a=None, *, d, b=1, c=2, e=4)>
```

If the kind of the params is not important, but order is, you can specify
`ch_to_all_pk=True`:

```pycon
>>> s.merge_with_sig(["d", "e"], ch_to_all_pk=True)
<Sig (d, e, a=None, b=1, c=2)>
>>> s.merge_with_sig([("d", 3), ("e", 4)], ch_to_all_pk=True)
<Sig (a=None, b=1, c=2, d=3, e=4)>
```

#### mk_args_and_kwargs(arguments, , apply_defaults=False, allow_partial=False, allow_excess=False, ignore_kind=False, args_limit=0)

Extract args and kwargs such that `func(*args, **kwargs)` can be called,
where func has instance’s signature.

* **Parameters:**
  * **arguments** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The {param_name: arg_val,…} dict to process
  * **args_limit** ([`int`](https://docs.python.org/3/builtins/functions.html#int) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – 

    How “far” in the params should args (positional arguments)
    be searched for.
    - args_limit==0: Take the minimum number possible of args (positional
      arguments). Only those that are position only or before a var-positional.
    - args_limit is None: Take the maximum number of args (positional arguments).
      The only kwargs (keyword arguments) you should have are keyword-only
      and var-keyword arguments.
    - args_limit positive integer: Take the args_limit first argument names
      (of signature) as args, and the rest as kwargs.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

```pycon
>>> def foo(w, /, x: float, y=1, *, z: int = 1):
...     return ((w + x) * y) ** z
>>> foo_sig = Sig(foo)
>>> args, kwargs = foo_sig.mk_args_and_kwargs(
...     dict(w=4, x=3, y=2, z=1)
... )
>>> assert (args, kwargs) == ((4,), {"x": 3, "y": 2, "z": 1})
>>> assert foo(*args, **kwargs) == foo(4, 3, 2, z=1) == 14
```

What about variadics?

```pycon
>>> def bar(a, /, b, *args, c=2, **kwargs):
...     pass
>>> Sig(bar).mk_args_and_kwargs(
...     dict(a=1, b=2, args=(3,4), c=5, kwargs=dict(d=6, e=7))
... )
((1, 2, 3, 4), {'c': 5, 'd': 6, 'e': 7})
```

You can also give the arguments in a different order:

```pycon
>>> Sig(bar).mk_args_and_kwargs(
...     dict(args=(3,4), kwargs=dict(d=6, e=7), b=2, c=5, a=1)
... )
((1, 2, 3, 4), {'c': 5, 'd': 6, 'e': 7})
```

The `args_limit` begs explanation.
Consider the signature of `def foo(w, /, x: float, y=1, *, z: int = 1): ...`
for instance. We could call the function with the following (args, kwargs) pairs:

- ((1,), {‘x’: 2, ‘y’: 3, ‘z’: 4})
- ((1, 2), {‘y’: 3, ‘z’: 4})
- ((1, 2, 3), {‘z’: 4})
  The two other combinations (empty args or empty kwargs) are not valid
  because of the / and \* constraints.

But when asked for an (args, kwargs) pair, which of the three valid options
should be returned? This is what the `args_limit` argument controls.

If `args_limit == 0`, the least args (positional arguments) will be returned.
It’s the default.

```pycon
>>> arguments = dict(w=4, x=3, y=2, z=1)
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=0)
((4,), {'x': 3, 'y': 2, 'z': 1})
```

If `args_limit is None`, the least kwargs (keyword arguments) will be returned.

```pycon
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=None)
((4, 3, 2), {'z': 1})
```

If `args_limit` is a positive integer, the first `[args_limit]` arguments
will be returned (not checking at all if this is valid!).

```pycon
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=1)
((4,), {'x': 3, 'y': 2, 'z': 1})
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=2)
((4, 3), {'y': 2, 'z': 1})
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=3)
((4, 3, 2), {'z': 1})
```

Note that if you specify `args_limit` to be greater than the maximum of
positional arguments, it behaves as if `args_limit` was `None`:

```pycon
>>> foo_sig.mk_args_and_kwargs(arguments, args_limit=4)
((4, 3, 2), {'z': 1})
```

Note that ‘args_limit’’s behavior is consistent with list behvior in the sense
that:

```pycon
>>> args = (0, 1, 2, 3)
>>> args[:0]
()
>>> args[:None]
(0, 1, 2, 3)
>>> args[2]
2
```

If variable positional arguments are present, `args_limit` is ignored and
all positional arguments are returned as args.

```pycon
>>> Sig(bar).mk_args_and_kwargs(
...     dict(a=1, b=2, args=(3,4), c=5, kwargs=dict(d=6, e=7)),
...     args_limit=1
... )
((1, 2, 3, 4), {'c': 5, 'd': 6, 'e': 7})
```

By default, only the arguments that were given in the `arguments` input will be
returned in the (args, kwargs) output.
If you also want to get those that have defaults (according to signature),
you need to specify it with the `apply_defaults=True` argument.

```pycon
>>> foo_sig.mk_args_and_kwargs(dict(w=4, x=3))
((4,), {'x': 3})
>>> foo_sig.mk_args_and_kwargs(dict(w=4, x=3), apply_defaults=True)
((4,), {'x': 3, 'y': 1, 'z': 1})
```

By default, all required arguments must be given.
Not doing so will lead to a `TypeError`.
If you want to process your arguments anyway, specify `allow_partial=True`.

```pycon
>>> foo_sig.mk_args_and_kwargs(dict(w=4))
Traceback (most recent call last):
  ...
TypeError: missing a required argument: 'x'
>>> foo_sig.mk_args_and_kwargs(dict(w=4), allow_partial=True)
((4,), {})
```

Specifying argument names that are not recognized by the signature will
lead to a `TypeError`.
If you want to avoid this (and just take from the input `kwargs` what ever you
can), specify this with `allow_excess=True`.

```pycon
>>> foo_sig.mk_args_and_kwargs(dict(w=4, x=3, extra='stuff'))
Traceback (most recent call last):
    ...
TypeError: Got unexpected keyword arguments: extra
>>> foo_sig.mk_args_and_kwargs(dict(w=4, x=3, extra='stuff'),
...     allow_excess=True)
((4,), {'x': 3})
```

See `map_arguments` (namely for the description of the arguments).

#### modified(\_allow_reordering=False, \*\*changes_for_name)

Returns a modified (new) signature object.

#### NOTE
This function doesn’t modify the signature, but creates a modified copy
of the signature.

IMPORTANT WARNING: This is an advanced feature. Avoid wrapping a function with
a modified signature, as this may not have the intended effect.

```pycon
>>> def foo(pka, *vpa, koa, **vka): ...
>>> sig = Sig(foo)
>>> sig
<Sig (pka, *vpa, koa, **vka)>
>>> assert sig.kinds['pka'] == PK
```

Let’s make a signature that is the same as sig, except that

> - `poa` is given a PO (POSITIONAL_ONLY) kind insteadk of PK
> - `koa` is given a default of None
> - the signature is given a return_annotation of str
```pycon
>>> new_sig = sig.modified(
...     pka={'kind': PO},
...     koa={'default': None},
...     return_annotation=str
... )
>>> new_sig
<Sig (pka, /, *vpa, koa=None, **vka) -> str>
>>> assert new_sig.kinds['pka'] == PO  # now pos is of the PO kind!
```

Here’s an example of changing signature parameters in bulk.
Here we change all kinds to be the friendly PK kind.

```pycon
>>> sig.modified(**{name: {'kind': PK} for name in sig.names})
<Sig (pka, vpa, koa, vka)>
```

Repetition of the above: This gives you a signature with all PK kinds.
If you wrap a function with it, it will look like it has all PK kinds.
But that doesn’t mean you can actually use thenm as such.
You’ll need to modify (decorate further) your function further to reflect
its new signature.

On the other hand, if you decorate a function with a sig that adds or modifies
defaults, these defaults will actually be used (unlike with `functools.wraps`).

#### *property* n_required

The number of required arguments.
A required argument is one that doesn’t have a default, nor is VAR_POSITIONAL
(`*args`) or VAR_KEYWORD (`**kwargs`).

#### NOTE
Sometimes a minimum number of arguments in VAR_POSITIONAL and
VAR_KEYWORD are in fact required,
but we can’t see this from the signature, so we can’t tell you about that! You
do the math.

```pycon
>>> f = lambda a00, /, a11, a12, *a23, a34, a35=1, a36='two', **a47: None
>>> Sig(f).n_required
4
```

#### *property* names

The parameter names, in signature order.

#### names_for_kind(kind)

Get the arg names tuple for a given kind.
Note, if you need to do this several times, or for several kinds, use
`names_of_kind` property (a tuple) instead: It groups all names of kinds once,
and caches the result.

#### normalize_kind(kind=\_ParameterKind.POSITIONAL_OR_KEYWORD, except_kinds=frozenset({_ParameterKind.VAR_POSITIONAL, \_ParameterKind.VAR_KEYWORD}), add_defaults_if_necessary=False, argname_to_default=None, allow_reordering=False)

A new `Sig` with every parameter set to `kind` (POSITIONAL_OR_KEYWORD by
default), except those whose kind is in `except_kinds` (the variadic kinds by
default).

```pycon
>>> Sig(lambda a, /, b, *, c: None).normalize_kind()
<Sig (a, b, c)>
```

#### pair_with(other_sig)

Get an object that pairs with another signature for comparison, merging, etc.

See `SigPair` for more details.

* **Return type:**
  [`SigPair`](_autosummary/i2.signatures.html.md#i2.signatures.SigPair)

#### *property* params

Just list(self.parameters.values()), because that’s often what we want.
Why a Sig.params property when we already have a Sig.parameters property?

Well, as much as is boggles my mind, it so happens that the Signature.parameters
is a name->Parameter mapping, but the Signature argument `parameters`,
though baring the same name,
is expected to be a list of Parameter instances.

So Sig.params is there to restore semantic consistence sanity.

#### *property* positional_names

Names of the parameters that can be given positionally (PO and PK kinds).

#### remove_names(names)

A new `Sig` without the given parameter names.

```pycon
>>> Sig(lambda a, /, b, *args, c=1, **kw: None).remove_names(["b", "c"])
<Sig (a, /, *args, **kw)>
```

#### replace_kwargs_using()

Decorator that replaces the variadic keyword argument of the target function using
the `sig`, the signature of a source function.
It essentially injects the difference between `sig` and the target function’s
signature into the target function’s signature. That is, it replaces the
variadic keyword argument (a.k.a. “kwargs”) with those parameters that are in `sig`
but not in the target function’s signature.

This is meant to be used when a `targ_func` (the function you’ll apply the
decorator to) has a variadict keyword argument that is just used to forward “extra”
arguments to another function, and you want to make sure that the signature of the
`targ_func` is consistent with the `sig` signature.
(Also, you don’t want to copy the signatures around manually.)

In the following, `sauce` (the target function) has a variadic keyword argument,
`sauce_kwargs`, that is used to forward extra arguments to `apple` (the source
function).

```pycon
>>> def apple(a, x: int, y=2, *, z=3, **extra_apple_options):
...     return a + x + y + z
>>> @replace_kwargs_using(apple)
... def sauce(a, b, c, **sauce_kwargs):
...     return b * c + apple(a, **sauce_kwargs)
```

The function will works:

```pycon
>>> sauce(1, 2, 3, x=4, z=5)  # func still works? Should be: 1 + 4 + 2 + 5 + 2 * 3
18
```

But the signature now doesn’t have the `**sauce_kwargs`, but more informative
signature elements sourced from `apple`:

```pycon
>>> Sig(sauce)
<Sig (a, b, c, *, x: int, y=2, z=3, **extra_apple_options)>
```

One thing to note is that the order of the arguments in the signature of `apple`
may change to accomodate for the python parameter order rules
(see [https://docs.python.org/3/reference/compound_stmts.html#function-definitions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)).
The new order will try to conserve the order of the original arguments of `sauce`
in-so-far as it doesn’t violate the python parameter order rules, though.
See examples below:

```pycon
>>> @Sig.replace_kwargs_using(apple)
... def sauce(a, b=2, c=3, **sauce_kwargs):
...     return b * c + apple(a, **sauce_kwargs)
>>> Sig(sauce)
<Sig (a, b=2, c=3, *, x: int, y=2, z=3, **extra_apple_options)>
```

```pycon
>>> @Sig.replace_kwargs_using(apple)
... def sauce(a=1, b=2, c=3, **sauce_kwargs):
...     return b * c + apple(a, **sauce_kwargs)
>>> Sig(sauce)
<Sig (a=1, b=2, c=3, *, x: int, y=2, z=3, **extra_apple_options)>
```

#### *property* required_names

A tuple of required names, preserving the original signature order.

A required name is that must be given in a function call, that is, the name of a
paramater that doesn’t have a default, and is not a variadic.

That lost one is a frequent gotcha, so oo not fall in that gotcha that easily,
we provide a property that contains what we need.

```pycon
>>> f = lambda a00, /, a11, a12, *a23, a34, a35=1, a36='two', **a47: None
>>> Sig(f).required_names
('a00', 'a11', 'a12', 'a34')
```

#### *classmethod* sig_or_default(obj, default_signature=<Signature (\*no_sig_args, \*\*no_sig_kwargs)>)

Returns a Sig instance, or a default signature if there was a ValueError
trying to construct it.

For example, `time.time` doesn’t have a signature

```pycon
>>> import time
>>> has_signature(time.time)
False
```

But we can tell `Sig` to give it the default one:

```pycon
>>> str(Sig.sig_or_default(time.time))
'(*no_sig_args, **no_sig_kwargs)'
```

That’s the default signature, which should work for most purposes.
You can also specify what the default should be though.

```pycon
>>> fake_signature = Sig(lambda *time_takes_no_arguments: ...)
>>> str(Sig.sig_or_default(time.time, fake_signature))
'(*time_takes_no_arguments)'
```

Careful though. If you assign a signature to a function that is not aligned
with that actually functioning of the function, bad things will happen.
In this case, the actual signature of time is the empty signature:

```pycon
>>> str(Sig.sig_or_default(time.time, Sig(lambda: ...)))
'()'
```

#### *classmethod* sig_or_none(obj)

Returns a Sig instance, or None if there was a ValueError trying to
construct it.
One use case is to be able to tell if an object has a signature or not.

```pycon
>>> robust_has_signature = lambda obj: bool(Sig.sig_or_none(obj))
>>> robust_has_signature(robust_has_signature)  # an easy case
True
>>> robust_has_signature(
...     Sig
... )  # another easy one: This time, a type/class (which is callable, yes)
True
```

But here’s where it get’s interesting. `print`, a builtin, doesn’t have a
signature through inspect.signature.

```pycon
>>> has_signature(print)
False
```

But we do get one with robust_has_signature

```pycon
>>> robust_has_signature(print)
True
```

#### sort_params()

Returns a signature with the parameters sorted by kind and default presence.

#### source_args_and_kwargs(\*args, \_ignore_kind=True, \_allow_partial=False, \_apply_defaults=False, \_args_limit=0, \*\*kwargs)

Source the (args, kwargs) for the signature instance, ignoring excess
arguments.

```pycon
>>> def foo(w, /, x: float, y=2, *, z: int = 1):
...     return w + x * y ** z
>>> args, kwargs = Sig(foo).source_args_and_kwargs(
...     4, x=3, y=2, extra="keywords", are="ignored"
... )
>>> args, kwargs
((4,), {'x': 3, 'y': 2})
```

The difference with source_arguments is that here the output is ready to be
called by the
function whose signature we have, since the position-only arguments will be
returned as
args.

```pycon
>>> foo(*args, **kwargs)
10
```

Note that though `w` is a position only argument, you can specify `w=4` as a
keyword argument too (by default):

```pycon
>>> args, kwargs = Sig(foo).source_args_and_kwargs(
...     w=4, x=3, y=2, extra="keywords", are="ignored"
... )
>>> assert (args, kwargs) == ((4,), {"x": 3, "y": 2})
```

If you don’t want to allow that, you can say `_ignore_kind=False`

```pycon
>>> Sig(foo).source_args_and_kwargs(
...     w=4, x=3, y=2, extra="keywords", are="ignored", _ignore_kind=False
... )
Traceback (most recent call last):
  ...
TypeError: ...'w'...
```

You can use `_allow_partial` that will allow you, if
set to `True`, to underspecify the params of a function (in view of being
completed later).

```pycon
>>> Sig(foo).source_args_and_kwargs(x=3, y=2, extra="keywords", are="ignored")
Traceback (most recent call last):
  ...
TypeError:...'w'...
```

But if you specify `_allow_partial=True`…

```pycon
>>> args, kwargs = Sig(foo).source_args_and_kwargs(
...     x=3, y=2, extra="keywords", are="ignored", _allow_partial=True
... )
>>> (args, kwargs) == ((), {"x": 3, "y": 2})
True
```

By default, `_apply_defaults=False`, which will lead to only get those
arguments you input.

```pycon
>>> args, kwargs = Sig(foo).source_args_and_kwargs(
...     4, x=3, y=2, extra="keywords", are="ignored"
... )
>>> (args, kwargs) == ((4,), {"x": 3, "y": 2})
True
```

But if you specify `_apply_defaults=True` non-specified non-require arguments
will be returned with their defaults:

```pycon
>>> args, kwargs = Sig(foo).source_args_and_kwargs(
...     4, x=3, y=2, extra="keywords", are="ignored", _apply_defaults=True
... )
>>> (args, kwargs) == ((4,), {"x": 3, "y": 2, "z": 1})
True
```

#### source_arguments(\*args, \_apply_defaults=False, \_allow_partial=False, \_ignore_kind=True, \*\*kwargs)

Source the arguments for the signature instance, ignoring excess arguments.

```pycon
>>> def foo(w, /, x: float, y="YY", *, z: str = "ZZ"):
...     ...
>>> Sig(foo).source_arguments(11, x=22, extra="keywords", are="ignored")
{'w': 11, 'x': 22}
```

Note that though `w` is a position only argument, you can specify `w=11` as a
keyword argument too (by default):

```pycon
>>> Sig(foo).source_arguments(w=11, x=22, extra="keywords", are="ignored")
{'w': 11, 'x': 22}
```

If you don’t want to allow that, you can say `_ignore_kind=False`

```pycon
>>> Sig(foo).source_arguments(
...     w=11, x=22, extra="keywords", are="ignored", _ignore_kind=False
... )
Traceback (most recent call last):
  ...
TypeError: ...'w'...
```

You can use `_allow_partial` that will allow you, if
set to `True`, to underspecify the params of a function (in view of being
completed later).

```pycon
>>> Sig(foo).source_arguments(x=3, y=2, extra="keywords", are="ignored")
Traceback (most recent call last):
  ...
TypeError: ...'w'...
```

But if you specify `_allow_partial=True`…

```pycon
>>> Sig(foo).source_arguments(
...     x=3, y=2, extra="keywords", are="ignored", _allow_partial=True
... )
{'x': 3, 'y': 2}
```

By default, `_apply_defaults=False`, which will lead to only get those
arguments you input.

```pycon
>>> Sig(foo).source_arguments(4, x=3, y=2, extra="keywords", are="ignored")
{'w': 4, 'x': 3, 'y': 2}
```

But if you specify `_apply_defaults=True` non-specified non-require arguments
will be returned with their defaults:

```pycon
>>> Sig(foo).source_arguments(
...     4, x=3, y=2, extra="keywords", are="ignored", _apply_defaults=True
... )
{'w': 4, 'x': 3, 'y': 2, 'z': 'ZZ'}
```

#### source_kwargs(\*args, \_apply_defaults=False, \_allow_partial=False, \_ignore_kind=True, \*\*kwargs)

Source the arguments for the signature instance, ignoring excess arguments.

```pycon
>>> def foo(w, /, x: float, y="YY", *, z: str = "ZZ"):
...     ...
>>> Sig(foo).source_arguments(11, x=22, extra="keywords", are="ignored")
{'w': 11, 'x': 22}
```

Note that though `w` is a position only argument, you can specify `w=11` as a
keyword argument too (by default):

```pycon
>>> Sig(foo).source_arguments(w=11, x=22, extra="keywords", are="ignored")
{'w': 11, 'x': 22}
```

If you don’t want to allow that, you can say `_ignore_kind=False`

```pycon
>>> Sig(foo).source_arguments(
...     w=11, x=22, extra="keywords", are="ignored", _ignore_kind=False
... )
Traceback (most recent call last):
  ...
TypeError: ...'w'...
```

You can use `_allow_partial` that will allow you, if
set to `True`, to underspecify the params of a function (in view of being
completed later).

```pycon
>>> Sig(foo).source_arguments(x=3, y=2, extra="keywords", are="ignored")
Traceback (most recent call last):
  ...
TypeError: ...'w'...
```

But if you specify `_allow_partial=True`…

```pycon
>>> Sig(foo).source_arguments(
...     x=3, y=2, extra="keywords", are="ignored", _allow_partial=True
... )
{'x': 3, 'y': 2}
```

By default, `_apply_defaults=False`, which will lead to only get those
arguments you input.

```pycon
>>> Sig(foo).source_arguments(4, x=3, y=2, extra="keywords", are="ignored")
{'w': 4, 'x': 3, 'y': 2}
```

But if you specify `_apply_defaults=True` non-specified non-require arguments
will be returned with their defaults:

```pycon
>>> Sig(foo).source_arguments(
...     4, x=3, y=2, extra="keywords", are="ignored", _apply_defaults=True
... )
{'w': 4, 'x': 3, 'y': 2, 'z': 'ZZ'}
```

#### to_signature_kwargs()

The dict of keyword arguments to make this signature instance.

```pycon
>>> def f(w, /, x: float = 2, y=1, *, z: int = 0) -> float:
...     ...
>>> Sig(f).to_signature_kwargs()
{'parameters':
    [<Parameter "w">,
    <Parameter "x: float = 2">,
    <Parameter "y=1">,
    <Parameter "z: int = 0">],
'return_annotation': <class 'float'>}
```

Note that this does NOT return:

```text
{'parameters': self.parameters,
'return_annotation': self.return_annotation}
```

which would not actually work as keyword arguments of `Signature`.
Yeah, I know. Don’t ask me, ask the authors of `Signature`!

Instead, `parammeters` will be `list(self.parameters.values())`, which does
work.

#### to_simple_signature()

A builtin `inspect.Signature` instance equivalent (i.e. without the extra
properties and methods)

```pycon
>>> def f(w, /, x: float = 2, y=1, *, z: int = 0):
...     ...
>>> Sig(f).to_simple_signature()
<Signature (w, /, x: float = 2, y=1, *, z: int = 0)>
```

#### *property* var_keyword_name

The name of the VAR_KEYWORD parameter, or `None` if there is none.

#### *property* var_positional_name

The name of the VAR_POSITIONAL parameter, or `None` if there is none.

#### *property* with_defaults

Sub-signature containing only “not required” (i.e. with defaults) parameters.

```pycon
>>> list(Sig(lambda *args, a, b, x=1, y=1, **kwargs: ...).with_defaults)
['args', 'x', 'y', 'kwargs']
```

#### *property* without_defaults

Sub-signature containing only “required” (i.e. without defaults) parameters.

```pycon
>>> list(Sig(lambda *args, a, b, x=1, y=1, **kwargs: ...).without_defaults)
['a', 'b']
```

#### wrap(func, ignore_incompatible_signatures=True, , copy_function=False)

Gives the input function the signature.

This is similar to the `functools.wraps` function, but parametrized by a
signature
(not a callable). Also, where as both write to the input func’s `__signature__`
attribute, here we also write to

- `__defaults__` and `__kwdefaults__`, extracting these from `__signature__`
  (functools.wraps doesn’t do that at the time of writing this
  (see [https://github.com/python/cpython/pull/21379](https://github.com/python/cpython/pull/21379))).
- `__annotations__` (also extracted from `__signature__`)
- does not write to `__module__`, `__name__`, `__qualname__`, `__doc__`
  (because again, we’re basinig the injecton on a signature, not a function,
  so we have no name, doc, etc…)

#### WARNING
The fact that you’ve modified the signature of your function doesn’t
mean that the decorated function will work as expected (or even work at all).
See below for examples.

```pycon
>>> def f(w, /, x: float = 1, y=2, z: int = 3):
...     return w + x * y ** z
>>> f(0, 1)  # 0 + 1 * 2 ** 3
8
>>> f.__defaults__
(1, 2, 3)
>>> assert 8 == f(0) == f(0, 1) == f(0, 1, 2) == f(0, 1, 2, 3)
```

Now let’s create a very similar function to f, but where:

- w is not position-only
- x annot is int instead of float, and doesn’t have a default
- z’s default changes to 10

```pycon
>>> def g(w, x: int, y=2, z: int = 10):
...     return w + x * y ** z
>>> s = Sig(g)
>>> f = s.wrap(f)
>>> import inspect
>>> inspect.signature(f)  # see that
<Sig (w, x: int, y=2, z: int = 10)>
>>> # But (unlike with functools.wraps) here we get __defaults__ and
__kwdefault__
>>> f.__defaults__  # see that x has no more default & z's default is now 10
(2, 10)
>>> f(
...     0, 1
... )  # see that now we get a different output because using different defaults
1024
```

Remember that you are modifying the signature, not the function itself.
Signature changes in defaults will indeed change the function’s behavior.
But changes in name or kind will only be reflected in the signature, and
misalignment with the wrapped function will lead to unexpected results.

```pycon
>>> def f(w, /, x: float = 1, y=2, *, z: int = 3):
...     return w + x * y ** z
>>> f(0)  # 0 + 1 * 2 ** 3
8
>>> f(0, 1, 2, 3)  # error expected!
Traceback (most recent call last):
  ...
TypeError: f() takes from 1 to 3 positional arguments but 4 were given
```

But if you try to remove the argument kind constraint by just changing the
signature, you’ll fail.

```pycon
>>> def g(w, x: float = 1, y=2, z: int = 3):
...     return w + x * y ** z
>>> f = Sig(g).wrap(f)
>>> f(0)
Traceback (most recent call last):
  ...
TypeError: f() missing 1 required keyword-only argument: 'z'
>>> f(0, 1, 2, 3)
Traceback (most recent call last):
  ...
TypeError: f() takes from 0 to 3 positional arguments but 4 were given
```

### *class* i2.signatures.SigPair(sig1, sig2)

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

Compare two signatures: shared and missing names, and per-parameter differences.

For example, offers methods to compare two signatures in various ways.

* **Parameters:**
  * **sig1** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable) | [`Sig`](_autosummary/i2.signatures.html.md#i2.signatures.Sig)) – First signature or signature-able object.
  * **sig2** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable) | [`Sig`](_autosummary/i2.signatures.html.md#i2.signatures.Sig)) – Second signature or signature-able object.

```pycon
>>> from pprint import pprint
>>> def three(a, b: int, c=3): ...
>>> def little(a, *, b=2, d=4) -> int: ...
>>> def pigs(a, b) -> int: ...
>>> sig_pair = SigPair(three, little)
>>>
>>> sig_pair.shared_names
['a', 'b']
>>> sig_pair.names_missing_in_sig1
['d']
>>> sig_pair.names_missing_in_sig2
['c']
>>> sig_pair.param_comparison()
False
>>> pprint(sig_pair.diff())
{'names_missing_in_sig1': ['d'],
'names_missing_in_sig2': ['c'],
'param_differences': {'b': {'annotation': (<class 'int'>,
                                            <class 'inspect._empty'>),
                            'default': (<class 'inspect._empty'>, 2),
                            'kind': (<_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
                                    <_ParameterKind.KEYWORD_ONLY: 3>)}},
'return_annotation': (<class 'inspect._empty'>, <class 'int'>)}
```

Call compatibility says that any arguments leading to a valid call to a function
having the first signature, will also lead to a valid call to a function having the
second signature. This is not the case for the signatures of `three` and `little`:

```pycon
>>> sig_pair.are_call_compatible()
False
```

But we don’t need to have equal signatures to have call compatibility. For example,

```pycon
>>> SigPair(three, lambda a, b=2, c=30: None).are_call_compatible()
True
```

Note that call-compatibility is not symmetric. For example, `pigs` is call
compatible with `three`, since any arguments that are valid for `pigs` are valid
for `three`:

```pycon
>>> SigPair(pigs, three).are_call_compatible()
True
```

But `three` is not call-compatible with `pigs` since `three` requires could include
a `c` argument, which `pigs` would choke on.

```pycon
>>> SigPair(three, pigs).are_call_compatible()
False
```

#### are_call_compatible(param_comparator=None)

Check if the signatures are call-compatible.

Returns True if sig1 can be used to call sig2 or vice versa.

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

```pycon
>>> sig1 = Sig(lambda a, b, c=3: None)
>>> sig2 = Sig(lambda a, b: None)
>>> comp = SigPair(sig1, sig2)
>>> comp.are_call_compatible()
False
```

```pycon
>>> comp = SigPair(sig2, sig1)
>>> comp.are_call_compatible()
True
```

#### diff()

Get a dictionary of differences between the two signatures.

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

```pycon
>>> from pprint import pprint
>>> def three(a, b: int, c=3): ...
>>> def little(a, *, b=2, d=4) -> int: ...
>>> def pigs(a, b: int = 2) -> int: ...
>>> pprint(SigPair(three, little).diff())
{'names_missing_in_sig1': ['d'],
'names_missing_in_sig2': ['c'],
'param_differences': {'b': {'annotation': (<class 'int'>,
                                            <class 'inspect._empty'>),
                            'default': (<class 'inspect._empty'>, 2),
                            'kind': (<_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
                                    <_ParameterKind.KEYWORD_ONLY: 3>)}},
'return_annotation': (<class 'inspect._empty'>, <class 'int'>)}
>>> pprint(SigPair(three, pigs).diff())
{'names_missing_in_sig2': ['c'],
'param_differences': {'b': {'default': (<class 'inspect._empty'>, 2)}},
'return_annotation': (<class 'inspect._empty'>, <class 'int'>)}
>>> pprint(SigPair(three, three).diff())
{}
```

#### diff_str()

Get a string representation of the differences between the two signatures.

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

#### *property* names_missing_in_sig1

List of names that are in the sig2 signature but not in sig1.

```pycon
>>> sig1 = Sig(lambda a, b, c: None)
>>> sig2 = Sig(lambda b, c, d: None)
>>> comp = SigPair(sig1, sig2)
>>> comp.names_missing_in_sig1
['d']
```

#### *property* names_missing_in_sig2

List of names that are in the sig1 signature but not in sig2.

```pycon
>>> sig1 = Sig(lambda a, b, c: None)
>>> sig2 = Sig(lambda b, c, d: None)
>>> comp = SigPair(sig1, sig2)
>>> comp.names_missing_in_sig2
['a']
```

#### param_comparison(comparator=<function param_comparator>, aggregation=<built-in function all>)

Compare parameters between the two signatures using the provided comparator function.

* **Parameters:**
  * **comparator** – A function to compare two parameters.
  * **aggregation** – A function to aggregate the results of the comparisons.
* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)
* **Returns:**
  Boolean result of the aggregated comparisons.

```pycon
>>> sig1 = Sig('(a, b: int, c=3)')
>>> sig2 = Sig('(a, *, b=2, d=4)')
>>> comp = SigPair(sig1, sig2)
>>> comp.param_comparison()
False
```

#### param_differences()

Get a dictionary of parameter differences between the two signatures.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  A dict containing differences for each shared param that has any.

```pycon
>>> sig1 = Sig('(a, b: int, c=3)')
>>> sig2 = Sig('(a, *, b=2, d=4)')
>>> comp = SigPair(sig1, sig2)
>>> result = comp.param_differences()
>>> expected = {
...     'b': {
...         'kind': (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY),
...         'default': (Parameter.empty, 2),
...         'annotation': (int, Parameter.empty),
...     }
... }
>>> result == expected
True
```

#### *property* shared_names

List of names that are common to both signatures, in the order of sig1.

```pycon
>>> sig1 = Sig(lambda a, b, c: None)
>>> sig2 = Sig(lambda b, c, d: None)
>>> comp = SigPair(sig1, sig2)
>>> comp.shared_names
['b', 'c']
```

### i2.signatures.all_pk_signature(callable_or_signature)

Changes all (non-variadic) arguments to be of the PK (POSITION_OR_KEYWORD) kind.

Wrapping a function with the resulting signature doesn’t make that function callable
with PK kinds in itself.
It just gives it a signature without position and keyword ONLY kinds.
It should be used to wrap such a function that actually carries out the
implementation though!

```pycon
>>> def foo(w, /, x: float, y=1, *, z: int = 1, **kwargs):
...     ...
>>> def bar(*args, **kwargs):
...     ...
...
>>> from inspect import signature
>>> new_foo = all_pk_signature(foo)
>>> Sig(new_foo)
<Sig (w, x: float, y=1, z: int = 1, **kwargs)>
>>> all_pk_signature(signature(foo))
<Sig (w, x: float, y=1, z: int = 1, **kwargs)>
```

But note that the variadic arguments `*args` and `**kwargs` remain variadic:

```pycon
>>> all_pk_signature(signature(bar))
<Signature (*args, **kwargs)>
```

It works with `Sig` too (since Sig is a Signature), and maintains it’s other
attributes (like name).

```pycon
>>> sig = all_pk_signature(Sig(bar))
>>> sig
<Sig (*args, **kwargs)>
>>> sig.name
'bar'
```

#### SEE ALSO
`i2.signatures.kind_forgiving_func`

### i2.signatures.assure_callable(obj)

Return `obj` if callable, else an empty function carrying the signature `obj` specifies.

```pycon
>>> f = ensure_callable(["a", ("b", 2)])
>>> Sig(f)
<Sig (a, b=2)>
```

### i2.signatures.assure_params(obj=None)

Get an interable of Parameter instances from an object.

* **Parameters:**
  **obj** (`Union`[[`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature), [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)])
* **Returns:**

From a callable:

```pycon
>>> def f(w, /, x: float = 1, y=1, *, z: int = 1):
...     ...
>>> ensure_params(f)
[<Parameter "w">, <Parameter "x: float = 1">, <Parameter "y=1">, <Parameter "z: int = 1">]
```

From an iterable of strings, dicts, or tuples

```pycon
>>> ensure_params(
...     [
...         "xyz",
...         (
...             "b",
...             Parameter.empty,
...             int,
...         ),  # if you want an annotation without a default use Parameter.empty
...         (
...             "c",
...             2,
...         ),  # if you just want a default, make it the second element of your tup
...         dict(name="d", kind=Parameter.VAR_KEYWORD),
...     ]
... )  # all kinds are by default PK: Use dict to specify otherwise.
[<Param "xyz">, <Param "b: int">, <Param "c=2">, <Param "**d">]
```

If no input is given, an empty list is returned.

```pycon
>>> ensure_params()  # equivalent to ensure_params(None)
[]
```

### i2.signatures.assure_signature(obj)

Make an `inspect.Signature` from a signature, callable, parameter, iterable of
parameter specs, or `None` (empty signature). Unlike `Sig`, a signature string is
not accepted.

* **Return type:**
  [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature)

```pycon
>>> ensure_signature(["a", "b"])
<Signature (a, b)>
>>> ensure_signature(None)
<Signature ()>
```

* **Raises:**
  [**TypeError**](https://docs.python.org/3/builtins/exceptions.html#TypeError) – If `obj` is none of the above (a signature string raises
  `ValueError` instead, from `ensure_param`).
* **Return type:**
  [*Signature*](https://docs.python.org/3/library/inspect.html#inspect.Signature)

### i2.signatures.call_forgivingly(func, \*args, \*\*kwargs)

Call function on given args and kwargs, but only taking what the function needs
(not choking if they’re extras variables)

#### TIP
If you into trouble because your kwargs has a ‘func’ key,
(which would then clash with the `func` param of call_forgivingly), then
use `_call_forgivingly` instead, specifying args and kwargs as tuple and
dict.

```pycon
>>> def foo(a, b: int = 0, c=None) -> int:
...     return "foo", (a, b, c)
>>> call_forgivingly(
...     foo,  # the function you want to call
...     "input for a",  # meant for a -- the first (and only) argument foo requires
...     c=42,  # skiping b and giving c a non-default value
...     intruder="argument",  # but wait, this argument name doesn't exist! Oh no!
... )  # well, as it happens, nothing bad -- the intruder argument is just ignored
('foo', ('input for a', 0, 42))
```

An example of what happens when variadic kinds are involved:

```pycon
>>> def bar(x, *args1, y=1, **kwargs1):
...     return x, args1, y, kwargs1
>>> call_forgivingly(bar, 1, 2, 3, y=4, z=5)
(1, (2, 3), 4, {'z': 5})
```

### i2.signatures.call_somewhat_forgivingly(func, args, kwargs, enforce_sig=None)

Call function on given args and kwargs, but with controllable argument leniency.
By default, the function will only pick from args and kwargs what matches it’s
signature, ignoring anything else in args and kwargs.

But the real use of `call_somewhat_forgivingly` kicks in when you specify a
`enforce_sig`: A signature (or any object that can be resolved into a signature
through `Sig(enforce_sig)`) that will be used to bind the inputs, thus validating
them against the `enforce_sig` signature (including extra arguments, defaults,
etc.).

`call_somewhat_forgivingly` helps you do this kind of thing systematically.

```pycon
>>> f = lambda a: a * 11
>>> assert call_somewhat_forgivingly(f, (2,), {}) == f(2)
```

In the above, we have no `enforce_sig`. The real use of call_somewhat_forgivingly
is when we ask it to enforce a signature. Let’s do this by specifying a function
(no need for it to do anything: Only the signature is used.

```pycon
>>> g = lambda a, b=None: ...
```

Calling `f` on it’s normal set of inputs (one input in this case) gives you the
same thing as `f`:

```pycon
>>> assert call_somewhat_forgivingly(f, (2,), {}, enforce_sig=g) == f(2)
>>> assert call_somewhat_forgivingly(f, (), {'a': 2}, enforce_sig=g) == f(2)
```

If you call with an extra positional argument, it will just be ignored.

```pycon
>>> assert call_somewhat_forgivingly(f, (2, 'ignored'), {}, enforce_sig=g) == f(2)
```

If you call with a `b` keyword-argument (which matches `g`’s signature,
it will also be ignored.

```pycon
>>> assert call_somewhat_forgivingly(
... f, (2,), {'b': 'ignored'}, enforce_sig=g
... ) == f(2)
>>> assert call_somewhat_forgivingly(
...     f, (), {'a': 2, 'b': 'ignored'}, enforce_sig=g
... ) == f(2)
```

But if you call with three positional arguments (one more than g allows),
or call with a keyword argument that is not in `g`’s signature, it will
raise a `TypeError`:

```pycon
>>> call_somewhat_forgivingly(f,
...     (2, 'ignored', 'does_not_fit_g_signature_anymore'), {}, enforce_sig=g
... )
Traceback (most recent call last):
    ...
TypeError: too many positional arguments
>>> call_somewhat_forgivingly(f,
...     (2,), {'this_argname': 'is not in g'}, enforce_sig=g
... )
Traceback (most recent call last):
    ...
TypeError: got an unexpected keyword argument 'this_argname'
```

### i2.signatures.ch_func_to_all_pk(func)

Returns a decorated function where all arguments are of the PK kind.
(PK: Positional_or_keyword)

* **Parameters:**
  **func** – A callable

```pycon
>>> def f(a, /, b, *, c=None, **kwargs):
...     return a + b * c
...
>>> print(Sig(f))
(a, /, b, *, c=None, **kwargs)
>>> ff = ch_func_to_all_pk(f)
>>> print(Sig(ff))
(a, b, c=None, **kwargs)
>>> ff(1, 2, 3)
7
>>>
>>> def g(x, y=1, *args, **kwargs):
...     ...
...
>>> print(Sig(g))
(x, y=1, *args, **kwargs)
>>> gg = ch_func_to_all_pk(g)
>>> print(Sig(gg))
(x, y=1, args=(), **kwargs)
```

### i2.signatures.ch_signature_to_all_pk(callable_or_signature)

Changes all (non-variadic) arguments to be of the PK (POSITION_OR_KEYWORD) kind.

Wrapping a function with the resulting signature doesn’t make that function callable
with PK kinds in itself.
It just gives it a signature without position and keyword ONLY kinds.
It should be used to wrap such a function that actually carries out the
implementation though!

```pycon
>>> def foo(w, /, x: float, y=1, *, z: int = 1, **kwargs):
...     ...
>>> def bar(*args, **kwargs):
...     ...
...
>>> from inspect import signature
>>> new_foo = all_pk_signature(foo)
>>> Sig(new_foo)
<Sig (w, x: float, y=1, z: int = 1, **kwargs)>
>>> all_pk_signature(signature(foo))
<Sig (w, x: float, y=1, z: int = 1, **kwargs)>
```

But note that the variadic arguments `*args` and `**kwargs` remain variadic:

```pycon
>>> all_pk_signature(signature(bar))
<Signature (*args, **kwargs)>
```

It works with `Sig` too (since Sig is a Signature), and maintains it’s other
attributes (like name).

```pycon
>>> sig = all_pk_signature(Sig(bar))
>>> sig
<Sig (*args, **kwargs)>
>>> sig.name
'bar'
```

#### SEE ALSO
`i2.signatures.kind_forgiving_func`

### i2.signatures.ch_variadics_to_non_variadic_kind(func, , ch_variadic_keyword_to_keyword=True)

Replace a function’s variadic parameters with a tuple and a dict parameter of
the same names, returning an equivalent function.

Essentially, given a `func(a, *b, c, **d)` function want to get a
`new_func(a, b=(), c=None, d={})` that has the same functionality
(in fact, calls the original `func` function behind the scenes), but without
where the variadic arguments `*b` and `**d` are replaced with a `b` expecting an
iterable (e.g. tuple/list) and `d` expecting a `dict` to contain the
desired inputs.

Besides this, the decorator tries to be as conservative as possible, making only
the minimum changes needed to meet the goal of getting to a variadic-less
interface. When it doubt, and error will be raised.

```pycon
>>> def foo(a, *args, bar, **kwargs):
...     return f"{a=}, {args=}, {bar=}, {kwargs=}"
>>> assert str(Sig(foo)) == '(a, *args, bar, **kwargs)'
>>> wfoo = ch_variadics_to_non_variadic_kind(foo)
>>> str(Sig(wfoo))
'(a, args=(), *, bar, kwargs={})'
```

And now to do this:

```pycon
>>> foo(1, 2, 3, bar=4, hello="world")
"a=1, args=(2, 3), bar=4, kwargs={'hello': 'world'}"
```

We can do it like this instead:

```pycon
>>> wfoo(1, (2, 3), bar=4, kwargs=dict(hello="world"))
"a=1, args=(2, 3), bar=4, kwargs={'hello': 'world'}"
```

Note, the outputs are the same. It’s just the way we call our function that has
changed.

```pycon
>>> assert wfoo(1, (2, 3), bar=4, kwargs=dict(hello="world")
... ) == foo(1, 2, 3, bar=4, hello="world")
>>> assert wfoo(1, (2, 3), bar=4) == foo(1, 2, 3, bar=4)
>>> assert wfoo(1, (), bar=4) == foo(1, bar=4)
```

Note that if there is not variadic positional arguments, the variadic keyword
will still be a keyword-only kind.

```pycon
>>> @ch_variadics_to_non_variadic_kind
... def func(a, bar=None, **kwargs):
...     return f"{a=}, {bar=}, {kwargs=}"
>>> str(Sig(func))
'(a, bar=None, *, kwargs={})'
>>> assert func(1, bar=4, kwargs=dict(hello="world")
...     ) == "a=1, bar=4, kwargs={'hello': 'world'}"
```

If the function has neither variadic kinds, it will remain untouched.

```pycon
>>> def func(a, /, b, *, c=3):
...     return a + b + c
>>> ch_variadics_to_non_variadic_kind(func) == func
True
```

If you only want the variadic positional to be handled, but leave any
VARIADIC_KEYWORD kinds (`**kwargs`) alone, you can do so by setting
`ch_variadic_keyword_to_keyword=False`.
If you’ll need to use `ch_variadics_to_non_variadic_kind` in such a way
repeatedly, we suggest you use `functools.partial` to not have to specify this
configuration repeatedly.

```pycon
>>> from functools import partial
>>> tuple_the_args = partial(ch_variadics_to_non_variadic_kind,
...     ch_variadic_keyword_to_keyword=False
... )
>>> @tuple_the_args
... def foo(a, *args, bar=None, **kwargs):
...     return f"{a=}, {args=}, {bar=}, {kwargs=}"
>>> Sig(foo)
<Sig (a, args=(), *, bar=None, **kwargs)>
>>> foo(1, (2, 3), bar=4, hello="world")
"a=1, args=(2, 3), bar=4, kwargs={'hello': 'world'}"
```

### i2.signatures.common_and_diff_argnames(func1, func2)

Get list of argument names that are common to two functions, as well as the two
lists of names that are different

* **Parameters:**
  * **func1** (`callable`) – First function
  * **func2** (`callable`) – Second function
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  A dict with fields ‘common’, ‘func1_not_func2’, and ‘func2_not_func1’

```pycon
>>> def f(t, h, i, n, k):
...     ...
...
>>> def g(t, w, i, c, e):
...     ...
...
>>> common_and_diff_argnames(f, g)
{'common': ['t', 'i'], 'func1_not_func2': ['h', 'n', 'k'], 'func2_not_func1': ['w', 'c', 'e']}
>>> common_and_diff_argnames(g, f)
{'common': ['t', 'i'], 'func1_not_func2': ['w', 'c', 'e'], 'func2_not_func1': ['h', 'n', 'k']}
```

### i2.signatures.compare_signatures(func1, func2, signature_comparator=<built-in function eq>)

Compare the `Sig` of two callables with `signature_comparator` (equality by default).

### i2.signatures.convert_to_PK(kinds)

A `{name: POSITIONAL_OR_KEYWORD}` dict for every name in `kinds` (a `kinds_modifier`).

### i2.signatures.copy_func(f)

Copy a function (not sure it works with all types of callables).

```pycon
>>> def h(x, y=2):
...     return x + y
>>> h.an_attr = 42
>>> hc = copy_func(h)
>>> hc(1), hc.__name__, hc.an_attr, hc is h
(3, 'h', 42, False)
```

### i2.signatures.defaults_are_the_same_when_not_empty(dflt1, dflt2)

Check if two defaults are the same when they are not empty.

```pycon
>>> defaults_are_the_same_when_not_empty(1, 1)
True
>>> defaults_are_the_same_when_not_empty(1, 2)
False
>>> defaults_are_the_same_when_not_empty(1, None)
False
>>> defaults_are_the_same_when_not_empty(1, Parameter.empty)
True
```

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

Wrap `func` so that calling it warns that `old_name` is deprecated in favour of `func`.

### i2.signatures.dflt1_is_empty_or_dflt2_is_not(dflt1, dflt2)

Why such a strange default comparison function?

This is to be used as a default in is_call_compatible_with.

Consider two functions func1 and func2 with a parameter p with default values
dflt1 and dflt2 respectively.
If dflt1 was not empty and dflt2 was, this would mean that func1 could be called
without specifying p, but func2 couldn’t.

So to avoid this situation, we use dflt1_is_empty_or_dflt2_is_not as the default

### i2.signatures.dflt1_is_empty_or_dflt2_is_not_param_comparator(param1, param2, \*, name=<function ignore_any_differences>, kind=<function ignore_any_differences>, default=<function dflt1_is_empty_or_dflt2_is_not>, annotation=<function ignore_any_differences>, aggreg=<built-in function all>)

Permissive version of param_comparator that ignores any differences of parameter
attributes.

It is meant to be used with partial, but with a permissive base, contrary to the
base param_comparator which requires strict equality (`eq`) for all attributes.

* **Return type:**
  *Comparison*

### i2.signatures.dict_of_attribute_signatures(cls)

Extract the signatures of all callable attributes of a class, as a `{name: signature}` dict.

* **Parameters:**
  **cls** ([`type`](https://docs.python.org/3/builtins/functions.html#type)) – The class that holds the the `(name, func)` pairs we want to extract.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature)]
* **Returns:**
  A dict of `(name, signature(func))` pairs extracted from class.

One of the intended applications is to use `dict_of_attribute_signatures` as a
decorator, like so:

```pycon
>>> @dict_of_attribute_signatures
... class names_and_signatures:
...     def foo(x: str, *, y=2) -> tuple: ...
...     def bar(z, /) -> float: ...
>>> names_and_signatures
{'foo': <Signature (x: str, *, y=2) -> tuple>, 'bar': <Signature (z, /) -> float>}
```

### i2.signatures.ensure_callable(obj)

Return `obj` if callable, else an empty function carrying the signature `obj` specifies.

```pycon
>>> f = ensure_callable(["a", ("b", 2)])
>>> Sig(f)
<Sig (a, b=2)>
```

### i2.signatures.ensure_param(p)

Make a `Param` from a parameter, a name, a `(name, default[, annotation])` tuple,
or a dict of `Param` keyword arguments.

```pycon
>>> ensure_param("x"), ensure_param(("x", 1, int)), ensure_param({"name": "y", "default": 2})
(<Param "x">, <Param "x: int = 1">, <Param "y=2">)
```

* **Raises:**
  [**TypeError**](https://docs.python.org/3/builtins/exceptions.html#TypeError) – If `p` is none of the above.

### i2.signatures.ensure_params(obj=None)

Get an interable of Parameter instances from an object.

* **Parameters:**
  **obj** (`Union`[[`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature), [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)])
* **Returns:**

From a callable:

```pycon
>>> def f(w, /, x: float = 1, y=1, *, z: int = 1):
...     ...
>>> ensure_params(f)
[<Parameter "w">, <Parameter "x: float = 1">, <Parameter "y=1">, <Parameter "z: int = 1">]
```

From an iterable of strings, dicts, or tuples

```pycon
>>> ensure_params(
...     [
...         "xyz",
...         (
...             "b",
...             Parameter.empty,
...             int,
...         ),  # if you want an annotation without a default use Parameter.empty
...         (
...             "c",
...             2,
...         ),  # if you just want a default, make it the second element of your tup
...         dict(name="d", kind=Parameter.VAR_KEYWORD),
...     ]
... )  # all kinds are by default PK: Use dict to specify otherwise.
[<Param "xyz">, <Param "b: int">, <Param "c=2">, <Param "**d">]
```

If no input is given, an empty list is returned.

```pycon
>>> ensure_params()  # equivalent to ensure_params(None)
[]
```

### i2.signatures.ensure_signature(obj)

Make an `inspect.Signature` from a signature, callable, parameter, iterable of
parameter specs, or `None` (empty signature). Unlike `Sig`, a signature string is
not accepted.

* **Return type:**
  [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature)

```pycon
>>> ensure_signature(["a", "b"])
<Signature (a, b)>
>>> ensure_signature(None)
<Signature ()>
```

* **Raises:**
  [**TypeError**](https://docs.python.org/3/builtins/exceptions.html#TypeError) – If `obj` is none of the above (a signature string raises
  `ValueError` instead, from `ensure_param`).
* **Return type:**
  [*Signature*](https://docs.python.org/3/library/inspect.html#inspect.Signature)

### i2.signatures.expand_nested_key(d, k)

Items of `d`, except that a lone `{k: {k: ...}}` nesting is unwrapped first.

### i2.signatures.extract_arguments(params, , what_to_do_with_remainding='return', include_all_when_var_keywords_in_params=False, assert_no_missing_position_only_args=False, \*\*kwargs)

Extract arguments needed to satisfy the params of a callable, dealing with the
dirty details.

Returns an (param_args, param_kwargs, remaining_kwargs) tuple where

- param_args are the values of kwargs that are PO (POSITION_ONLY) as defined by
  params,
- param_kwargs are those names that are both in params and not in param_args, and
- remaining_kwargs are the remaining.

Intended usage: When you need to call a function `func` that has some
position-only arguments,
but you have a kwargs dict of arguments in your hand. You can’t just to `func(
**kwargs)`.
But you can (now) do

```text
args, kwargs, remaining = extract_arguments(kwargs, func)  # extract from kwargs
what you need for func
# ... check if remaing is empty (or not, depending on your paranoia), and then
call the func:
func(*args, **kwargs)
```

(And if you doing that a lot: Do put it in a decorator!)

#### SEE ALSO
extract_arguments.without_remainding

The most frequent case you’ll encounter is when there’s no POSITION_ONLY args,
your param_args will be empty
and you param_kwargs will contain all the arguments that match params,
in the order of these params.

```pycon
>>> from inspect import signature
>>> def f(a, b, c=None, d=0):
...     ...
...
>>> extract_arguments(f, b=2, a=1, c=3, d=4, extra="stuff")
((), {'a': 1, 'b': 2, 'c': 3, 'd': 4}, {'extra': 'stuff'})
```

But sometimes you do have POSITION_ONLY arguments.
What extract_arguments will do for you is return the value of these as the first
element of
the triple.

```pycon
>>> def f(a, b, c=None, /, d=0):
...     ...
...
>>> extract_arguments(f, b=2, a=1, c=3, d=4, extra="stuff")
((1, 2, 3), {'d': 4}, {'extra': 'stuff'})
```

Note above how we get `(1, 2, 3)`, the order defined by the func’s signature,
instead of `(2, 1, 3)`, the order defined by the kwargs.
So it’s the params (e.g. function signature) that determine the order, not kwargs.
When using to call a function, this is especially crucial if we use POSITION_ONLY
arguments.

See also that the third output, the remaining_kwargs, as `{'extra': 'stuff'}` since
it was not in the params of the function.
Even if you include a VAR_KEYWORD kind of argument in the function, it won’t change
this behavior.

```pycon
>>> def f(a, b, c=None, /, d=0, **kws):
...     ...
...
>>> extract_arguments(f, b=2, a=1, c=3, d=4, extra="stuff")
((1, 2, 3), {'d': 4}, {'extra': 'stuff'})
```

This is because we don’t want to assume that all the kwargs can actually be
included in a call to the function behind the params.
Instead, the user can chose whether to include the remainder by doing a:

```text
param_kwargs.update(remaining_kwargs)
```

et voilà.

That said, we do understand that it may be a common pattern, so we’ll do that
extra step for you
if you specify `include_all_when_var_keywords_in_params=True`.

```pycon
>>> def f(a, b, c=None, /, d=0, **kws):
...     ...
...
>>> extract_arguments(
...     f,
...     b=2,
...     a=1,
...     c=3,
...     d=4,
...     extra="stuff",
...     include_all_when_var_keywords_in_params=True,
... )
((1, 2, 3), {'d': 4, 'extra': 'stuff'}, {})
```

If you’re expecting no remainder you might want to just get the args and kwargs (
not this third
expected-to-be-empty remainder). You have two ways to do that, specifying:

- `what_to_do_with_remainding='ignore'`, which will just return the (args,
  kwargs) pair
- `what_to_do_with_remainding='assert_empty'`, which will do the same, but first
  assert the remainder is empty

We suggest to use `functools.partial` to configure the `argument_argument` you need.

```pycon
>>> from functools import partial
>>> arg_extractor = partial(
...     extract_arguments,
...     what_to_do_with_remainding="assert_empty",
...     include_all_when_var_keywords_in_params=True,
... )
>>> def f(a, b, c=None, /, d=0, **kws):
...     ...
...
>>> arg_extractor(f, b=2, a=1, c=3, d=4, extra="stuff")
((1, 2, 3), {'d': 4, 'extra': 'stuff'})
```

And what happens if the kwargs doesn’t contain all the POSITION_ONLY arguments?

```pycon
>>> def f(a, b, c=None, /, d=0):
...     ...
...
>>> extract_arguments(f, b=2, d="is a kw arg", e="is not an arg at all")
((MissingArgValFor("a"), 2, MissingArgValFor("c")), {'d': 'is a kw arg'}, {'e': 'is not an arg at all'})
```

A few more examples…

Let’s call `extract_arguments` with params being not a function,
but, a Signature instance, a mapping whose values are Parameter instances,
or an iterable of Parameter instances…

```pycon
>>> def func(a, b, /, c=None, *, d=0, **kws):
...     ...
...
>>> sig = Signature.from_callable(func)
>>> param_map = sig.parameters
>>> param_iterable = param_map.values()
>>> kwargs = dict(b=2, a=1, c=3, d=4, extra="stuff")
>>> assert extract_arguments(sig, **kwargs) == extract_arguments(func, **kwargs)
>>> assert extract_arguments(param_map, **kwargs) == extract_arguments(
...     func, **kwargs
... )
>>> assert extract_arguments(param_iterable, **kwargs) == extract_arguments(
...     func, **kwargs
... )
```

Edge case:
No params specified? No problem. You’ll just get empty args and kwargs. Everything
in the remainder

```pycon
>>> extract_arguments(params=(), b=2, a=1, c=3, d=0)
((), {}, {'b': 2, 'a': 1, 'c': 3, 'd': 0})
```

* **Parameters:**
  * **params** (`Union`[[`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature), [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Specifies what PO arguments should be extracted.
    Could be a callable, Signature, iterable of Parameters…
  * **what_to_do_with_remainding** – ‘return’ (default): function will return `param_args`, `param_kwargs`,
    `remaining_kwargs`
    ‘ignore’: function will return `param_args`, `param_kwargs`
    ‘assert_empty’: function will assert that `remaining_kwargs` is empty and then
    return `param_args`, `param_kwargs`
  * **include_all_when_var_keywords_in_params** – If True, and the params have a
    VAR_KEYWORD kind, include all kwargs in `param_kwargs` (nothing remains).
  * **assert_no_missing_position_only_args** – If True, raise an `AssertionError`
    when a POSITION_ONLY param has no matching kwarg.
  * **kwargs** – The kwargs to extract the args from
* **Returns:**
  A (param_args, param_kwargs, remaining_kwargs) tuple.

### i2.signatures.extract_arguments_asserting_no_remainder(params, , what_to_do_with_remainding='assert_empty', include_all_when_var_keywords_in_params=False, assert_no_missing_position_only_args=False, \*\*kwargs)

Extract arguments needed to satisfy the params of a callable, dealing with the
dirty details.

Returns an (param_args, param_kwargs, remaining_kwargs) tuple where

- param_args are the values of kwargs that are PO (POSITION_ONLY) as defined by
  params,
- param_kwargs are those names that are both in params and not in param_args, and
- remaining_kwargs are the remaining.

Intended usage: When you need to call a function `func` that has some
position-only arguments,
but you have a kwargs dict of arguments in your hand. You can’t just to `func(
**kwargs)`.
But you can (now) do

```text
args, kwargs, remaining = extract_arguments(kwargs, func)  # extract from kwargs
what you need for func
# ... check if remaing is empty (or not, depending on your paranoia), and then
call the func:
func(*args, **kwargs)
```

(And if you doing that a lot: Do put it in a decorator!)

#### SEE ALSO
extract_arguments.without_remainding

The most frequent case you’ll encounter is when there’s no POSITION_ONLY args,
your param_args will be empty
and you param_kwargs will contain all the arguments that match params,
in the order of these params.

```pycon
>>> from inspect import signature
>>> def f(a, b, c=None, d=0):
...     ...
...
>>> extract_arguments(f, b=2, a=1, c=3, d=4, extra="stuff")
((), {'a': 1, 'b': 2, 'c': 3, 'd': 4}, {'extra': 'stuff'})
```

But sometimes you do have POSITION_ONLY arguments.
What extract_arguments will do for you is return the value of these as the first
element of
the triple.

```pycon
>>> def f(a, b, c=None, /, d=0):
...     ...
...
>>> extract_arguments(f, b=2, a=1, c=3, d=4, extra="stuff")
((1, 2, 3), {'d': 4}, {'extra': 'stuff'})
```

Note above how we get `(1, 2, 3)`, the order defined by the func’s signature,
instead of `(2, 1, 3)`, the order defined by the kwargs.
So it’s the params (e.g. function signature) that determine the order, not kwargs.
When using to call a function, this is especially crucial if we use POSITION_ONLY
arguments.

See also that the third output, the remaining_kwargs, as `{'extra': 'stuff'}` since
it was not in the params of the function.
Even if you include a VAR_KEYWORD kind of argument in the function, it won’t change
this behavior.

```pycon
>>> def f(a, b, c=None, /, d=0, **kws):
...     ...
...
>>> extract_arguments(f, b=2, a=1, c=3, d=4, extra="stuff")
((1, 2, 3), {'d': 4}, {'extra': 'stuff'})
```

This is because we don’t want to assume that all the kwargs can actually be
included in a call to the function behind the params.
Instead, the user can chose whether to include the remainder by doing a:

```text
param_kwargs.update(remaining_kwargs)
```

et voilà.

That said, we do understand that it may be a common pattern, so we’ll do that
extra step for you
if you specify `include_all_when_var_keywords_in_params=True`.

```pycon
>>> def f(a, b, c=None, /, d=0, **kws):
...     ...
...
>>> extract_arguments(
...     f,
...     b=2,
...     a=1,
...     c=3,
...     d=4,
...     extra="stuff",
...     include_all_when_var_keywords_in_params=True,
... )
((1, 2, 3), {'d': 4, 'extra': 'stuff'}, {})
```

If you’re expecting no remainder you might want to just get the args and kwargs (
not this third
expected-to-be-empty remainder). You have two ways to do that, specifying:

- `what_to_do_with_remainding='ignore'`, which will just return the (args,
  kwargs) pair
- `what_to_do_with_remainding='assert_empty'`, which will do the same, but first
  assert the remainder is empty

We suggest to use `functools.partial` to configure the `argument_argument` you need.

```pycon
>>> from functools import partial
>>> arg_extractor = partial(
...     extract_arguments,
...     what_to_do_with_remainding="assert_empty",
...     include_all_when_var_keywords_in_params=True,
... )
>>> def f(a, b, c=None, /, d=0, **kws):
...     ...
...
>>> arg_extractor(f, b=2, a=1, c=3, d=4, extra="stuff")
((1, 2, 3), {'d': 4, 'extra': 'stuff'})
```

And what happens if the kwargs doesn’t contain all the POSITION_ONLY arguments?

```pycon
>>> def f(a, b, c=None, /, d=0):
...     ...
...
>>> extract_arguments(f, b=2, d="is a kw arg", e="is not an arg at all")
((MissingArgValFor("a"), 2, MissingArgValFor("c")), {'d': 'is a kw arg'}, {'e': 'is not an arg at all'})
```

A few more examples…

Let’s call `extract_arguments` with params being not a function,
but, a Signature instance, a mapping whose values are Parameter instances,
or an iterable of Parameter instances…

```pycon
>>> def func(a, b, /, c=None, *, d=0, **kws):
...     ...
...
>>> sig = Signature.from_callable(func)
>>> param_map = sig.parameters
>>> param_iterable = param_map.values()
>>> kwargs = dict(b=2, a=1, c=3, d=4, extra="stuff")
>>> assert extract_arguments(sig, **kwargs) == extract_arguments(func, **kwargs)
>>> assert extract_arguments(param_map, **kwargs) == extract_arguments(
...     func, **kwargs
... )
>>> assert extract_arguments(param_iterable, **kwargs) == extract_arguments(
...     func, **kwargs
... )
```

Edge case:
No params specified? No problem. You’ll just get empty args and kwargs. Everything
in the remainder

```pycon
>>> extract_arguments(params=(), b=2, a=1, c=3, d=0)
((), {}, {'b': 2, 'a': 1, 'c': 3, 'd': 0})
```

* **Parameters:**
  * **params** – Specifies what PO arguments should be extracted.
    Could be a callable, Signature, iterable of Parameters…
  * **what_to_do_with_remainding** – ‘return’ (default): function will return `param_args`, `param_kwargs`,
    `remaining_kwargs`
    ‘ignore’: function will return `param_args`, `param_kwargs`
    ‘assert_empty’: function will assert that `remaining_kwargs` is empty and then
    return `param_args`, `param_kwargs`
  * **include_all_when_var_keywords_in_params** – If True, and the params have a
    VAR_KEYWORD kind, include all kwargs in `param_kwargs` (nothing remains).
  * **assert_no_missing_position_only_args** – If True, raise an `AssertionError`
    when a POSITION_ONLY param has no matching kwarg.
  * **kwargs** – The kwargs to extract the args from
* **Returns:**
  A (param_args, param_kwargs, remaining_kwargs) tuple.

### i2.signatures.extract_arguments_ignoring_remainder(params, , what_to_do_with_remainding='ignore', include_all_when_var_keywords_in_params=False, assert_no_missing_position_only_args=False, \*\*kwargs)

Extract arguments needed to satisfy the params of a callable, dealing with the
dirty details.

Returns an (param_args, param_kwargs, remaining_kwargs) tuple where

- param_args are the values of kwargs that are PO (POSITION_ONLY) as defined by
  params,
- param_kwargs are those names that are both in params and not in param_args, and
- remaining_kwargs are the remaining.

Intended usage: When you need to call a function `func` that has some
position-only arguments,
but you have a kwargs dict of arguments in your hand. You can’t just to `func(
**kwargs)`.
But you can (now) do

```text
args, kwargs, remaining = extract_arguments(kwargs, func)  # extract from kwargs
what you need for func
# ... check if remaing is empty (or not, depending on your paranoia), and then
call the func:
func(*args, **kwargs)
```

(And if you doing that a lot: Do put it in a decorator!)

#### SEE ALSO
extract_arguments.without_remainding

The most frequent case you’ll encounter is when there’s no POSITION_ONLY args,
your param_args will be empty
and you param_kwargs will contain all the arguments that match params,
in the order of these params.

```pycon
>>> from inspect import signature
>>> def f(a, b, c=None, d=0):
...     ...
...
>>> extract_arguments(f, b=2, a=1, c=3, d=4, extra="stuff")
((), {'a': 1, 'b': 2, 'c': 3, 'd': 4}, {'extra': 'stuff'})
```

But sometimes you do have POSITION_ONLY arguments.
What extract_arguments will do for you is return the value of these as the first
element of
the triple.

```pycon
>>> def f(a, b, c=None, /, d=0):
...     ...
...
>>> extract_arguments(f, b=2, a=1, c=3, d=4, extra="stuff")
((1, 2, 3), {'d': 4}, {'extra': 'stuff'})
```

Note above how we get `(1, 2, 3)`, the order defined by the func’s signature,
instead of `(2, 1, 3)`, the order defined by the kwargs.
So it’s the params (e.g. function signature) that determine the order, not kwargs.
When using to call a function, this is especially crucial if we use POSITION_ONLY
arguments.

See also that the third output, the remaining_kwargs, as `{'extra': 'stuff'}` since
it was not in the params of the function.
Even if you include a VAR_KEYWORD kind of argument in the function, it won’t change
this behavior.

```pycon
>>> def f(a, b, c=None, /, d=0, **kws):
...     ...
...
>>> extract_arguments(f, b=2, a=1, c=3, d=4, extra="stuff")
((1, 2, 3), {'d': 4}, {'extra': 'stuff'})
```

This is because we don’t want to assume that all the kwargs can actually be
included in a call to the function behind the params.
Instead, the user can chose whether to include the remainder by doing a:

```text
param_kwargs.update(remaining_kwargs)
```

et voilà.

That said, we do understand that it may be a common pattern, so we’ll do that
extra step for you
if you specify `include_all_when_var_keywords_in_params=True`.

```pycon
>>> def f(a, b, c=None, /, d=0, **kws):
...     ...
...
>>> extract_arguments(
...     f,
...     b=2,
...     a=1,
...     c=3,
...     d=4,
...     extra="stuff",
...     include_all_when_var_keywords_in_params=True,
... )
((1, 2, 3), {'d': 4, 'extra': 'stuff'}, {})
```

If you’re expecting no remainder you might want to just get the args and kwargs (
not this third
expected-to-be-empty remainder). You have two ways to do that, specifying:

- `what_to_do_with_remainding='ignore'`, which will just return the (args,
  kwargs) pair
- `what_to_do_with_remainding='assert_empty'`, which will do the same, but first
  assert the remainder is empty

We suggest to use `functools.partial` to configure the `argument_argument` you need.

```pycon
>>> from functools import partial
>>> arg_extractor = partial(
...     extract_arguments,
...     what_to_do_with_remainding="assert_empty",
...     include_all_when_var_keywords_in_params=True,
... )
>>> def f(a, b, c=None, /, d=0, **kws):
...     ...
...
>>> arg_extractor(f, b=2, a=1, c=3, d=4, extra="stuff")
((1, 2, 3), {'d': 4, 'extra': 'stuff'})
```

And what happens if the kwargs doesn’t contain all the POSITION_ONLY arguments?

```pycon
>>> def f(a, b, c=None, /, d=0):
...     ...
...
>>> extract_arguments(f, b=2, d="is a kw arg", e="is not an arg at all")
((MissingArgValFor("a"), 2, MissingArgValFor("c")), {'d': 'is a kw arg'}, {'e': 'is not an arg at all'})
```

A few more examples…

Let’s call `extract_arguments` with params being not a function,
but, a Signature instance, a mapping whose values are Parameter instances,
or an iterable of Parameter instances…

```pycon
>>> def func(a, b, /, c=None, *, d=0, **kws):
...     ...
...
>>> sig = Signature.from_callable(func)
>>> param_map = sig.parameters
>>> param_iterable = param_map.values()
>>> kwargs = dict(b=2, a=1, c=3, d=4, extra="stuff")
>>> assert extract_arguments(sig, **kwargs) == extract_arguments(func, **kwargs)
>>> assert extract_arguments(param_map, **kwargs) == extract_arguments(
...     func, **kwargs
... )
>>> assert extract_arguments(param_iterable, **kwargs) == extract_arguments(
...     func, **kwargs
... )
```

Edge case:
No params specified? No problem. You’ll just get empty args and kwargs. Everything
in the remainder

```pycon
>>> extract_arguments(params=(), b=2, a=1, c=3, d=0)
((), {}, {'b': 2, 'a': 1, 'c': 3, 'd': 0})
```

* **Parameters:**
  * **params** – Specifies what PO arguments should be extracted.
    Could be a callable, Signature, iterable of Parameters…
  * **what_to_do_with_remainding** – ‘return’ (default): function will return `param_args`, `param_kwargs`,
    `remaining_kwargs`
    ‘ignore’: function will return `param_args`, `param_kwargs`
    ‘assert_empty’: function will assert that `remaining_kwargs` is empty and then
    return `param_args`, `param_kwargs`
  * **include_all_when_var_keywords_in_params** – If True, and the params have a
    VAR_KEYWORD kind, include all kwargs in `param_kwargs` (nothing remains).
  * **assert_no_missing_position_only_args** – If True, raise an `AssertionError`
    when a POSITION_ONLY param has no matching kwarg.
  * **kwargs** – The kwargs to extract the args from
* **Returns:**
  A (param_args, param_kwargs, remaining_kwargs) tuple.

### i2.signatures.flatten_if_var_kw(kvs, var_kw_name)

Yield `(key, value)` pairs, replacing a `(var_kw_name, {var_kw_name: d})` pair by the items of `d`.

### i2.signatures.function_caller(func, args, kwargs)

Call `func(*args, **kwargs)`; the default “caller” of the wrapping tools.

### i2.signatures.has_signature(obj, robust=False)

Check if an object has a signature – i.e. is callable and inspect.signature(
obj) returns something.

This can be used to more easily get signatures in bulk without having to write
try/catches:

```pycon
>>> from functools import partial
>>> len(
...     list(
...         filter(
...             None,
...             map(
...                 partial(has_signature, robust=False),
...                 (Sig, print, map, filter, Sig.wrap),
...             ),
...         )
...     )
... )
2
```

If robust is set to True, `has_signature` will use `Sig` to get the signature,
so will return True in most cases.

### i2.signatures.ignore_any_differences(x, y)

A comparator that always returns `True` (used to ignore a parameter attribute).

### i2.signatures.insert_annotations(s, , , return_annotation, \*\*annotations)

Insert annotations in a signature.
(Note: not really insert but returns a copy of input signature)

```pycon
>>> from inspect import signature
>>> s = signature(lambda a, b, c=1, d="bar": 0)
>>> s
<Signature (a, b, c=1, d='bar')>
>>> ss = insert_annotations(s, b=int, d=str)
>>> ss
<Signature (a, b: int, c=1, d: str = 'bar')>
>>> insert_annotations(s, b=int, d=str, e=list)
Traceback (most recent call last):
...
AssertionError: These argument names weren't found in the signature: {'e'}
```

### i2.signatures.is_call_compatible_with(sig1, sig2, , param_comparator=None)

Return True if `sig1` is compatible with `sig2`. Meaning that all valid ways
to call `sig1` are valid for `sig2`.

* **Parameters:**
  * **sig1** ([`Sig`](_autosummary/i2.signatures.html.md#i2.signatures.Sig)) – The main signature.
  * **sig2** ([`Sig`](_autosummary/i2.signatures.html.md#i2.signatures.Sig)) – The signature to be compared with.
  * **param_comparator** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter), [`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – The function used to compare two parameters
* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

```pycon
>>> is_call_compatible_with(
...     Sig('(a, /, b, *, c)'),
...     Sig('(a, b, c)')
... )
True
>>> is_call_compatible_with(
...     Sig('()'),
...     Sig('(a)')
... )
False
>>> is_call_compatible_with(
...     Sig('()'),
...     Sig('(a=0)')
... )
True
>>> is_call_compatible_with(
...     Sig('(a, /, *, c)'),
...     Sig('(a, /, b, *, c)')
... )
False
>>> is_call_compatible_with(
...     Sig('(a, /, *, c)'),
...     Sig('(a, /, b=0, *, c)')
... )
True
>>> is_call_compatible_with(
...     Sig('(a, /, b)'),
...     Sig('(a, /, b, *, c)')
... )
False
>>> is_call_compatible_with(
...     Sig('(a, /, b)'),
...     Sig('(a, /, b, *, c=0)')
... )
True
>>> is_call_compatible_with(
...     Sig('(a, /, b, *, c)'),
...     Sig('(*args, **kwargs)')
... )
True
```

### i2.signatures.is_signature_error(e)

Check if an exception is a signature error

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

### i2.signatures.keyed_comparator(comparator, key)

Create a key-function enabled binary operator.

In various places in python functionality is extended by allowing a key function.
For example, the `sorted` function allows a key function to be passed, which is
applied to each element before sorting. The keyed_comparator function allows a
comparator to be extended in the same way. The returned comparator will apply the
key function toeach input before applying the original comparator.

* **Return type:**
  [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]

```pycon
>>> from operator import eq
>>> parity = lambda x: x % 2
>>> comparator = keyed_comparator(eq, parity)
>>> list(map(comparator, [1, 1, 2, 2], [3, 4, 5, 6]))
[True, False, False, True]
```

### i2.signatures.kind_forgiving_func(func, kinds_modifier=<function convert_to_PK>)

Wraps the func, changing the argument kinds according to kinds_modifier.
The default behaviour is to change all kinds to POSITIONAL_OR_KEYWORD kinds.
The original purpose of this function is to remove argument-kind restriction
annoyances when doing functional manipulations such as:

```pycon
>>> from functools import partial
>>> isinstance_of_str = partial(isinstance, class_or_tuple=str)
>>> isinstance_of_str('I am a string')
Traceback (most recent call last):
  ...
TypeError: isinstance() takes no keyword arguments
```

Here, instead, we can just get a kinder version of the function and do what we
want to do:

```pycon
>>> _isinstance = kind_forgiving_func(isinstance)
>>> isinstance_of_str = partial(_isinstance, class_or_tuple=str)
>>> isinstance_of_str('I am a string')
True
>>> isinstance_of_str(42)
False
```

#### SEE ALSO
`i2.signatures.all_pk_signature`

### i2.signatures.maybe_first(items)

The first item of `items`, or `None` if there is none.

### i2.signatures.mk_func_comparator_based_on_signature_comparator(signature_comparator)

Make a function comparator that compares two callables through their signatures.

* **Return type:**
  [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable), [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]

### i2.signatures.mk_sig_from_args(\*args_without_default, \*\*args_with_defaults)

Make a Signature instance by specifying args_without_default and
args_with_defaults.

```pycon
>>> mk_sig_from_args("a", "b", c=1, d="bar")
<Signature (a, b, c=1, d='bar')>
```

### i2.signatures.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.signatures.name_of_var_kw_argument(sig)

The name of the VAR_KEYWORD parameter of `sig`, or `None` if it has none.

### i2.signatures.normalized_func(func)

Wrap `func` so its call arguments are re-bound through `func`’s own signature.

Work in progress: the wrapper has a `(*args, **kwargs)` signature and the tests
that would let it relax argument kinds are marked `xfail`.

### i2.signatures.param_attribute_dict(name_kind_default_annotation)

Zip four comparison results into a `{name, kind, default, annotation}` dict (an `aggreg`).

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

### i2.signatures.param_binary_func(param1, param2, \*, name=<built-in function eq>, kind=<built-in function eq>, default=<built-in function eq>, annotation=<built-in function eq>, aggreg=<built-in function all>)

Compare two parameters.

Note that by default, this function is strict, and will return False if
any of the parameters are not equal. This is because the default
aggregation function is `all` and the default comparison functions of the
parameter’s attributes are `eq` (meaning equality, not identity).

But you can change that by passing different comparison functions and/or
aggregation functions.

In fact, the real purpose of this function is to be used as a factory of parameter
binary functions, through parametrizing it with `functools.partial`.

The parameter binary functions themselves are meant to be used to make signature
binary functions.

* **Parameters:**
  * **param1** ([`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)) – first parameter
  * **param2** ([`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)) – second parameter
  * **name** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]) – function to compare names
  * **kind** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]) – function to compare kinds
  * **default** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]) – function to compare defaults
  * **annotation** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]) – function to compare annotations
  * **aggreg** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – function to aggregate results
* **Return type:**
  [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)

```pycon
>>> from inspect import Parameter
>>> param1 = Parameter('x', Parameter.POSITIONAL_OR_KEYWORD)
>>> param2 = Parameter('x', Parameter.POSITIONAL_OR_KEYWORD)
>>> param_binary_func(param1, param2)
True
```

See [https://github.com/i2mint/i2/issues/50#issuecomment-1381686812](https://github.com/i2mint/i2/issues/50#issuecomment-1381686812) for discussion.

### i2.signatures.param_comparator(param1, param2, \*, name=<built-in function eq>, kind=<built-in function eq>, default=<built-in function eq>, annotation=<built-in function eq>, aggreg=<built-in function all>)

Compare two parameters.

Note that by default, this function is strict, and will return False if
any of the parameters are not equal. This is because the default
aggregation function is `all` and the default comparison functions of the
parameter’s attributes are `eq` (meaning equality, not identity).

But you can change that by passing different comparison functions and/or
aggregation functions.

In fact, the real purpose of this function is to be used as a factory of parameter
binary functions, through parametrizing it with `functools.partial`.

The parameter binary functions themselves are meant to be used to make signature
binary functions.

* **Parameters:**
  * **param1** ([`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)) – first parameter
  * **param2** ([`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)) – second parameter
  * **name** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]) – function to compare names
  * **kind** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]) – function to compare kinds
  * **default** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]) – function to compare defaults
  * **annotation** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`), [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Compared`)], [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]) – function to compare annotations
  * **aggreg** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)]], [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)]) – function to aggregate results
* **Return type:**
  [`TypeVar`](https://docs.python.org/3/library/typing.html#typing.TypeVar)(`Comparison`)

```pycon
>>> from inspect import Parameter
>>> param1 = Parameter('x', Parameter.POSITIONAL_OR_KEYWORD)
>>> param2 = Parameter('x', Parameter.POSITIONAL_OR_KEYWORD)
>>> param_binary_func(param1, param2)
True
```

See [https://github.com/i2mint/i2/issues/50#issuecomment-1381686812](https://github.com/i2mint/i2/issues/50#issuecomment-1381686812) for discussion.

### i2.signatures.param_comparison_dict(param1, param2, \*, name=<function return_tuple>, kind=<function return_tuple>, default=<function return_tuple>, annotation=<function return_tuple>, aggreg=<function param_attribute_dict>)

A ParamComparator that returns a dictionary with pairs parameter attributes.

```pycon
>>> param1 = Sig('(a: int = 1)')['a']
>>> param2 = Sig('(a: str = 2)')['a']
>>> param_comparison_dict(param1, param2)
{'name': ('a', 'a'), 'kind': ..., 'default': (1, 2), 'annotation': (<class 'int'>, <class 'str'>)}
```

* **Return type:**
  *Comparison*

### i2.signatures.param_differences_dict(param1, param2, \*, name=<built-in function eq>, kind=<built-in function eq>, default=<built-in function eq>, annotation=<built-in function eq>)

Makes a dictionary exibiting the differences between two parameters.

```pycon
>>> param1 = Sig('(a: int = 1)')['a']
>>> param2 = Sig('(a: str = 2)')['a']
>>> param_differences_dict(param1, param2)
{'default': (1, 2), 'annotation': (<class 'int'>, <class 'str'>)}
>>> param_differences_dict(param1, param2, default=lambda x, y: isinstance(x, type(y)))
{'annotation': (<class 'int'>, <class 'str'>)}
```

### i2.signatures.param_for_kind(name=None, kind='positional_or_keyword', with_default=False, annotation)

Make an `inspect.Parameter` of a given kind, with a generated name and default if not given (handy in tests).

It’s annoying to have to compose parameters from scratch to testing things.
This tool should help making it less annoying.

```pycon
>>> list(map(param_for_kind, param_kinds))
[<Parameter "POSITIONAL_ONLY">, <Parameter "POSITIONAL_OR_KEYWORD">, <Parameter "VAR_POSITIONAL">, <Parameter "KEYWORD_ONLY">, <Parameter "VAR_KEYWORD">]
>>> param_for_kind.positional_or_keyword()
<Parameter "POSITIONAL_OR_KEYWORD">
>>> param_for_kind.positional_or_keyword("foo")
<Parameter "foo">
>>> param_for_kind.keyword_only()
<Parameter "KEYWORD_ONLY">
>>> param_for_kind.keyword_only("baz", with_default=True)
<Parameter "baz='dflt_keyword_only'">
```

### i2.signatures.param_has_default_or_is_var_kind(p)

Whether the parameter is optional in a call: it has a default or is variadic.

### i2.signatures.parameter_to_dict(p)

The `name`, `kind`, `default` and `annotation` of a parameter, as a dict.

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

### i2.signatures.params_of(obj)

The list of `Parameter` objects of a signature, a name-to-parameter mapping, or a callable.

```pycon
>>> params_of(lambda a, b=1: None)
[<Parameter "a">, <Parameter "b=1">]
```

### i2.signatures.permissive_param_comparator(param1, param2, \*, name=<function ignore_any_differences>, kind=<function ignore_any_differences>, default=<function ignore_any_differences>, annotation=<function ignore_any_differences>, aggreg=<built-in function all>)

Permissive version of param_comparator that ignores any differences of parameter
attributes.

It is meant to be used with partial, but with a permissive base, contrary to the
base param_comparator which requires strict equality (`eq`) for all attributes.

* **Return type:**
  *Comparison*

### i2.signatures.postprocess(egress)

Make a decorator that applies `egress` to the output of the wrapped function.

```pycon
>>> @postprocess(list)
... def r(n):
...     return range(n)
>>> r(3)
[0, 1, 2]
```

### i2.signatures.replace_kwargs_using(sig)

Decorator that replaces the variadic keyword argument of the target function using
the `sig`, the signature of a source function.
It essentially injects the difference between `sig` and the target function’s
signature into the target function’s signature. That is, it replaces the
variadic keyword argument (a.k.a. “kwargs”) with those parameters that are in `sig`
but not in the target function’s signature.

This is meant to be used when a `targ_func` (the function you’ll apply the
decorator to) has a variadict keyword argument that is just used to forward “extra”
arguments to another function, and you want to make sure that the signature of the
`targ_func` is consistent with the `sig` signature.
(Also, you don’t want to copy the signatures around manually.)

In the following, `sauce` (the target function) has a variadic keyword argument,
`sauce_kwargs`, that is used to forward extra arguments to `apple` (the source
function).

```pycon
>>> def apple(a, x: int, y=2, *, z=3, **extra_apple_options):
...     return a + x + y + z
>>> @replace_kwargs_using(apple)
... def sauce(a, b, c, **sauce_kwargs):
...     return b * c + apple(a, **sauce_kwargs)
```

The function will works:

```pycon
>>> sauce(1, 2, 3, x=4, z=5)  # func still works? Should be: 1 + 4 + 2 + 5 + 2 * 3
18
```

But the signature now doesn’t have the `**sauce_kwargs`, but more informative
signature elements sourced from `apple`:

```pycon
>>> Sig(sauce)
<Sig (a, b, c, *, x: int, y=2, z=3, **extra_apple_options)>
```

One thing to note is that the order of the arguments in the signature of `apple`
may change to accomodate for the python parameter order rules
(see [https://docs.python.org/3/reference/compound_stmts.html#function-definitions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)).
The new order will try to conserve the order of the original arguments of `sauce`
in-so-far as it doesn’t violate the python parameter order rules, though.
See examples below:

```pycon
>>> @Sig.replace_kwargs_using(apple)
... def sauce(a, b=2, c=3, **sauce_kwargs):
...     return b * c + apple(a, **sauce_kwargs)
>>> Sig(sauce)
<Sig (a, b=2, c=3, *, x: int, y=2, z=3, **extra_apple_options)>
```

```pycon
>>> @Sig.replace_kwargs_using(apple)
... def sauce(a=1, b=2, c=3, **sauce_kwargs):
...     return b * c + apple(a, **sauce_kwargs)
>>> Sig(sauce)
<Sig (a=1, b=2, c=3, *, x: int, y=2, z=3, **extra_apple_options)>
```

### i2.signatures.resolve_function(obj)

Get the underlying function of a property or cached_property

Note that if all conditions fail, the object itself is returned.

The problem this function solves is that sometimes there’s a function behind an
object, but it’s not always easy to get to it. For example, in a class, you might
want to get the source of the code decorated with `@property`, a
`@cached_property`, or a `partial` function.

Consider the following example:

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

```pycon
>>> from functools import cached_property, partial
>>> class C:
...     @property
...     def prop(self):
...         pass
...     @cached_property
...     def cached_prop(self):
...         pass
...     partial_func = partial(partial)
```

Note that `prop` is not callable, and you can’t get its source.

```pycon
>>> import inspect
>>> callable(C.prop)
False
>>> inspect.getsource(C.prop)
Traceback (most recent call last):
...
TypeError: <property object at 0x...> is not a module, class, method, function, traceback, frame, or code object
```

But if you grab the underlying function, you can get the source:

```pycon
>>> func = resolve_function(C.prop)
>>> callable(func)
True
>>> isinstance(inspect.getsource(func), str)
True
```

Same goes with `cached_property` and `partial`:

```pycon
>>> isinstance(inspect.getsource(resolve_function(C.cached_prop)), str)
True
>>> isinstance(inspect.getsource(resolve_function(C.partial_func)), str)
True
```

### i2.signatures.return_tuple(x, y)

A comparator that returns the `(x, y)` pair itself instead of a verdict.

### i2.signatures.set_signature_of_func(func, parameters, , return_annotation, \_\_validate_parameters_\_=True)

Set the signature of a function, with sugar.

* **Parameters:**
  * **func** – Function whose signature you want to set
  * **parameters** – A list of parameter specifications: `inspect.Parameter` objects
    or anything `ensure_param` can resolve into one.
  * **return_annotation** – Passed on to inspect.Signature.
  * **\_\_validate_parameters_\_** – Passed on to inspect.Signature.
* **Returns:**
  None (but sets the signature of the input function)

```pycon
>>> import inspect
>>> def foo(*args, **kwargs):
...     pass
...
>>> inspect.signature(foo)
<Signature (*args, **kwargs)>
>>> set_signature_of_func(foo, ["a", "b", "c"])
>>> inspect.signature(foo)
<Signature (a, b, c)>
>>> set_signature_of_func(
...     foo, ["a", ("b", None), ("c", 42, int)]
... )  # specifying defaults and annotations
>>> inspect.signature(foo)
<Signature (a, b=None, c: int = 42)>
>>> set_signature_of_func(
...     foo, ["a", "b", "c"], return_annotation=str
... )  # specifying return annotation
>>> inspect.signature(foo)
<Signature (a, b, c) -> str>
>>> # But you can always specify parameters the "long" way
>>> set_signature_of_func(
...     foo,
...     [inspect.Parameter(name="kws", kind=inspect.Parameter.VAR_KEYWORD)],
...     return_annotation=str,
... )
>>> inspect.signature(foo)
<Signature (**kws) -> str>
```

### i2.signatures.sig_to_dataclass(sig, , cls_name=None, bases=(), module=None, \*\*kwargs)

Make a `class` (through `make_dataclass`) from the given signature.

* **Parameters:**
  * **sig** (`Union`[[`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature), [`Iterable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable)[[`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Parameter`](https://docs.python.org/3/library/inspect.html#inspect.Parameter)], [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A `SignatureAble`, that is, anything that ensure_signature can
    resolve into an `inspect.Signature` object, including a signature object
    itself, but also most callables, a list or params, etc.
  * **cls_name** – The same as `cls_name` of `dataclasses.make_dataclass`
  * **bases** – The same as `bases` of `dataclasses.make_dataclass`
  * **module** – Set to module (usually `__name__` to specify ther module of
    caller) so that the class and instances can be pickle-able.
  * **kwargs** – Passed on to `dataclasses.make_dataclass`
* **Returns:**
  A dataclass

```pycon
>>> def foo(a, /, b : int=2, *, c=3):
...     pass
...
>>> K = sig_to_dataclass(foo, cls_name='K')
>>> str(Sig(K))
'(a, b: int = 2, c=3) -> None'
>>> k = K(1,2,3)
>>> (k.a, k.b, k.c)
(1, 2, 3)
```

Would also work with any of these (and more):

```pycon
>>> K = sig_to_dataclass(Sig(foo), cls_name='K')
>>> K = sig_to_dataclass(Sig(foo).params, cls_name='K')
```

#### NOTE
`cls_name` is not required (we’ll try to figure out a good default for you),
but it’s advised to only use this convenience in extreme mode.
Choosing your own name might make for a safer future if you’re reusing your class.

### i2.signatures.sort_params(params)

* **Parameters:**
  **params** – An iterable of `Parameter` instances
* **Returns:**
  A list of these instances sorted so as to obey the `kind` and `default`
  order rules of python signatures.

Note 1: It doesn’t mean that these params constitute a valid signature together,
since it doesn’t verify rules like unicity of names and variadic kinds.

Note 2: Though you can use `sorted` on an iterable of `i2.signatures.Param`
instances, know that even for sorting the three parameters below,
the `sort_params` function is more than twice as fast.

```pycon
>>> from inspect import Parameter
>>> sort_params(
...     [Parameter('a', kind=Parameter.POSITIONAL_OR_KEYWORD, default=1),
...     Parameter('b', kind=Parameter.POSITIONAL_ONLY),
...     Parameter('c', kind=Parameter.POSITIONAL_OR_KEYWORD)]
... )
[<Parameter "b">, <Parameter "c">, <Parameter "a=1">]
```

### i2.signatures.tuple_the_args(func, , ch_variadic_keyword_to_keyword=False)

A decorator that will change a VAR_POSITIONAL (\*args) argument to a tuple (args)
argument of the same name.

### i2.signatures.use_interface(interface_sig)

Use interface_sig as (enforced/validated) signature of the decorated function.
That is, the decorated function will use the original function has the backend,
the function actually doing the work, but with a frontend specified
(in looks and in argument validation) `interface_sig`

consider the situation where are functionality is parametrized by a
function `g` taking two inputs, `a`, and `b`.
Now you want to carry out this functionality using a function `f` that does what
`g` should do, but doesn’t use `a`, and doesn’t even have it in it’s arguments.

The solution to this is to \_adapt_ `f` to the `g` interface:

```text
def my_g(a, b):
    return f(a)
```

and use `my_g`.

```pycon
>>> f = lambda a: a * 11
>>> interface = lambda a, b=None: ...
>>>
>>> new_f = use_interface(interface)(f)
```

See how only the first argument, or `a` keyword argument, is taken into account
in `new_f`:

```pycon
>>> assert new_f(2) == f(2)
>>> assert new_f(2, 3) == f(2)
>>> assert new_f(2, b=3) == f(2)
>>> assert new_f(b=3, a=2) == f(2)
```

But if we add more positional arguments than `interface` allows,
or any keyword arguments that `interface` doesn’t recognize…

```pycon
>>> new_f(1,2,3)
Traceback (most recent call last):
  ...
TypeError: too many positional arguments
>>> new_f(1, c=2)
Traceback (most recent call last):
  ...
TypeError: got an unexpected keyword argument 'c'
```

### i2.signatures.validate_signature(func)

Validates the signature of a function.

* **Return type:**
  [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)

```pycon
>>> @validate_signature
... def has_valid_signature(x=Sig.empty, y=2):
...     pass
>>> # all good, no errors raised
>>>
>>> @validate_signature
... def does_no_have_valid_signature(x=2, y=Sig.empty):
...     pass
Traceback (most recent call last):
...
i2.signatures.InvalidSignature: Invalid signature for function <function does_no_have_valid_signature at 0x106a72a70>: non-default argument follows default a
rgument
```


# _autosummary/i2.util.html.md

# i2.util

Misc util objects

### Functions

| [`FileLikeObject`](_autosummary/i2.util.html.md#i2.util.FileLikeObject)(file, \*[, io_cls, open_mode])     | Context manager for file-like objects.                                                                                                                            |
|----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`asis`](_autosummary/i2.util.html.md#i2.util.asis)(x)                                           | The identity function: f(x) := x (takes only one argument, and returns it).                                                                                       |
| [`copy_func`](_autosummary/i2.util.html.md#i2.util.copy_func)(func, \*[, copy_dict, code, globals_])  | Make a (shallow) copy of a function.                                                                                                                              |
| [`deprecation_of`](_autosummary/i2.util.html.md#i2.util.deprecation_of)(func, old_name)                    | Wrap `func` so that each call emits a DeprecationWarning naming `old_name`.                                                                                       |
| [`dflt_idx_preprocessor`](_autosummary/i2.util.html.md#i2.util.dflt_idx_preprocessor)(obj, idx)                   | Get `idx` from `obj`: by item for ints, digit strings and Mappings, else by attribute.                                                                            |
| [`dp_get`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#i2.util.ensure_identifiers)(\*objs[, get_identfiers, ...]) | Ensure an iterable of identifiers                                                                                                                                 |
| [`get_app_folder`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#i2.util.get_function_body)(func)                           | Get the body of a function as a (dedented) string, from its source code.                                                                                          |
| [`ignore_exception`](_autosummary/i2.util.html.md#i2.util.ignore_exception)(x)                               | The identity function: f(x) := x (takes only one argument, and returns it).                                                                                       |
| [`inject_method`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#i2.util.inspect_formatargspec)(args[, varargs, ...])       | Copy formatargspec from python 3.7 standard library.                                                                                                              |
| [`is_lambda`](_autosummary/i2.util.html.md#i2.util.is_lambda)(func)                                   | Whether `func` is a lambda (its `__name__` is `"<lambda>"`).                                                                                                      |
| [`lambda_code`](_autosummary/i2.util.html.md#i2.util.lambda_code)(lambda_func)                          | Extract code of expression from lambda function.                                                                                                                  |
| [`mk_sentinel`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#i2.util.register_object)([obj, name])                      | Register an object (e.g. function, class) in the global registry.                                                                                                 |
| [`return_false`](_autosummary/i2.util.html.md#i2.util.return_false)(\*args, \*\*kwargs)                  | Return False, whatever the arguments.                                                                                                                             |
| [`return_none`](_autosummary/i2.util.html.md#i2.util.return_none)(\*args, \*\*kwargs)                   | Return None, whatever the arguments.                                                                                                                              |
| [`return_true`](_autosummary/i2.util.html.md#i2.util.return_true)(\*args, \*\*kwargs)                   | Return True, whatever the arguments.                                                                                                                              |

### Classes

| [`AttributeMapping`](_autosummary/i2.util.html.md#i2.util.AttributeMapping)                             | A read-only mapping with attribute access.                                                                                                                                                                         |
|-----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`AttributeMutableMapping`](_autosummary/i2.util.html.md#i2.util.AttributeMutableMapping)                      | A mutable mapping that provides both attribute and dictionary-style access.                                                                                                                                        |
| [`ConditionalExceptionCatcher`](_autosummary/i2.util.html.md#i2.util.ConditionalExceptionCatcher)(exception_types) | Context manager to catch exceptions of a certain type and instance condition.                                                                                                                                      |
| [`FolderSpec`](_autosummary/i2.util.html.md#i2.util.FolderSpec)(env_var, default_path)            |                                                                                                                                                                                                                    |
| [`FrozenDict`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#i2.util.LiteralVal)(val)                              | An object to indicate that the value should be considered literally.                                                                                                                                               |
| [`NoDefault`](_autosummary/i2.util.html.md#i2.util.NoDefault)()                                  | Type of the `no_default` sentinel, marking the absence of a default value.                                                                                                                                         |
| [`PicklableLambda`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#i2.util.frozendict)                                   |                                                                                                                                                                                                                    |
| [`imdict`](_autosummary/i2.util.html.md#i2.util.imdict)                                       | A dict whose mutating methods raise `TypeError`, hashable by identity.                                                                                                                                             |
| [`lazyprop`](_autosummary/i2.util.html.md#i2.util.lazyprop)(func)                               | A descriptor implementation of lazyprop (cached property) from David Beazley's "Python Cookbook" book.                                                                                                             |

### Exceptions

| [`ExistingArgument`](_autosummary/i2.util.html.md#i2.util.ExistingArgument)    | Raised by `FunctionBuilder.add_arg` when the argument name is already taken.     |
|----------------------------------------------------------------------|----------------------------------------------------------------------------------|
| [`FrozenHashError`](_autosummary/i2.util.html.md#i2.util.FrozenHashError)     | Raised (and cached) when a `frozendict` holds an unhashable value and is hashed. |
| [`MissingArgument`](_autosummary/i2.util.html.md#i2.util.MissingArgument)     | Raised by `FunctionBuilder.remove_arg` when the argument is not in the function. |
| [`OverwritesForbidden`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#i2.util.AttributeMapping)

### *class* i2.util.AttributeMutableMapping

Bases: [`AttributeMapping`](_autosummary/i2.util.html.md#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()`](_autosummary/i2.util.html.md#i2.util.FunctionBuilder.from_func) classmethod. At any
point, [`get_func()`](_autosummary/i2.util.html.md#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`](_autosummary/i2.util.html.md#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
```


# _autosummary/i2.wrapper.html.md

# i2.wrapper

A wrapper object and tools to work with it

How the `Wrap` class works:

```default
      *outer_args, **outer_kwargs
                 │
                 ▼
┌───────────────────────────────────┐
│              ingress              │
└───────────────────────────────────┘
                 │
                 ▼
      *inner_args, **inner_kwargs
                 │
                 ▼
┌───────────────────────────────────┐
│               func                │
└───────────────────────────────────┘
                 │
                 ▼
             func_output
                 │
                 ▼
┌───────────────────────────────────┐
│              egress               │
└───────────────────────────────────┘
                 │
                 ▼
            final_output
```

How the `Ingress` class (ingress templated function maker) works:

```default
      *outer_args, **outer_kwargs
                 │
                 ▼
┌───────────────────────────────────┐
│          outer_sig_bind           │
└───────────────────────────────────┘
                 │
                 ▼
          outer_all_kwargs
                 │
                 ▼
┌───────────────────────────────────┐
│            kwargs_trans           │
└───────────────────────────────────┘
                 │
                 ▼
          inner_all_kwargs
                 │
                 ▼
┌───────────────────────────────────┐
│          inner_sig_bind           │
└───────────────────────────────────┘
                 │
                 ▼
      *inner_args, **inner_kwargs
```

### Module Attributes

| [`AUTO_PRESERVE_SIGNATURE`](_autosummary/i2.wrapper.html.md#i2.wrapper.AUTO_PRESERVE_SIGNATURE)   | `preserve_signature` value meaning "decide per ingress" (see `_should_preserve_signature()`).   |
|----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|

### Functions

| `add_smart_defaults`([func, ...])                                                                   | Add smart defaults to function.                                                                                                           |
|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
| [`append_empty_args`](_autosummary/i2.wrapper.html.md#i2.wrapper.append_empty_args)(func)                            | To use to transform an ingress function that only returns kwargs to one that returns the normal form of ingress functions: ((), kwargs)   |
| [`apply_func_on_cond`](_autosummary/i2.wrapper.html.md#i2.wrapper.apply_func_on_cond)(func, cond, k, v)               | Return `func(v)` if `cond(k, v)` is true, else `v` unchanged.                                                                             |
| [`arg_val_converter`](_autosummary/i2.wrapper.html.md#i2.wrapper.arg_val_converter)(func, \*\*conversion_for_arg)    | Wrap `func` so that the given arguments are converted (`name=converter`) before the call.                                                 |
| [`arg_val_converter_ingress`](_autosummary/i2.wrapper.html.md#i2.wrapper.arg_val_converter_ingress)(func[, \_\_strict])      | Function form of `ArgValConverterIngress`: an ingress converting the named arguments.                                                     |
| [`bind_funcs_object_attrs`](_autosummary/i2.wrapper.html.md#i2.wrapper.bind_funcs_object_attrs)(funcs[, ...])              | Transform one or several functions into a class that contains them as methods sourcing specific arguments from the instance's attributes. |
| [`bind_funcs_object_attrs_old`](_autosummary/i2.wrapper.html.md#i2.wrapper.bind_funcs_object_attrs_old)(funcs[, ...])          | Transform one or several functions into a class that contains them as methods sourcing specific arguments from the instance's attributes. |
| [`camelize`](_autosummary/i2.wrapper.html.md#i2.wrapper.camelize)(s)                                        |                                                                                                                                           |
| `ch_names`([func])                                                                                  | Change the argument names of a function.                                                                                                  |
| [`complete_dict_applying_functions`](_autosummary/i2.wrapper.html.md#i2.wrapper.complete_dict_applying_functions)(d, /[, ...])      | Complete dict `d` by applying function to variables in `d`, sequentially.                                                                 |
| [`convert_VK_to_KO`](_autosummary/i2.wrapper.html.md#i2.wrapper.convert_VK_to_KO)(kinds)                            | In a `{name: kind}` dict, replace VAR_KEYWORD kinds with KEYWORD_ONLY.                                                                    |
| [`convert_dict_values`](_autosummary/i2.wrapper.html.md#i2.wrapper.convert_dict_values)(to_convert, ...)               | Yield `(key, value)` pairs of `to_convert`, converting the values whose key has a function.                                               |
| [`func_to_method_func`](_autosummary/i2.wrapper.html.md#i2.wrapper.func_to_method_func)(func[, instance_params, ...])  | Get a 'method function' from a 'normal function'.                                                                                         |
| [`identity`](_autosummary/i2.wrapper.html.md#i2.wrapper.identity)(x)                                        | Return the input unchanged.                                                                                                               |
| `include_exclude`([func, include, exclude])                                                         | Reorder and/or remove parameters.                                                                                                         |
| [`include_exclude_ingress_factory`](_autosummary/i2.wrapper.html.md#i2.wrapper.include_exclude_ingress_factory)(func[, ...])       | A pattern underlying any ingress that takes a subset of parameters (possibly reordering them).                                            |
| [`invert_map`](_autosummary/i2.wrapper.html.md#i2.wrapper.invert_map)(d)                                      | Swap keys and values of a mapping, raising `ValueError` if values are not unique.                                                         |
| [`items_with_mapped_keys`](_autosummary/i2.wrapper.html.md#i2.wrapper.items_with_mapped_keys)(d, key_mapper)              | Transform dict keys.                                                                                                                      |
| [`kwargs_trans`](_autosummary/i2.wrapper.html.md#i2.wrapper.kwargs_trans)([kwargs, \_recursive, \_inplace])     | Transform a kwargs dict or build a transformer.                                                                                           |
| [`kwargs_trans_to_extract_args_from_attrs`](_autosummary/i2.wrapper.html.md#i2.wrapper.kwargs_trans_to_extract_args_from_attrs)(...)       | Pop the `obj_param` object out of `outer_kwargs` and source `attr_names` from its attributes.                                             |
| `map_names`([func])                                                                                 | Change the argument names of a function.                                                                                                  |
| [`mk_ingress_from_name_mapper`](_autosummary/i2.wrapper.html.md#i2.wrapper.mk_ingress_from_name_mapper)(func, name_mapper, \*) | Make an ingress that renames `func`'s parameters (`{inner_name: outer_name}`).                                                            |
| [`modify_dict_on_cond`](_autosummary/i2.wrapper.html.md#i2.wrapper.modify_dict_on_cond)(d, cond, func)                 | Copy `d`, applying `func` to the values whose `(key, value)` satisfy `cond`.                                                              |
| [`move_names_to_the_end`](_autosummary/i2.wrapper.html.md#i2.wrapper.move_names_to_the_end)(names, ...)                  | Remove the items of `names_to_move_to_the_end` from `names` and append to the right of names                                              |
| [`move_params_to_the_end`](_autosummary/i2.wrapper.html.md#i2.wrapper.move_params_to_the_end)(func, names_to_move)        | Choose args from func, according to choice_args_func and move them to the right                                                           |
| [`param_to_dataclass_field_tuple`](_autosummary/i2.wrapper.html.md#i2.wrapper.param_to_dataclass_field_tuple)(param)              | The `(name, annotation, default)` tuple `dataclasses.make_dataclass` expects for a field.                                                 |
| [`parameters_to_dict`](_autosummary/i2.wrapper.html.md#i2.wrapper.parameters_to_dict)(parameters)                     | Map each parameter name to its `parameter_to_dict` (name, kind, default, annotation) dict.                                                |
| [`params_used_in_funcs`](_autosummary/i2.wrapper.html.md#i2.wrapper.params_used_in_funcs)(funcs)                        | The set of parameter names of all the given functions.                                                                                    |
| [`partialx`](_autosummary/i2.wrapper.html.md#i2.wrapper.partialx)(func, \*args[, \_\_name_\_, ...])         | Extends the functionality of builtin `functools.partial` with the ability to                                                              |
| [`required_params_used_in_funcs`](_autosummary/i2.wrapper.html.md#i2.wrapper.required_params_used_in_funcs)(funcs)               | The set of names that are required (no default) in at least one of the given functions.                                                   |
| `rm_params`([func, params_to_remove, ...])                                                          | Get a function with some parameters removed.                                                                                              |
| [`transparent_egress`](_autosummary/i2.wrapper.html.md#i2.wrapper.transparent_egress)(output)                         |                                                                                                                                           |
| [`transparent_ingress`](_autosummary/i2.wrapper.html.md#i2.wrapper.transparent_ingress)(\*args, \*\*kwargs)            |                                                                                                                                           |
| `wrap`([func, ingress, egress, caller, name, ...])                                                  | Wrap a function, optionally transforming interface, input and output.                                                                     |
| [`wrap_from_sig`](_autosummary/i2.wrapper.html.md#i2.wrapper.wrap_from_sig)(func, new_sig)                       | Give `func` the signature `new_sig`, calling it with only the arguments it takes.                                                         |

### Classes

| [`ArgNameMappingIngress`](_autosummary/i2.wrapper.html.md#i2.wrapper.ArgNameMappingIngress)(inner_sig, \*[, ...])     | Ingress that renames parameters: called with outer names, returns inner (args, kwargs).                 |
|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
| [`ArgValConverterIngress`](_autosummary/i2.wrapper.html.md#i2.wrapper.ArgValConverterIngress)(func[, ...])             | Ingress with `func`'s signature that applies `name=converter` functions to arguments.                   |
| [`Ingress`](_autosummary/i2.wrapper.html.md#i2.wrapper.Ingress)(inner_sig[, kwargs_trans, ...])         | The Ingress class offers a template for creating ingress classes.                                       |
| [`InnerMapIngress`](_autosummary/i2.wrapper.html.md#i2.wrapper.InnerMapIngress)(inner_sig[, kwargs_trans, ...]) | Build an ingress from the wrapped function's signature and a spec of changes to it.                     |
| [`MakeFromFunc`](_autosummary/i2.wrapper.html.md#i2.wrapper.MakeFromFunc)(func_to_obj)                       | Used to indicate that an object should be made as a function of an input func                           |
| [`PickleHelperMixin`](_autosummary/i2.wrapper.html.md#i2.wrapper.PickleHelperMixin)()                             | Mixin whose `__reduce__` pickles an instance by its `__name__` (a global reference).                    |
| [`SmartDefault`](_autosummary/i2.wrapper.html.md#i2.wrapper.SmartDefault)(func_computing_default, ...)       | Placeholder default for a parameter whose value `add_smart_defaults` computes from the other arguments. |
| [`Wrap`](_autosummary/i2.wrapper.html.md#i2.wrapper.Wrap)(func[, ingress, egress, name, ...])        | A function wrapper with interface modifiers.                                                            |
| [`Wrapx`](_autosummary/i2.wrapper.html.md#i2.wrapper.Wrapx)(func[, ingress, egress, caller, name])    | An extended wrapping object that allows more complex wrapping mechanisms.                               |

### Exceptions

| [`CallerValidationError`](_autosummary/i2.wrapper.html.md#i2.wrapper.CallerValidationError)   | Raised when a caller is not valid                          |
|--------------------------------------------------------------------------|------------------------------------------------------------|
| [`EgressValidationError`](_autosummary/i2.wrapper.html.md#i2.wrapper.EgressValidationError)   | Raised when a egress is not valid                          |
| [`IngressValidationError`](_autosummary/i2.wrapper.html.md#i2.wrapper.IngressValidationError)  | Raised when a ingress is not valid                         |
| [`WrapperValidationError`](_autosummary/i2.wrapper.html.md#i2.wrapper.WrapperValidationError)  | Raised when wrapper some construction params are not valid |

### i2.wrapper.AUTO_PRESERVE_SIGNATURE *= 'auto'*

`preserve_signature` value meaning “decide per ingress” (see
`_should_preserve_signature()`). Named rather than spelled `'auto'` at each
use so the sentinel has exactly one definition.

### *class* i2.wrapper.ArgNameMappingIngress(inner_sig, , conserve_kind=False, \*\*outer_name_for_inner_name)

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

Ingress that renames parameters: called with outer names, returns inner (args, kwargs).

Unless `conserve_kind=True`, all parameter kinds of the outer signature become
POSITIONAL_OR_KEYWORD. `mk_ingress_from_name_mapper` is the function form.

### *class* i2.wrapper.ArgValConverterIngress(func, \_ArgValConverterIngress_\_strict=True, \*\*conversion_for_arg)

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

Ingress with `func`’s signature that applies `name=converter` functions to arguments.

Names that are not parameters of `func` are rejected with an `AssertionError`
at construction (the `__strict` parameter cannot be passed by keyword from outside
the class, since the name is mangled; use `arg_val_converter_ingress` to switch it
off).

### *exception* i2.wrapper.CallerValidationError

Bases: [`WrapperValidationError`](_autosummary/i2.wrapper.html.md#i2.wrapper.WrapperValidationError)

Raised when a caller is not valid

### *exception* i2.wrapper.EgressValidationError

Bases: [`WrapperValidationError`](_autosummary/i2.wrapper.html.md#i2.wrapper.WrapperValidationError)

Raised when a egress is not valid

### *class* i2.wrapper.Ingress(inner_sig, kwargs_trans=None, outer_sig=None, , allow_excess=True, apply_defaults=True, allow_partial=False)

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

The Ingress class offers a template for creating ingress classes.

Note that when writing a decorator with `i2.wrapper`, you’re usually better off
writing an ingress function for the purpose. As a result, your code will usually
be less complex, easier to read, and more efficient than using the Ingress class.

So why use the `Ingress` class at all? For one, because it’ll take care of some
common mechanics for you, so once you understand how to use it, you’ll probably
create a correct wrapper faster.

Further, if you’re writing a general wrapping tool (e.g. your own currying machine,
some rule-based input casting function, etc.) then you’ll find that using
Ingres will usually with on the complexity, readability and/or efficiency front.

Consider the following function:

```pycon
>>> def f(w, /, x: float, y=2, *, z: int = 3):
...     return f"(w:={w}) + (x:={x}) * (y:={y}) ** (z:={z}) == {w + x * y ** z}"
>>>
>>> f(0, 1)
'(w:=0) + (x:=1) * (y:=2) ** (z:=3) == 8'
```

Let’s say you wanted to dispatch this function to a command line interface,
or a webservice where all arguments are taken from the url.
The problem here is that this means that all incoming values will be strings
in that case.
Say you wanted all input values to be cast to ints. In that case you could do:

```pycon
>>> from i2.wrapper import Ingress, wrap
>>> from inspect import signature
>>>
>>> trans_all_vals_to_ints = lambda d: {k: int(v) for k, v in d.items()}
>>>
>>> cli_f = wrap(
...     f,
...     ingress=Ingress(signature(f), kwargs_trans=trans_all_vals_to_ints)
... )
>>>
>>> cli_f("2", "3", "4")
'(w:=2) + (x:=3) * (y:=4) ** (z:=3) == 194'
```

In a more realistic situation, you’d want to have more control over this value
transformation.

Say you wanted to convert to int if it’s possible, try float if not,
and just leave the string alone otherwise.

```pycon
>>> def _try_casting_to_numeric(x):
...     try:
...         return int(x)
...     except ValueError:
...         try:
...             return float(x)
...         except ValueError:
...             return x
...
>>> def cast_numbers(d: dict):
...     return {k: _try_casting_to_numeric(v) for k, v in d.items()}
>>>
>>> cli_f = wrap(f, ingress=Ingress(signature(f), kwargs_trans=cast_numbers))
>>>
>>> cli_f("2", "3.14", "4")
'(w:=2) + (x:=3.14) * (y:=4) ** (z:=3) == 202.96'
```

Let’s say that our values transformations are not all 1-to-1 as in the examples
above.
Instead, they can be

- `1-to-many` (e.g. the outer `w` is used to compute the inner `w` and `x`)
- `many-to-1` (e.g. the outer `x` and `y` are used to compute inner `y`)

```default
  w   x   y   z
 / \   \ /    |
w   x   y     z
```

```pycon
>>> def kwargs_trans(outer_kw):
...     return dict(
...         # e.g. 1-to-many: one outer arg (w) producing two inner args (w, and y)
...         w=outer_kw['w'] * 2,
...         x=outer_kw['w'] * 3,
...         # e.g. many-to-1: two outer args (x and y) producing one inner arg (y)
...         y=outer_kw['x'] + outer_kw['y'],
...         # Note that no z is mentioned: This means we're just leaving it alone
...     )
...
>>>
>>> ingress = Ingress(signature(f), kwargs_trans=kwargs_trans)
>>> assert ingress(2, x=3, y=4) == ((4,), {'x': 6, 'y': 7, 'z': 3})
>>>
>>> wrapped_f = wrap(f, ingress=ingress)
>>> assert wrapped_f(2, x=3, y=4) == '(w:=4) + (x:=6) * (y:=7) ** (z:=3) == 2062'
```

The following is an example that involves several aspects of the `Ingress` class.

```pycon
>>> from i2 import Sig
>>> def kwargs_trans(outer_kw):
...     return dict(
...         w=outer_kw['w'] * 2,
...         x=outer_kw['w'] * 3,
...         # need to pop you (inner func has no you argument)
...         y=outer_kw['x'] + outer_kw.pop('you'),
...         # Note that no z is mentioned: This means we're just leaving it alone
...     )
>>>
>>> ingress = Ingress(
...     inner_sig=signature(f),
...     kwargs_trans=kwargs_trans,
...     outer_sig=Sig(f).ch_names(y='you')  # need to give the outer sig a you
...     # You could also express it this way (though you'd lose the annotations)
...     # outer_sig=lambda w, /, x, you=2, *, z=3: None
... )
>>> assert ingress(2, x=3, you=4) == ((4,), {'x': 6, 'y': 7, 'z': 3})
>>>
>>> wrapped_f = wrap(f, ingress=ingress)
>>> assert wrapped_f(2, x=3, you=4) == '(w:=4) + (x:=6) * (y:=7) ** (z:=3) == 2062'
```

A convenience method allows to do the same with the ingress instance itself:

```pycon
>>> wrapped_f = ingress.wrap(f)
>>> assert wrapped_f(2, x=3, you=4) == '(w:=4) + (x:=6) * (y:=7) ** (z:=3) == 2062'
```

#### *classmethod* name_map(wrapped, \*\*old_to_new_name)

Change argument names.

```pycon
>>> def f(w, /, x: float, y=2, *, z: int = 3):
...     return f"(w:={w}) + (x:={x}) * (y:={y}) ** (z:={z}) == {w + x * y ** z}"
>>> ingress = Ingress.name_map(f, w='DoubleYou', z='Zee')
>>> ingress
Ingress signature: (DoubleYou, /, x: float, y=2, *, Zee: int = 3)
>>> wrapped_f = ingress.wrap(f)
>>> wrapped_f(1, 2, y=3, Zee=4)
'(w:=1) + (x:=2) * (y:=3) ** (z:=4) == 163'
```

#### wrap(func, egress=None, , name=None)

Convenience method to wrap a function with the instance ingress.
`ingress.wrap(func,...)` equivalent to `Wrap(func, ingress, ...)`

* **Return type:**
  [`Wrap`](_autosummary/i2.wrapper.html.md#i2.wrapper.Wrap)

### *exception* i2.wrapper.IngressValidationError

Bases: [`WrapperValidationError`](_autosummary/i2.wrapper.html.md#i2.wrapper.WrapperValidationError)

Raised when a ingress is not valid

### *class* i2.wrapper.InnerMapIngress(inner_sig, kwargs_trans=None, , \_allow_reordering=False, \*\*changes_for_name)

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

Build an ingress from the wrapped function’s signature and a spec of changes to it.

*Systematically*, i.e. “according to a fixed plan/system” is what it’s about here.
As we’ll see below, if you need to write a particular adapter for a specific case,
you probably should do by writing an actual ingress function directly.
In cases where you might want to apply a same logic to wrap many functions,
you may want to fix that wrapping logic: `InnerMapIngress` provides one
way to do this.

* **Parameters:**
  * **inner_sig** – The signature of the wrapped function.
  * **kwargs_trans** ([`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)[[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)], [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – A dict-to-dict transformation of the outer kwargs to
    the kwargs that should be input to the inner function.
    That is `kwargs_trans` is `outer_kwargs -> inner_kwargs`.
    Note that though both outer and inner signatures could have those annoying
    position-only kinds, you don’t have to think of that.
    The parameter kind restrictions are taken care of automatically.
  * **\_allow_reordering** – Whether we want to allow reordering of variables
  * **in_to_out_sig_changes** – The `inner_name=dict_of_changes_for_that_name`
    pairs, the `dict_of_changes_for_that_name` is a `dict` with keys being valid
    `inspect.Parameter`

Consider the following function that has a position only, a keyword only,
two arguments with annotations, and three with a default.

```pycon
>>> def f(w, /, x: float = 1, y=2, *, z: int = 3):
...     return w + x * y ** z
```

Say we wanted a version of this function

- that didn’t have the argument kind restrinctions (all POSITION_OR_KEYWORD),
- where the annotation of `x` was changed `int` and the default removed
- where `y` was named `you` instead, and has an annotation (`int`).
- where the default of `z` was `10` instead of `3`, and doesn’t have an
  annotation.

In order to get a version of this function we wanted (more lenient kinds,
with some annotations and a default change), we can use the ingress function:

```pycon
>>> def directly_defined_ingress(w, x: int, you: int=2, z = 10):
...     return (w,), dict(x=x, y=you, z=z)
```

When we need to wrap a specific function in a specific way, defining an
ingress function  this way is usually the simplest way.
But in some cases we need to build the ingress function using some predefined
rule/protocol to make applying the rule/protocol systematic.

For those cases, `InnerMapIngress` comes in handy.

With `InnerMapIngress` we’d build our ingress function like this:

```pycon
>>> from inspect import Parameter, signature
>>> PK = Parameter.POSITIONAL_OR_KEYWORD
>>> empty = Parameter.empty
>>> ingress = InnerMapIngress(
...     f,
...     # change kind to PK:
...     w=dict(kind=PK),
...     # change annotation of x from float to int and remove default
...     x=dict(annotation=int, default=empty),
...     # rename y to you and add annotation int:
...     y=dict(name='you', annotation=int),
...     # change kind to PK, default to 10, and remove annotation:
...     z=dict(kind=PK, default=10, annotation=empty),
... )
```

**Note**

- Only the changes we wish to make to the parameters are mentioned.
  You could also define the parameters explicitly by simply listing all three
  of the dimensions (kind, annotation, and default)
- Three? But a `Parameter` object has four; what about the name?
  Indeed, you can use name as well, more on that later.
- Note that in order to specify that you want no default, or no annotation,
  you cannot use `None` since `None` is both a valid default and a valid
  annotation; So instead you need to use `Parameter.empty` (conveniently
  assigned to a constant named `empty` in the `wrapping` module.

Now see that all arguments are `POSITIONAL_OR_KEYWORD`, `x` and `y` are
`int`, and default of `z` is 10:

```pycon
>>> assert (
...     str(signature(ingress))
...     == str(signature(directly_defined_ingress))
...     == '(w, x: int, you: int = 2, z=10)'
... )
```

Additionally, `ingress` function does it’s job of dispatching the right args
and kwargs to the target function:

```pycon
>>> assert (
...     ingress(0,1,2,3)
...     == directly_defined_ingress(0,1,2,3)
...     == ((0,), {'x': 1, 'y': 2, 'z': 3})
... )
```

#### *classmethod* from_signature(inner_sig, outer_sig, \_allow_reordering=False)

A convienience ingress constructor to specify wrappings that affect arguments
independently.

* **Parameters:**
  * **inner_sig** – The signature of wrapped, inner function (or the inner
    function itself)
  * **outer_sig** – The desired outer signature. Can also use a function (will
    only take it’s signature though).
  * **\_allow_reordering** – Whether to allow `outer_sig` to reorder arguments.
* **Returns:**
  An ingress that will allow one to use a function having the
  `inner_sig` signature to

Say we wanted to get a version of the function:

```pycon
>>> def f(w, /, x: float = 1, y=2, *, z: int = 3):
...     return w + x * y ** z
```

That was equivalent to (note the kind, default and annotation differences):

```pycon
>>> def g(w, x=1, y: float = 2.0, z=10):
...     return w + x * y ** z
```

```pycon
>>> h = wrap(f, ingress=InnerMapIngress.from_signature(f, g))
>>> Sig(h)
<Sig (w, x=1, y: float = 2.0, z=10)>
```

Note we could have used `...from_signature(Sig(f), Sig(g))` as well,
since the method doesn’t use the actual functions, just their signatures.

So we’ve seen that `h` takes on the signature (kind, defaults,
and annotations) of `g`.
Let’s see now that `h` actually computes, uses the defaults of `g` and
can doesn’t have the position only restriction on `w`.

```pycon
>>> assert h(0) == g(0) == 1024 == 0 + 1 * 2 ** 10
>>> assert h(1,2) == g(1,2) == 2049 == 1 + 2 * 2 ** 10
>>> assert h(1,2,3,4) == g(1,2,3,4) == 1 + 2 * 3 ** 4
>>>
>>> assert h(w=1,x=2,y=3,z=4) == g(1,2,3,4) == 1 + 2 * 3 ** 4  # w keyword arg!
```

### *class* i2.wrapper.MakeFromFunc(func_to_obj)

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

Used to indicate that an object should be made as a function of an input func

### *class* i2.wrapper.PickleHelperMixin

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

Mixin whose `__reduce__` pickles an instance by its `__name__` (a global reference).

### *class* i2.wrapper.SmartDefault(func_computing_default, original_default)

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

Placeholder default for a parameter whose value `add_smart_defaults` computes from the other arguments.

Holds the function that computes the value and, if the parameter had one, its
original default (shown in the repr).

#### original_default

alias of `_empty`

### *class* i2.wrapper.Wrap(func, ingress=None, egress=None, , name=None, preserve_signature='auto')

Bases: `_Wrap`

A function wrapper with interface modifiers.

* **Parameters:**
  * **func** – The wrapped function
  * **ingress** – The incoming data transformer. It determines the argument properties
    (name, kind, default and annotation) as well as the actual input of the
    wrapped function.
  * **egress** – The outgoing data transformer. It also takes precedence over the
    wrapped function to determine the return annotation of the `Wrap` instance
  * **name** – Name to give the wrapper (will use wrapped func name by default)
  * **preserve_signature** – 

    Controls signature preservation from the wrapped function.
    - ’auto’ (default): Automatically preserve if ingress has `(*args, **kwargs)` signature
    - True: Always preserve signature from func (copies \_\_signature_\_)
    - False: Don’t preserve (use ingress’s natural signature)

    When signature is preserved, both \_\_signature_\_ and \_\_annotations_\_ are
    copied from func to the wrapper, ensuring type checkers and IDEs see
    the original signature.
* **Returns:**
  A callable instance wrapping `func`

Some examples:

```pycon
>>> from inspect import signature
>>> from i2 import Sig
```

```pycon
>>> def func(a, b):
...     return a * b
```

```pycon
>>> wrapped_func = wrap(func)  # no transformations: wrapped_func is the same as func
>>> assert wrapped_func(2, 'Hi') == func(2, 'Hi') == 'HiHi'
```

Modifying the first argument

```pycon
>>> def ingress(a, b):
...   return (2 * a, b), dict()
>>> wrapped_func = wrap(func, ingress=ingress)  # first variable is now multiplied by 2
>>> wrapped_func(2, 'Hi')
'HiHiHiHi'
```

Same using keyword args, we need to use tuple to represent an empty tuple

```pycon
>>> def ingress(a, b):
...   return tuple(), dict(a=2 * a, b=b) # Note that b MUST be present as well, or an error will be raised
>>> wrapped_func = wrap(func, ingress=ingress)  # first variable is now multiplied by 2
>>> wrapped_func(2, 'Hi')
'HiHiHiHi'
```

Using both args and kwargs

```pycon
>>> def ingress(a, b):
...   return (2 * a, ), dict(b=b)
>>> wrapped_func = wrap(func, ingress=ingress)  # first variable is now multiplied by 2
>>> wrapped_func(2, 'Hi')
'HiHiHiHi'
```

We can use ingress to ADD parameters to func

```pycon
>>> def ingress(a, b, c):
...   return (a, b + c), dict()
>>> wrapped_func = wrap(func, ingress=ingress)
>>> # now wrapped_func takes three arguments
>>> wrapped_func(2, 'Hi', 'world!')
'Hiworld!Hiworld!'
```

Egress is a bit more straightforward, it simply applies to the output of the
wrapped function. We can use ingress to ADD parameters to func

```pycon
>>> def egress(output):
...   return output + ' ITSME!!!'
>>> wrapped_func = wrap(func, ingress=ingress, egress=egress)
>>> # now wrapped_func takes three arguments
>>> wrapped_func(2, 'Hi', 'world!')
'Hiworld!Hiworld! ITSME!!!'
```

A more involved example:

```pycon
>>> def ingress(a, b: str, c="hi"):
...     return (a + len(b) % 2,), dict(string=f"{c} {b}")
...
>>> def func(times, string):
...     return times * string
...
>>> wrapped_func = wrap(func, ingress=ingress)
>>> assert wrapped_func(2, "world! ", "Hi") == "Hi world! Hi world! Hi world! "
>>>
>>> wrapped_func = wrap(func, egress=len)
>>> assert wrapped_func(2, "co") == 4 == len("coco") == len(func(2, "co"))
>>>
>>> wrapped_func = wrap(func, ingress=ingress, egress=len)
>>> assert (
...     wrapped_func(2, "world! ", "Hi")
...     == 30
...     == len("Hi world! Hi world! Hi world! ")
... )
```

An `ingress` function links the interface of the wrapper to the interface of the
wrapped func; therefore it’s definition often depends on information of both,
and for that reason, we provide the ability to specify the ingress not only
explicitly (as in the examples above), but through a factory – a function that
will be called on `func` to produce the ingress that should be used to wrap it.

**Common Patterns and Best Practices**

**Pattern 1: Transform inputs while preserving signature**

By default (with preserve_signature=’auto’), Wrap automatically preserves
signatures when your ingress uses `(*args, **kwargs)`:

```pycon
>>> def uppercase_args(func):
...     def ingress(*args, **kwargs):
...         args = tuple(str(a).upper() if isinstance(a, str) else a for a in args)
...         return args, kwargs
...     return Wrap(func, ingress=ingress)
>>>
>>> @uppercase_args
... def greet(name: str, greeting: str = "Hello") -> str:
...     return f"{greeting}, {name}!"
>>>
>>> greet("alice")  # Signature preserved, input transformed
'Hello, ALICE!'
```

**Pattern 2: Keep return annotation with transparent egress**

When using an egress that doesn’t transform the type, return annotations
are automatically preserved:

```pycon
>>> def add_logging(func):
...     def egress(output):
...         # print(f"Result: {output}")  # Commented out for doctest
...         return output  # Type unchanged
...     return Wrap(func, egress=egress)
>>>
>>> @add_logging
... def calculate(x: int) -> int:
...     return x * 2
>>>
>>> result = calculate(5)  # Return type preserved as int
>>> result
10
```

**Pattern 3: Validation without transformation**

Use ingress for validation without modifying arguments:

```pycon
>>> def validate_positive(func):
...     def ingress(*args, **kwargs):
...         if any(a <= 0 for a in args if isinstance(a, (int, float))):
...             raise ValueError("All numeric arguments must be positive")
...         return args, kwargs
...     return Wrap(func, ingress=ingress)
>>>
>>> @validate_positive
... def multiply(x: int, y: int) -> int:
...     return x * y
>>>
>>> multiply(2, 3)
6
```

**Pattern 4: Error handling and logging**

Wrap both ends for comprehensive error handling:

```pycon
>>> def safe_call(func):
...     def ingress(*args, **kwargs):
...         # print(f"Calling {func.__name__}")  # Commented out for doctest
...         return args, kwargs
...
...     def egress(output):
...         # print(f"Success: {output}")  # Commented out for doctest
...         return output
...
...     return Wrap(func, ingress=ingress, egress=egress)
```

## Common Pitfalls and Solutions

**Pitfall 1: Losing signatures with explicit non-generic ingress**

If your ingress doesn’t use `(*args, **kwargs)`, the auto mode won’t preserve
the signature. Use preserve_signature=True explicitly:

```pycon
>>> # WRONG: Signature lost with non-generic ingress
>>> def my_func(x: int, y: int = 5) -> int:
...     return x + y
>>>
>>> def ingress(x, y):  # Specific signature
...     return (x,), {'y': y}
>>>
>>> # Without explicit preservation, signature won't match original
>>> wrapped = Wrap(my_func, ingress=ingress, preserve_signature=False)
>>> # Signature is now (x, y) instead of (x: int, y: int = 5) -> int
>>>
>>> # RIGHT: Explicit preservation
>>> wrapped = Wrap(my_func, ingress=ingress, preserve_signature=True)
>>> # Now signature is correctly (x: int, y: int = 5) -> int
```

**Pitfall 2: Type-changing egress without annotation**

If your egress changes the output type, annotate it. Otherwise, the function’s
original return type will be preserved, creating incorrect type hints:

```pycon
>>> # RIGHT: Egress annotated with correct return type
>>> def stringify(func):
...     def egress(output) -> str:  # Annotated!
...         return str(output)
...     return Wrap(func, egress=egress)
>>>
>>> @stringify
... def calc(x: int) -> str:  # Note: return annotation updated
...     return x * 2
>>>
>>> isinstance(calc(5), str)
True
```

**Pitfall 3: Forgetting to return (args, kwargs) from ingress**

Ingress MUST return a tuple of (args, kwargs) for the wrapped function:

```pycon
>>> # WRONG: ingress doesn't return (args, kwargs)
>>> # def broken_ingress(*args, **kwargs):
>>> #     print("called")
>>> #     return None  # WRONG! Must return (args, kwargs)
>>>
>>> # RIGHT: Always return (args, kwargs)
>>> def correct_ingress(*args, **kwargs):
...     # Do any processing here
...     return args, kwargs  # CORRECT
```

**Pitfall 4: Modifying mutable arguments in place**

Be careful when modifying arguments - changes affect the original objects:

```pycon
>>> # RIGHT: Create new objects
>>> def fixed(func):
...     def ingress(*args, **kwargs):
...         if args and isinstance(args[0], list):
...             args = ([*args[0], 999],) + args[1:]  # New list
...         return args, kwargs
...     return Wrap(func, ingress=ingress)
```

## Backward Compatibility Notes

**Signature Preservation (v3.0):**

Currently, preserve_signature defaults to ‘auto’ which only preserves
signatures for generic `(*args, **kwargs)` ingress functions. In v3.0,
we may change the default to True to always preserve signatures unless
explicitly disabled. This matches user expectations that decorators
should preserve signatures by default.

To prepare for this change:

- If you want current behavior: explicitly set preserve_signature=’auto’
- If you want v3.0 behavior: explicitly set preserve_signature=True
- If you never want preservation: explicitly set preserve_signature=False

#### SEE ALSO
`wrap` function.

### *exception* i2.wrapper.WrapperValidationError

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

Raised when wrapper some construction params are not valid

### *class* i2.wrapper.Wrapx(func, ingress=None, egress=None, , caller=None, name=None)

Bases: `_Wrap`

An extended wrapping object that allows more complex wrapping mechanisms.

* **Parameters:**
  * **func** – The wrapped function
  * **ingress** – The incoming data transformer. It determines the argument properties
    (name, kind, default and annotation) as well as the actual input of the
    wrapped function.
  * **egress** – The outgoing data transformer. It also takes precedence over the
    wrapped function to determine the return annotation of the `Wrap` instance
  * **caller** – A caller defines what it means to call the `func` on the
    arguments it is given. It should be of the form
    `caller(func, args, kwargs, *, ...extra_keyword_only_params)`.
    By default, the caller will simply return `func(*args, **kwargs)`.
  * **name** – Name to give the wrapper (will use wrapped func name by default)
* **Returns:**
  A callable instance wrapping `func`

```pycon
>>> from inspect import signature
>>>
>>> def func(x, y):
...     return x + y
...
>>> def save_on_output_egress(v, *, k, s):
...     s[k] = v
...     return v
...
>>> save_on_output = Wrapx(func, egress=save_on_output_egress)
>>> # TODO: should be `(x, y, *, k, s)` --> Need to work on the merge for this.
>>> str(signature(save_on_output))
'(x, y, k, s)'
>>>
>>> store = dict()
>>> save_on_output(1, 2, k='save_here', s=store)
3
>>> assert save_on_output(1, 2, k='save_here', s=store) == 3 == func(1, 2)
>>> store  # see what's in the store now!
{'save_here': 3}
```

A caller is meant to control the way the function is called.
It is given the `func` and the `func_args` and `func_kwargs`
(whatever the ingress function gives it, if present) and possibly additional
params and will return… well, what ever you tell it to.

This can be used, for example, to call the function in a subprocess,
or on a remote system, differ computation (command pattern, for example, using
`functools.partial`, or do what ever needs to have a view both on the function
and its inputs.

Here, we will wrap the function so it will apply to an iterable of inputs
(of the first argument), returning a list of results

```pycon
>>> def func(x, y=2):
...     return x + y
...
>>> def iterize(func, args, kwargs):
...     first_arg_val = next(iter(kwargs.values()))
...     return list(map(func, first_arg_val))
...
>>> iterized_func = Wrapx(func, caller=iterize)
>>> iterized_func([1, 2, 3, 4])
[3, 4, 5, 6]
```

Let’s do the same as above, but allow other variables (here `y`) to be input as
well. This takes a bit more work…

```pycon
>>> from functools import partial
>>> def _iterize_first_arg(func, args, kwargs):
...     first_arg_name = next(iter(kwargs))
...     remaining_kwargs = {
...         k: v for k, v in kwargs.items() if k != first_arg_name
...     }
...     return list(
...         map(partial(func, **remaining_kwargs), kwargs[first_arg_name])
...     )
```

Let’s demo a different way of using Wrapx: Making a wrapper to apply at
function definition time

```pycon
>>> iterize_first_arg = partial(Wrapx, caller=_iterize_first_arg)
>>> @iterize_first_arg
... def func(x, y):
...     return x + y
>>>
>>> func([1, 2, 3, 4], 10)
[11, 12, 13, 14]
```

### i2.wrapper.append_empty_args(func)

To use to transform an ingress function that only returns kwargs to one that
returns the normal form of ingress functions: ((), kwargs)

### i2.wrapper.apply_func_on_cond(func, cond, k, v)

Return `func(v)` if `cond(k, v)` is true, else `v` unchanged.

### i2.wrapper.arg_val_converter(func, \*\*conversion_for_arg)

Wrap `func` so that the given arguments are converted (`name=converter`) before the call.

```pycon
>>> def f(x, y=1):
...     return x + y
>>> g = arg_val_converter(f, x=int)
>>> g('2', 3)
5
```

### i2.wrapper.arg_val_converter_ingress(func, \_\_strict=True, \*\*conversion_for_arg)

Function form of `ArgValConverterIngress`: an ingress converting the named arguments.

### i2.wrapper.bind_funcs_object_attrs(funcs, init_params=(), , cls=None, module=None, \*\*extra_attrs)

Transform one or several functions into a class that contains them as methods
sourcing specific arguments from the instance’s attributes.

```pycon
>>> from inspect import signature
>>> from dataclasses import dataclass
>>>
>>> def foo(a, b, c=2, *, d='bar'):
...     return f"{d}: {(a + b) * c}"
>>> foo(1, 2)
'bar: 6'
>>> Klass = bind_funcs_object_attrs(foo, init_params='a c')
>>> Klass.__name__
'Foo'
>>> instance = Klass(a=1, c=3)
>>> assert instance.foo(2, d='hello') == 'hello: 9' == foo(
...     a=1, b=2, c=3, d='hello')
>>> str(signature(Klass))
'(a, c=2) -> None'
>>>
>>> instance = Klass(a=1, c=3)
>>> str(instance)
'Foo(a=1, c=3)'
>>> str(signature(instance.foo))
"(b, *, d='bar')"
>>> instance.foo(2, d='hello')
'hello: 9'
>>> instance.foo(10, d='goodbye')
'goodbye: 33'
```

```pycon
>>> def foo(a, b, c):
...     return a + b * c
...
>>> def bar(d, e):
...     return f"{d=}, {e=}"
...
>>> @dataclass
... class K:
...     a: int
...     e: int
...
>>> C = bind_funcs_object_attrs([foo, bar], 'a e', cls=K)
>>> str(signature(C))
'(a: int, e: int) -> None'
>>> c = C(1,2)
>>> assert str(signature(c.foo)) == '(b, c)'
>>> c.foo(3,4)
13
>>> assert str(signature(c.bar)) == '(d)'
>>> c.bar(5)
'd=5, e=2'
```

### i2.wrapper.bind_funcs_object_attrs_old(funcs, init_params=(), , cls=None)

Transform one or several functions into a class that contains them as methods
sourcing specific arguments from the instance’s attributes.

```pycon
>>> from inspect import signature
>>> from dataclasses import dataclass
>>>
>>> def foo(a, b, c=2, *, d='bar'):
...     return f"{d}: {(a + b) * c}"
>>> foo(1, 2)
'bar: 6'
>>> Klass = bind_funcs_object_attrs_old(foo, init_params='a c')
>>> Klass.__name__
'Foo'
>>> instance = Klass(a=1, c=3)
>>> assert instance.foo(2, d='hello') == 'hello: 9' == foo(
...     a=1, b=2, c=3, d='hello')
>>> str(signature(Klass))
'(a, c=2) -> None'
>>>
>>> instance = Klass(a=1, c=3)
>>> str(instance)
'Foo(a=1, c=3)'
>>> str(signature(instance.foo))
"(b, *, d='bar')"
>>> instance.foo(2, d='hello')
'hello: 9'
>>> instance.foo(10, d='goodbye')
'goodbye: 33'
>>> def foo(a, b, c):
...     return a + b * c
...
>>> def bar(d, e):
...     return f"{d=}, {e=}"
...
>>> @dataclass
... class K:
...     a: int
...     e: int
...
>>> C = bind_funcs_object_attrs([foo, bar], 'a e', cls=K)
>>> str(signature(C))
'(a: int, e: int) -> None'
>>> c = C(1,2)
>>> assert str(signature(c.foo)) == '(b, c)'
>>> c.foo(3,4)
13
>>> assert str(signature(c.bar)) == '(d)'
>>> c.bar(5)
'd=5, e=2'
```

### i2.wrapper.camelize(s)

```pycon
>>> camelize('camel_case')
'CamelCase'
```

### i2.wrapper.complete_dict_applying_functions(d, , \_only_if_name_missing=True, \_allow_overwrites=False, \*\*func_for_name)

Complete dict `d` by applying function to variables in `d`, sequentially.

That is, doing `d[name] = func(**d)` for all `name, func in d.items()`.

Set `_allow_overwrites=True` to allow overwrites.

Set `_only_if_name_missing=False` to apply all functions of `func_for_name`
regardless if the `name` already exists in `d` or not.

```pycon
>>> func_for_name = dict(
...     b=lambda a: a * 10, c=lambda a, b: a + b, d=lambda c: c * 2
... )
>>> complete_dict_applying_functions(dict(a=1), **func_for_name)
{'a': 1, 'b': 10, 'c': 11, 'd': 22}
```

Notice that when `b` is present in input `dict`, it’s value is conserved.
That is, the `b` of `func_for_name` isn’t applied to compute it.

```pycon
>>> complete_dict_applying_functions(dict(a=1, b=2), **func_for_name)
{'a': 1, 'b': 2, 'c': 3, 'd': 6}
```

If you specify `_only_if_name_missing=False`,
`complete_dict_applying_functions` will try to compute everything
`func_for_name` tells it too, regardless if the input dictionary contains the
key or not, resulting in an error:

```pycon
>>> complete_dict_applying_functions(
...     dict(a=1, b=2), **func_for_name, _only_if_name_missing=False
... )
Traceback (most recent call last):
  ...
i2.errors.OverwritesNotAllowed: You're not allowed to overwrite to the values of b
```

If you want, on the other hand, to allow overwrites, you can do so specifying
`_allow_overwrites=True`:

```pycon
>>> complete_dict_applying_functions(
...     dict(a=1, b=2), **func_for_name,
...     _only_if_name_missing=False, _allow_overwrites=True
... )
{'a': 1, 'b': 10, 'c': 11, 'd': 22}
```

### i2.wrapper.convert_VK_to_KO(kinds)

In a `{name: kind}` dict, replace VAR_KEYWORD kinds with KEYWORD_ONLY.

### i2.wrapper.convert_dict_values(to_convert, key_to_conversion_function)

Yield `(key, value)` pairs of `to_convert`, converting the values whose key has a function.

```pycon
>>> dict(convert_dict_values({'x': '2', 'y': 3}, {'x': int}))
{'x': 2, 'y': 3}
```

### i2.wrapper.func_to_method_func(func, instance_params=(), , method_name=None, method_params=None, instance_arg_name='self')

Get a ‘method function’ from a ‘normal function’. Also known as “methodize”.

That is, get a function that gives the same outputs as the ‘normal function’,
except that some of the arguments are sourced from the attributes of the first
argument.

The intended use case is when you want to inject one or several methods in a class
or instance, sourcing some of the arguments of the underlying function from a
common pool: The attributes of the instance.

Consider the following function involving four parameters: `a, b, c` and `d`.

* **Return type:**
  [`Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable)

```pycon
>>> def func(a, b: int, c=2, *, d='bar'):
...     return f"{d}: {(a + b) * c}"
>>> func(1, 2, c=3, d='hello')
'hello: 9'
```

If we wanted to make an equivalent “method function” that would source it’s `a`
and it’s `c` from the first argument’s (in practice this first argument will be
and instance of the class the method will be bound to), we can do so like so:

```pycon
>>> method_func = func_to_method_func(func, 'a c')
>>> from inspect import signature
>>> str(signature(method_func))
"(self, b: int, *, d='bar')"
```

Note that the first argument is `self` (default name for an “instance”),
that `a` and `c` are not there, but that the two remaining parameters,
`b` and `d` are present, in the same order, and with the same annotations and
parameter kind (the `d` is still keyword-only).

Now let’s make a dummy object that has attributes `a` and a `c`, and use it to
call `method_func`:

```pycon
>>> from collections import namedtuple
>>> instance = namedtuple('FakeInstance', 'a c')(1, 3)
>>> method_func(instance, 2, d='hello')
'hello: 9'
```

Which is:

```pycon
>>> assert method_func(instance, 2, d='hello') == func(1, 2, c=3, d='hello')
```

Often, though, what you’ll want is to include this method function directly in
a class, as you’re making that class “normally”. That works too:

```pycon
>>> from dataclasses import dataclass
>>> @dataclass
... class Klass:
...     a : int = 1
...     c : int = 3
...     method_func = func_to_method_func(func, 'a c')
>>> instance = Klass(1, 3)
>>> instance.method_func(2, d='hello')
'hello: 9'
```

What if your function has argument names that don’t correspond to the names you
have, or want, as attributes of the class? Or even, you have several functions that
share an argument name that need to be bound to a different attribute?

For that, just use `map_names` to wrap the function, giving it the names that
you need to give it to have the effect you want (the binding of those arguments
to attributes of the instance):

```pycon
>>> from i2.wrapper import ch_names
>>> def func(x, y: int, z=2, *, d='bar'):
...     return f"{d}: {(x + y) * z}"
>>> from dataclasses import dataclass
>>> @dataclass
... class Klass:
...     a : int = 1
...     c : int = 3
...     method_func = func_to_method_func(ch_names(func, x='a', z='c'), 'a c')
>>> instance = Klass(1, 3)
>>> instance.method_func(2, d='hello')
'hello: 9'
```

### i2.wrapper.identity(x)

Return the input unchanged.

```pycon
>>> identity('x')
'x'
```

### i2.wrapper.include_exclude_ingress_factory(func, include=None, exclude=None, , allow_partial=False)

A pattern underlying any ingress that takes a subset of parameters (possibly
reordering them).

For example: Keep only required arguments, or reorder params to be able to
partialize #3 (without having to partialize #1 and #2)

### i2.wrapper.invert_map(d)

Swap keys and values of a mapping, raising `ValueError` if values are not unique.

```pycon
>>> invert_map({'a': 1, 'b': 2})
{1: 'a', 2: 'b'}
```

### i2.wrapper.items_with_mapped_keys(d, key_mapper)

Transform dict keys. More precisely yield (new_k,v) pairs from a key mapper dict.

* **Parameters:**
  * **d** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – src dict
  * **key_mapper** – {old_name: new_name, …} mapping
* **Returns:**
  generator of (new_name, value) pairs

Often used in conjunction with dict:

```pycon
>>> dict(items_with_mapped_keys(
...     {'a': 1, 'b': 2, 'c': 3, 'd': 4},
...     {'a': 'Ay', 'd': 'Dee'})
... )
{'Ay': 1, 'b': 2, 'c': 3, 'Dee': 4}
```

### i2.wrapper.kwargs_trans(kwargs=None, , \_recursive=False, \_inplace=False, \*\*key_and_val_func)

Transform a kwargs dict or build a transformer.

* **Parameters:**
  * **kwargs** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The dict containing the input kwargs that we will transform
  * **\_recursive** – Whether the transformations listed in `key_and_val_func` should
    be applied “recursively”. When set to `False` (default), each transformation
    function applies to the original `kwargs`, not the one that was transformed,
    so far.
  * **\_inplace** – If set to `False` will make a shallow copy of the `kwargs`
    before transforming it (only relevant if `_recursive=True`
  * **key_and_val_func** – The `key=val_func` pairs that indicate that a
    `val_func` should be applied to the `kwargs`, maching the argument names of
    the `val_func` to the keys of `kwargs` and extracting the values found
    therein to use for the corresponding inputs of that `val_func`.
* **Returns:**
  The transformed kwargs.

```pycon
>>> d = dict(a=1, b=2, c=3)
>>> kwargs_trans(
...     d,
...     a=lambda a: a * 10,
...     b=lambda a, b: a + b
... )
{'a': 10, 'b': 3, 'c': 3}
```

See that `d` is unchanged here (transformation is not in place).

```pycon
>>> d
{'a': 1, 'b': 2, 'c': 3}
```

Typically you’ll use `kwargs_trans` as a factory:

```pycon
>>> trans = kwargs_trans(a=lambda a: a * 10, b=lambda a, b: a + b)
>>> trans(d)
{'a': 10, 'b': 3, 'c': 3}
```

Here we’ll demo what the `_recursive` and `_inplace` arguments do.

```pycon
>>> from functools import partial
>>> re_kwargs_trans = partial(kwargs_trans, _recursive=True, _inplace=True)
>>> d = dict(a=1, b=2, c=3)
>>>
>>> re_kwargs_trans(
...     d,
...     a=lambda a: a * 10,
...     b=lambda a, b: a + b
...     # since _recursive=True, the a that is used is the new a = 10, not a = 1:
... )
{'a': 10, 'b': 12, 'c': 3}
```

Since `_inplace=True`, `d` itself has changed:

```pycon
>>> d
{'a': 10, 'b': 12, 'c': 3}
```

Sometimes you’ll pipe several transformers together:

```pycon
>>> from i2 import Pipe
>>> trans = Pipe(
...     re_kwargs_trans(a=lambda a: a / 10),
...     re_kwargs_trans(c=lambda a,b,c: a * b * c),
...     # and then compute a new value of a using a and c:
...     re_kwargs_trans(a=lambda a, c: c - 1),
... )
>>> trans(d)
{'a': 35.0, 'b': 12, 'c': 36.0}
```

### i2.wrapper.kwargs_trans_to_extract_args_from_attrs(outer_kwargs, attr_names=(), obj_param='self')

Pop the `obj_param` object out of `outer_kwargs` and source `attr_names` from its attributes.

Mutates `outer_kwargs` (the object is popped). Explicit kwargs win over attributes.

```pycon
>>> class O: pass
>>> o = O(); o.a = 1; o.b = 2
>>> kwargs_trans_to_extract_args_from_attrs({'self': o, 'c': 3}, attr_names=('a', 'b'))
{'a': 1, 'b': 2, 'c': 3}
```

### i2.wrapper.mk_ingress_from_name_mapper(func, name_mapper, , conserve_kind=False)

Make an ingress that renames `func`’s parameters (`{inner_name: outer_name}`).

```pycon
>>> def foo(a, b: int, c=7):
...     return (a, b, c)
>>> ingress = mk_ingress_from_name_mapper(foo, dict(a='aa', c='cc'))
>>> Sig(ingress)
<Sig (aa, b: int, cc=7)>
>>> ingress(1, b=2, cc=3)
((), {'a': 1, 'b': 2, 'c': 3})
>>> wrap(foo, ingress=ingress)(1, 2, cc=3)
(1, 2, 3)
```

By default the outer signature loses positional-only and keyword-only kinds;
`conserve_kind=True` keeps them.

### i2.wrapper.modify_dict_on_cond(d, cond, func)

Copy `d`, applying `func` to the values whose `(key, value)` satisfy `cond`.

### i2.wrapper.move_names_to_the_end(names, names_to_move_to_the_end)

Remove the items of `names_to_move_to_the_end` from `names`
and append to the right of names

```pycon
>>> names = ['a','c','d','e']
>>> names_to_move_to_the_end = ['c','e']
>>> move_names_to_the_end(names, names_to_move_to_the_end)
['a', 'd', 'c', 'e']
>>> names_to_move_to_the_end = 'c e'
>>> move_names_to_the_end(names, names_to_move_to_the_end)
['a', 'd', 'c', 'e']
```

### i2.wrapper.move_params_to_the_end(func, names_to_move)

Choose args from func, according to choice_args_func and move them
to the right

```pycon
>>> from functools import partial
>>> from i2 import Sig
>>> def foo(a, b, c):
...     return a + b + c
>>> g = partial(foo, b=4)  # fixing a, which is before b
>>> h = move_params_to_the_end(g, Sig(g).defaults)
>>> assert str(Sig(g)) == '(a, *, b=4, c)'
>>> assert str(Sig(h)) == '(a, *, c, b=4)'
```

### i2.wrapper.param_to_dataclass_field_tuple(param)

The `(name, annotation, default)` tuple `dataclasses.make_dataclass` expects for a field.

### i2.wrapper.parameters_to_dict(parameters)

Map each parameter name to its `parameter_to_dict` (name, kind, default, annotation) dict.

### i2.wrapper.params_used_in_funcs(funcs)

The set of parameter names of all the given functions.

### i2.wrapper.partialx(func, \*args, \_\_name_\_=None, \_rm_partialize=False, \_allow_reordering=False, \*\*kwargs)

Extends the functionality of builtin `functools.partial` with the ability to

- set `__name__`
- remove partialized arguments from signature
- reorder params (so that defaults are at the end)

```pycon
>>> def f(a, b=2, c=3):
...     return a + b * c
>>> curried_f = partialx(f, c=10, _rm_partialize=True)
>>> curried_f.__name__
'f'
>>> from inspect import signature
>>> str(signature(curried_f))
'(a, b=2)'
```

```pycon
>>> def f(a, b, c=3):
...     return a + b * c
```

Note that `a` gets a default, but `b` does not, yet is after `a`.
This is allowed because these parameters all became KEYWORD_ONLY.

```pycon
>>> g = partialx(f, a=1)
>>> str(Sig(g))
'(*, a=1, b, c=3)'
```

If you wanted to reorder the parameters to have all defaulted kinds be at the end,
as usual, you can do so using `_allow_reordering=True`

```pycon
>>> g = partialx(f, a=1, _allow_reordering=True)
>>> str(Sig(g))
'(*, b, a=1, c=3)'
```

### i2.wrapper.required_params_used_in_funcs(funcs)

The set of names that are required (no default) in at least one of the given functions.

### i2.wrapper.transparent_egress(output)

```pycon
>>> transparent_egress('unnecessary_doctest')
'unnecessary_doctest'
```

### i2.wrapper.transparent_ingress(\*args, \*\*kwargs)

```pycon
>>> transparent_ingress(1, 2, test=1)
((1, 2), {'test': 1})
```

### i2.wrapper.wrap_from_sig(func, new_sig)

Give `func` the signature `new_sig`, calling it with only the arguments it takes.

```pycon
>>> def f(x, y=1):
...     return x + y
>>> h = wrap_from_sig(f, Sig('(x, y=1, z=0)'))
>>> Sig(h)
<Sig (x, y=1, z=0)>
>>> h(1, 2, 3), h(1, z=5)
(3, 2)
```


# about-this-build.html.md

<!-- generated by epythet -->

# About this build

This documentation was built on **2026-09-22 15:20 UTC** from commit <a href="https://github.com/i2mint/i2/commit/14bd29dcc277dcb205c8903a80c8973d208b8b48"><code>14bd29d</code></a> on branch <code>master</code>, for **i2 0.1.74** (from <code>setup.cfg</code>).

#### WARNING
The documentation and the package may be misaligned:

- The documented version (0.1.74) is ahead of the latest release on PyPI (0.1.73): these docs describe unreleased code.

## Source

|                     |                                                                                                                                                  |
|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| Commit              | <a href="https://github.com/i2mint/i2/commit/14bd29dcc277dcb205c8903a80c8973d208b8b48"><code>14bd29dcc277dcb205c8903a80c8973d208b8b48</code></a> |
| Branch              | <code>master</code>                                                                                                                              |
| Tags at this commit | <code>0.1.74</code>                                                                                                                              |
| Working tree        | clean                                                                                                                                            |
| Remote              | <code>https://github.com/i2mint/i2</code>                                                                                                        |

## Continuous integration

|              |                                                                                            |
|--------------|--------------------------------------------------------------------------------------------|
| Repository   | <code>i2mint/i2</code>                                                                     |
| Run          | <a href="https://github.com/i2mint/i2/actions/runs/35746425932">35746425932</a>            |
| Ref          | <code>refs/heads/master</code>                                                             |
| Event commit | <code>af249c6ae5f32a11ff96b7a70cac00c96cc887ff</code> (in the history of the built commit) |

## Tools

|          |         |
|----------|---------|
| epythet  | 0.2.12  |
| Sphinx   | 9.1.0   |
| docutils | 0.22.4  |
| Python   | 3.12.14 |

## Configuration as resolved

|               |                                                                   |
|---------------|-------------------------------------------------------------------|
| theme         | <code>auto</code> (Sphinx theme <code>sphinxawesome_theme</code>) |
| accent        | <code>#3752a0</code>                                              |
| api_generator | <code>autosummary</code>                                          |
| ignore        | <code>tests/</code>, <code>scrap/</code>, <code>examples/</code>  |
| agent_outputs | <code>true</code>                                                 |
| aggregates    | <code>md</code>                                                   |
| ai_artifacts  | <code>true</code>                                                 |

## Package on PyPI

Latest release: <a href="https://pypi.org/project/i2/0.1.73/">0.1.73</a>, older than the documented version (0.1.74).

## Reproduce

```bash
git clone https://github.com/i2mint/i2 && cd i2
git checkout 14bd29dcc277dcb205c8903a80c8973d208b8b48
pip install "epythet==0.2.12"
epythet quickstart . --ignore tests/ scrap/ examples/
```

The same data, for machines: <a href="build_info.json"><code>build_info.json</code></a> (schema version 1).


# ai-agents.html.md

<!-- generated by epythet -->

# For AI agents

`i2` ships artifacts for coding agents alongside its code. This page lists
them, says where each lives in the repository, and points at the
machine-readable copies of this documentation.

## Skills

Skills are folders holding a `SKILL.md` (the [Agent Skills](https://agentskills.io) format): a description that tells an agent when to use it and a body with the procedure. Install one into your agent with `gh skill` (any host: `--agent claude-code`, `copilot`, `cursor`, `codex`, `gemini`), or use the copy bundled in the wheel.

### `i2-castgraph`

Routing data through a graph of type/representation conversions with i2.castgraph’s TransformationGraph — register pairwise transformers, then convert any source representation to any reachable target via multi-hop shortest-path routing. Trigger on “convert between many data representations”, “type conversion registry / service”, “transformation graph”, “filepath -> text -> record style pipeline”, “find a conversion path between two kinds”, “adapter / anti-corruption layer for external formats”, “cast a thing whose form varies but role is stable”.

Source: [`.claude/skills/i2-castgraph`](https://github.com/i2mint/i2/tree/HEAD/.claude/skills/i2-castgraph).

### `i2-multi-object`

Composing and combining a fixed collection of functions (or context managers) with i2.multi_object — Pipe (function composition), FuncFanout (apply many functions to the same input), ParallelFuncs (per-key dict-to-dict), FlexFuncFanout (shared argument pool), and ContextFanout (bundle context managers). Trigger on “compose functions into a pipeline”, “apply several functions to the same arguments”, “run a function per dict key”, “chain transformations”, “bundle multiple context managers as one”, “function pipeline”, “fan out / fan in”.

Source: [`.claude/skills/i2-multi-object`](https://github.com/i2mint/i2/tree/HEAD/.claude/skills/i2-multi-object).

### `i2-sig-arithmetic`

Building, merging, and editing function signatures with i2’s Sig — signature “+/-” arithmetic, changing parameter names/kinds/defaults/annotations, and giving a function a different signature (a la functools.wraps but signature-driven). Trigger on “merge two signatures”, “combine the parameters of several functions”, “give this function a new signature”, “rename/reorder function parameters”, “change a parameter’s default”, “make all params positional-or-keyword”, “add an optional keyword argument to a function”, “Sig() decorator”, “ch_func_to_all_pk”.

Source: [`.claude/skills/i2-sig-arithmetic`](https://github.com/i2mint/i2/tree/HEAD/.claude/skills/i2-sig-arithmetic).

### `i2-signatures`

Introspecting function signatures and binding call arguments to parameter names with i2’s Sig class — turning an arbitrary (\*args, \*\*kwargs) call into a named-argument dict (with defaults filled in), and back. Trigger on “bind args to parameter names”, “get the full kwargs of a call”, “apply defaults to a call”, “what are this function’s parameter names/kinds/defaults”, “call a function forgivingly / ignoring extra kwargs”, “normalize a call for caching/hashing”, “inspect.signature is not enough”, “Sig”, “map_arguments”.

Source: [`.claude/skills/i2-signatures`](https://github.com/i2mint/i2/tree/HEAD/.claude/skills/i2-signatures).

### `i2-wrapper`

Wrapping functions to transform their interface, inputs, and output with i2.wrapper — wrap, Ingress/Egress, ch_names, include_exclude, rm_params, arg_val_converter, partialx, and caller-based wrapping (Wrapx). Trigger on “wrap a function and change its signature”, “transform arguments before calling”, “rename/remove/reorder a function’s parameters but keep it working”, “convert/cast argument values”, “make a CLI/HTTP-friendly version of a function”, “currying with a clean signature”, “apply a function over an iterable of inputs (iterize)”, “ingress / egress”, “decorator factory”.

Source: [`.claude/skills/i2-wrapper`](https://github.com/i2mint/i2/tree/HEAD/.claude/skills/i2-wrapper).

## Instruction files

Files agents read before working in this repository.

- [`.claude/CLAUDE.md`](https://github.com/i2mint/i2/tree/HEAD/.claude/CLAUDE.md): read by Claude Code

## Machine-readable documentation

This site publishes the same documentation in forms that fit an agent’s context window:

- [`llms.txt`](https://i2mint.github.io/i2/llms.txt): an index of every page with a one-line description ([llms.txt](https://llmstxt.org) format)
- [`i2.md`](https://i2mint.github.io/i2/i2.md): the whole documentation as one Markdown file
- `<page>.html.md`: a rendered Markdown twin of every page, advertised from each page’s `<head>` with `<link rel="alternate" type="text/markdown">`
- [`objects.inv`](https://i2mint.github.io/i2/objects.inv): the Sphinx inventory: a symbol-to-URL index (`sphobjinv convert plain objects.inv -`)


# api.html.md

# API reference

| [`i2`](_autosummary/i2.html.md#module-i2)   | Meta-programming tools to build declarative frameworks   |
|-----------------------------------------------------------------|----------------------------------------------------------|


