# i2.itypes

Types

### Functions

| [`dot_string_of_callable_typ`](#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`](#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`](#i2.itypes.input_and_output_types)(typ)                       | The `(input_types, output_type)` pair of a parametrized `typing.Callable`.                          |
| [`is_a_new_type`](#i2.itypes.is_a_new_type)(typ)                                | Whether `typ` is a `typing.NewType` (checked through its `__qualname__` and `__supertype__`).       |
| [`is_callable_kind`](#i2.itypes.is_callable_kind)(typ)                             |                                                                                                     |
| [`iterable_to_literal`](#i2.itypes.iterable_to_literal)(iterable)                     | Convert an iterable to a Literal type.                                                              |
| [`new_type`](#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`](#i2.itypes.typ_name)(typ)                                     | The name of a typing generic (its `_name`) or of a NewType (its `__name__`).                        |
| [`validate_literal`](#i2.itypes.validate_literal)(func)                            | Decorator to validate (Literal-annotated) argument values at call time.                             |

### Classes

| [`HasAttrs`](#i2.itypes.HasAttrs)()                  | Make a protocol to express the existence of specific attributes.                |
|------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`ObjectClassifier`](#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)
```
