Name | rttc JSON |
Version |
1.0.6
JSON |
| download |
home_page | None |
Summary | Python run-time type check |
upload_time | 2024-11-07 23:02:01 |
maintainer | None |
docs_url | None |
author | None |
requires_python | None |
license | None |
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"
}