Metadata-Version: 2.3
Name: typing-arguments
Version: 0.1.0
Summary: Store references of your typing arguments to be available at runtime.
Project-URL: Repository, https://github.com/team23/typing-arguments
Author-email: TEAM23 GmbH <info@team23.de>
License: The MIT License (MIT)
        Copyright (c) 2024 TEAM23 GmbH
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
        EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
        IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
        DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
        OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
        OR OTHER DEALINGS IN THE SOFTWARE.
Requires-Python: >=3.10
Provides-Extra: pydantic
Requires-Dist: pydantic<3.0.0,>=2.0.0; extra == 'pydantic'
Description-Content-Type: text/markdown

# `typing-arguments`

Typing arguments using the `Generic` base class in python are great, but they lack the ability to
easily access the type arguments at runtime. This library provides a mixin class that can be used
to make type arguments available in the class and its instances.

This is also true for classes based on `pydantic.BaseModel` for pydantic > 2.x.

## Installation

Just use `pip install typing-arguments` to install the library.

**Note:** `typing-arguments` is tested on Python `3.10`, `3.11`, `3.12` and `3.13`. This is also ensured running
all tests on all those versions using `tox`.

## Usage

### Quick Example

```python
T1 = TypeVar("T1")
T2 = TypeVar("T2", bound="SomeBaseClass")


class Something(
    GenericArgumentsMixin,
    Generic[T1, T2],
):
    t1 = typing_arg(T1)
    t2 = typing_arg(T2)


ConcreteClass = Something[str, SomeBaseClassChild]
ConcreteClass.t1  # str
ConcreteClass.t2  # SomeBaseClassChild
```

**Hint:** You may also use this with pydantic models:

```python
T1 = TypeVar("T1")
T2 = TypeVar("T2", bound="SomeBaseClass")


class SomethingModel(
    GenericArgumentsMixin,
    BaseModel,
    Generic[T1, T2],
):
    t1: ClassVar = typing_arg(T1)
    t2: ClassVar = typing_arg(T2)


ConcreteClassModel = SomethingModel[str, SomeBaseClassChild]
ConcreteClassModel.t1  # str
ConcreteClassModel.t2  # SomeBaseClassChild
```

Internally `GenericArgumentsMixin` will create a new attribute `__typing_arguments__`
inside the class and its instances. This attribute is a dictionary mapping the type
variables to their concrete types. This is useful if you want to access the type
arguments in a generic way.

The `typing_arg` function is a helper function to make the type arguments available
in the class and its instances using a nicely named attribute. This is just a
convenience function, as you can also access the type arguments directly from the
`__typing_arguments__` attribute.

**Note:** If you are using pydantic models you should use the `ClassVar` annotation
to ensure pydantic will not try to catch and validate the type arguments as normal
model fields.

You may also mix different generic base classes like so:

```python
T1 = TypeVar("T1")
T2 = TypeVar("T2", bound="SomeBaseClass")


class Base1(
    GenericArgumentsMixin,
    Generic[T1],
):
    pass


class Base2(
    GenericArgumentsMixin,
    Generic[T2],
):
    t2 = typing_arg(T2)


class Something(
    Base1[str],
    Base2[SomeBaseClassChild],
):
    t1 = typing_arg(T1)


Something.t1  # str
Something.t2  # SomeBaseClassChild
```

In this example `Base1` and `Base2` are both generic base classes. `Base1` only
defines a type argument `T1` and `Base2` only defines a type argument `T2`. The
`Something` class inherits from both `Base1` and `Base2`. Note that `Base1` does
not define a simple accessor like `t1` using `typing_arg`, while `Base2` does. This
is not a problem and can be later added by `Something` using `typing_arg` as well.

You may encounter issues using the `typing_arg` function when using type validator
like mypy or your IDE. If so you might need to use `cast` to tell the type checker
you are sure about what you are doing. For example:

```python
T1 = TypeVar("T1", bound="SomeBaseClass")


class Something(
    GenericArgumentsMixin,
    Generic[T1],
):
    t1 = cast(type[SomeBaseClass], typing_arg(T1))
```

**Note:** You will still need to use `ClassVar` when using pydantic models. This
might result in using the same type twice (inside `ClassVar` and `cast`).