rttc


Namerttc JSON
Version 1.0.6 PyPI version JSON
download
home_pageNone
SummaryPython run-time type check
upload_time2024-11-07 23:02:01
maintainerNone
docs_urlNone
authorNone
requires_pythonNone
licenseNone
keywords type check safe
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ## Python run-time type checking utility

Author: [Yuxuan Zhang](mailto:python@z-yx.cc) | [GitHub Repository](https://github.com/zhangyx1998/rttc)

The `rttc` project originates from this [post](https://discuss.python.org/t/runtime-type-checking-using-parameterized-types/70173) on the python discussion forum.

### Usage

#### Want to do something like this?

```python
>>> isinstance(["hello type check"], list[str])
TypeError: isinstance() argument 2 cannot be a parameterized generic
```

#### Just drop-in replace `isinstance()` with `type_check()`

```python
from type_check import type_check

type_check(["hello type check"], list[str]) # True
type_check([1], list[str]) # False
```

And of course you can use type variables!

```
DataType = list[tuple[float, str]]

type_check([(1.0, "hello rttc")], DataType) # True
type_check([(1, 2), [3.0, "4"]] , DataType) # False
```

#### Wondering how far you can go?

These features all work recursively with each other!

- **Union types** are supported:

    ```python
    type_check(1   , int | bool) # True
    type_check(True, int | bool) # True
    type_check("1" , int | bool) # False
    ```

- **Literals** are supported:

    ```python
    type_check("alex", Literal["alex", "bob"]) # True
    type_check("hack", Literal["alex", "bob"]) # False
    ```

- **Inherited classes** are supported:

    ```python
    class C(list[int]):
        pass

    type_check(C([1])  , C) # True
    type_check(C([1.0]), C) # False
    ```

- **Type-hinted classes** are supported:

    ```python
    from typing import TypeVar, Generic, Literal
    from dataclasses import dataclass

    T = TypeVar("T")
    P = TypeVar("P")

    @dataclass
    class C(Generic[T, P]):
        x: T
        y: P
        z: Literal[1]

    type_check(C(x=1  , y="y", z=1), C[int, str]) # True
    type_check(C(x=1.0, y="y", z=1), C[int, str]) # False - C.x = float(1.0) is not int
    type_check(C(x=1  , y="y", z=2), C[int, str]) # False - C.z = int(2) is not Literal[1]
    ```


- **Custom checking hooks**:

    Examples coming soon...

    For now, please refer to [type_check/builtin_checks.py](https://github.com/zhangyx1998/rttc/blob/master/type_check/builtin_checks.py).

### Other tools in the box

### `type_assert()`

Similar to type_check(), but it raises `TypeCheckError` instead of returns `bool`.
The raised `TypeCheckError` contains debug-friendly information indicating what caused type check to fail (check below for details).

### `type_guard`

This decorator allows you to convert a class or a function into a type-guarded object.
It is analogous to performing a `type_assert` on function return values or on returned class instances.

```python
from type_check import type_guard

@type_guard
def fn(x) -> int | float | str:
    return x

fn(1) # ok

fn([]) # TypeCheckError: list([]) is not int | float | str

from dataclasses import dataclass

@type_guard
@dataclass
class A:
    x: int

A(x=1) # ok
A(x=1.0) # TypeCheckError: A.x = float(1.0) is not int
```

Since `1.0.4`, the following is made possible:

```python
@type_guard
@dataclass
class B:
    x: int

B[int](x=1)   # ok
B[int](x=1.0) # TypeCheckError: A.x = float(1.0) is not int
B[float](x=1) # TypeCheckError: A.x = int(1) is not float
```

### Info-rich return values and exceptions

### `TypeCheckResult`

`TypeCheckResult` is the return type of `type_check()` function.
It can be used directly as a `bool` or compared with a `bool`.

For example:

```python
from type_check import type_check

result = type_check([1], list[int])

print(bool(result), result == True, result == False)
# True, True, False
print(str(result))
# [1] is list[int] => True
print(repr(result))
# type_check([1] is list[int] => True)
```

And when a result evaluates to `False`, you can use `TypeCheckResult.reason` to know why:

```python
result = type_check(["1"], list[int])

print(result)
# ['1'] is list[int] => False
print(result.reason)
# list[0] = str('1') is not int
```

### `TypeCheckError`

`TypeCheckError` is inherited from `TypeError`, it will be raised by `type_assert()` and `@type_guard` when type check fails.

It contains chained attributes and keys to help you locate the data that cause type check to fail:

```python
from type_check import type_assert

type_assert([1, 2, '3', 4], list[int])
```

Will raise:

```
TypeCheckError                            Traceback (most recent call last)
Cell In[47], line 10
    6 print(result.reason)
    8 from type_check import type_assert
---> 10 type_assert([1, 2, "3", 4], list[int])

File rttc/type_check/core.py:45, in type_assert(obj, t, chain)
    43     # Clear traceback to avoid confusion
    44     type_check_error = e.with_traceback(None)
---> 45     raise type_check_error
    46 except TypeError as e:
    47     type_error = e.with_traceback(None)

TypeCheckError: list[2] = str('3') is not int
```

### Testing

Test cases live under `tests/` and are grouped by categories. Test results are available at [docs/test-results.txt](https://github.com/zhangyx1998/rttc/blob/master/docs/test-results.txt). _PRs to add more test cases to it will be deeply appreciated._

#### Instructions to run tests locally

```sh
git clone git@github.com:zhangyx1998/rttc.git

cd rttc && python3 -m pip install -r tests/requirements.txt # termcolor

python3 -m tests
```

#### Testing against well-known library `typeguard`:

```sh
pip3 install typeguard

TARGET=typegurad python3 -m tests
```

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "rttc",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "type check safe",
    "author": null,
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/6a/f6/204a7da8929007857b691ad50d0a1f2217772cf4712747597bbce4924c90/rttc-1.0.6.tar.gz",
    "platform": null,
    "description": "## Python run-time type checking utility\n\nAuthor: [Yuxuan Zhang](mailto:python@z-yx.cc) | [GitHub Repository](https://github.com/zhangyx1998/rttc)\n\nThe `rttc` project originates from this [post](https://discuss.python.org/t/runtime-type-checking-using-parameterized-types/70173) on the python discussion forum.\n\n### Usage\n\n#### Want to do something like this?\n\n```python\n>>> isinstance([\"hello type check\"], list[str])\nTypeError: isinstance() argument 2 cannot be a parameterized generic\n```\n\n#### Just drop-in replace `isinstance()` with `type_check()`\n\n```python\nfrom type_check import type_check\n\ntype_check([\"hello type check\"], list[str]) # True\ntype_check([1], list[str]) # False\n```\n\nAnd of course you can use type variables!\n\n```\nDataType = list[tuple[float, str]]\n\ntype_check([(1.0, \"hello rttc\")], DataType) # True\ntype_check([(1, 2), [3.0, \"4\"]] , DataType) # False\n```\n\n#### Wondering how far you can go?\n\nThese features all work recursively with each other!\n\n- **Union types** are supported:\n\n    ```python\n    type_check(1   , int | bool) # True\n    type_check(True, int | bool) # True\n    type_check(\"1\" , int | bool) # False\n    ```\n\n- **Literals** are supported:\n\n    ```python\n    type_check(\"alex\", Literal[\"alex\", \"bob\"]) # True\n    type_check(\"hack\", Literal[\"alex\", \"bob\"]) # False\n    ```\n\n- **Inherited classes** are supported:\n\n    ```python\n    class C(list[int]):\n        pass\n\n    type_check(C([1])  , C) # True\n    type_check(C([1.0]), C) # False\n    ```\n\n- **Type-hinted classes** are supported:\n\n    ```python\n    from typing import TypeVar, Generic, Literal\n    from dataclasses import dataclass\n\n    T = TypeVar(\"T\")\n    P = TypeVar(\"P\")\n\n    @dataclass\n    class C(Generic[T, P]):\n        x: T\n        y: P\n        z: Literal[1]\n\n    type_check(C(x=1  , y=\"y\", z=1), C[int, str]) # True\n    type_check(C(x=1.0, y=\"y\", z=1), C[int, str]) # False - C.x = float(1.0) is not int\n    type_check(C(x=1  , y=\"y\", z=2), C[int, str]) # False - C.z = int(2) is not Literal[1]\n    ```\n\n\n- **Custom checking hooks**:\n\n    Examples coming soon...\n\n    For now, please refer to [type_check/builtin_checks.py](https://github.com/zhangyx1998/rttc/blob/master/type_check/builtin_checks.py).\n\n### Other tools in the box\n\n### `type_assert()`\n\nSimilar to type_check(), but it raises `TypeCheckError` instead of returns `bool`.\nThe raised `TypeCheckError` contains debug-friendly information indicating what caused type check to fail (check below for details).\n\n### `type_guard`\n\nThis decorator allows you to convert a class or a function into a type-guarded object.\nIt is analogous to performing a `type_assert` on function return values or on returned class instances.\n\n```python\nfrom type_check import type_guard\n\n@type_guard\ndef fn(x) -> int | float | str:\n    return x\n\nfn(1) # ok\n\nfn([]) # TypeCheckError: list([]) is not int | float | str\n\nfrom dataclasses import dataclass\n\n@type_guard\n@dataclass\nclass A:\n    x: int\n\nA(x=1) # ok\nA(x=1.0) # TypeCheckError: A.x = float(1.0) is not int\n```\n\nSince `1.0.4`, the following is made possible:\n\n```python\n@type_guard\n@dataclass\nclass B:\n    x: int\n\nB[int](x=1)   # ok\nB[int](x=1.0) # TypeCheckError: A.x = float(1.0) is not int\nB[float](x=1) # TypeCheckError: A.x = int(1) is not float\n```\n\n### Info-rich return values and exceptions\n\n### `TypeCheckResult`\n\n`TypeCheckResult` is the return type of `type_check()` function.\nIt can be used directly as a `bool` or compared with a `bool`.\n\nFor example:\n\n```python\nfrom type_check import type_check\n\nresult = type_check([1], list[int])\n\nprint(bool(result), result == True, result == False)\n# True, True, False\nprint(str(result))\n# [1] is list[int] => True\nprint(repr(result))\n# type_check([1] is list[int] => True)\n```\n\nAnd when a result evaluates to `False`, you can use `TypeCheckResult.reason` to know why:\n\n```python\nresult = type_check([\"1\"], list[int])\n\nprint(result)\n# ['1'] is list[int] => False\nprint(result.reason)\n# list[0] = str('1') is not int\n```\n\n### `TypeCheckError`\n\n`TypeCheckError` is inherited from `TypeError`, it will be raised by `type_assert()` and `@type_guard` when type check fails.\n\nIt contains chained attributes and keys to help you locate the data that cause type check to fail:\n\n```python\nfrom type_check import type_assert\n\ntype_assert([1, 2, '3', 4], list[int])\n```\n\nWill raise:\n\n```\nTypeCheckError                            Traceback (most recent call last)\nCell In[47], line 10\n    6 print(result.reason)\n    8 from type_check import type_assert\n---> 10 type_assert([1, 2, \"3\", 4], list[int])\n\nFile rttc/type_check/core.py:45, in type_assert(obj, t, chain)\n    43     # Clear traceback to avoid confusion\n    44     type_check_error = e.with_traceback(None)\n---> 45     raise type_check_error\n    46 except TypeError as e:\n    47     type_error = e.with_traceback(None)\n\nTypeCheckError: list[2] = str('3') is not int\n```\n\n### Testing\n\nTest cases live under `tests/` and are grouped by categories. Test results are available at [docs/test-results.txt](https://github.com/zhangyx1998/rttc/blob/master/docs/test-results.txt). _PRs to add more test cases to it will be deeply appreciated._\n\n#### Instructions to run tests locally\n\n```sh\ngit clone git@github.com:zhangyx1998/rttc.git\n\ncd rttc && python3 -m pip install -r tests/requirements.txt # termcolor\n\npython3 -m tests\n```\n\n#### Testing against well-known library `typeguard`:\n\n```sh\npip3 install typeguard\n\nTARGET=typegurad python3 -m tests\n```\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Python run-time type check",
    "version": "1.0.6",
    "project_urls": null,
    "split_keywords": [
        "type",
        "check",
        "safe"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "91b5f4032291dd47fa79a147a55566f2575813bc1604b75779f5413823dcb4ec",
                "md5": "d8a4417d2690be24dae4b82785c8f5f4",
                "sha256": "5a5b7140f4c8f379b7f27e546e589b30a2c24a93b6be92dbffb38188efae7f62"
            },
            "downloads": -1,
            "filename": "rttc-1.0.6-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d8a4417d2690be24dae4b82785c8f5f4",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 9357,
            "upload_time": "2024-11-07T23:02:01",
            "upload_time_iso_8601": "2024-11-07T23:02:01.005832Z",
            "url": "https://files.pythonhosted.org/packages/91/b5/f4032291dd47fa79a147a55566f2575813bc1604b75779f5413823dcb4ec/rttc-1.0.6-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6af6204a7da8929007857b691ad50d0a1f2217772cf4712747597bbce4924c90",
                "md5": "967025b4b5b64bbbbdc6f623b84cfcf5",
                "sha256": "aa493cd47a22209a90936f3c62bbf8aa0d02f9eaf114b7034a4c4cf2c8dd9e63"
            },
            "downloads": -1,
            "filename": "rttc-1.0.6.tar.gz",
            "has_sig": false,
            "md5_digest": "967025b4b5b64bbbbdc6f623b84cfcf5",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 7692,
            "upload_time": "2024-11-07T23:02:01",
            "upload_time_iso_8601": "2024-11-07T23:02:01.965868Z",
            "url": "https://files.pythonhosted.org/packages/6a/f6/204a7da8929007857b691ad50d0a1f2217772cf4712747597bbce4924c90/rttc-1.0.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-07 23:02:01",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "rttc"
}
        
Elapsed time: 0.83932s