# typical: Python's Typing Toolkit
[![image](https://img.shields.io/pypi/v/typical.svg)](https://pypi.org/project/typical/)
[![image](https://img.shields.io/pypi/l/typical.svg)](https://pypi.org/project/typical/)
[![image](https://img.shields.io/pypi/pyversions/typical.svg)](https://pypi.org/project/typical/)
[![image](https://img.shields.io/github/languages/code-size/seandstewart/typical.svg?style=flat)](https://github.com/seandstewart/typical)
[![Test & Lint](https://github.com/seandstewart/typical/workflows/Test%20&%20Lint/badge.svg)](https://github.com/seandstewart/typical/actions)
[![Coverage](https://codecov.io/gh/seandstewart/typical/branch/master/graph/badge.svg)](https://codecov.io/gh/seandstewart/typical)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
[![Netlify Status](https://api.netlify.com/api/v1/badges/982a0ced-bb7f-4391-87e8-1957071d2f66/deploy-status)](https://app.netlify.com/sites/typical-python/deploys)
![How Typical](static/typical.png)
## Introduction
Typical is a library devoted to runtime analysis, inference,
validation, and enforcement of Python types,
[PEP 484](https://www.python.org/dev/peps/pep-0484/) Type Hints, and
custom user-defined data-types.
Typical is fully compliant with the following Python Typing PEPs:
- [PEP 484 -- Type Hints](https://www.python.org/dev/peps/pep-0484/)
- [PEP 563 -- Postponed Evaluation of Annotations](https://www.python.org/dev/peps/pep-0563/)
- [PEP 585 -- Type Hinting Generics In Standard Collections](https://www.python.org/dev/peps/pep-0585/)
- [PEP 586 -- Literal Types](https://www.python.org/dev/peps/pep-0586/)
- [PEP 589 -- TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys](https://www.python.org/dev/peps/pep-0589/)
- [PEP 604 -- Allow writing union types as X | Y](https://www.python.org/dev/peps/pep-0604/)
It provides a high-level Protocol API, Functional API, and Object API to suit most any
occasion.
## Getting Started
Installation is as simple as `pip install -U typical`.
## Help
The latest documentation is hosted at
[python-typical.org](https://python-typical.org/).
> Starting with version 2.0, All documentation is hand-crafted
> markdown & versioned documentation can be found at typical's
> [Git Repo](https://github.com/seandstewart/typical/tree/master/docs).
> (Versioned documentation is still in-the-works directly on our
> domain.)
## A Typical Use-Case
The decorator that started it all:
### `typic.al(...)`
```python
import typic
@typic.al
def hard_math(a: int, b: int, *c: int) -> int:
return a + b + sum(c)
hard_math(1, "3")
#> 4
@typic.al(strict=True)
def strict_math(a: int, b: int, *c: int) -> int:
return a + b + sum(c)
strict_math(1, 2, 3, "4")
#> Traceback (most recent call last):
#> ...
#> typic.constraints.error.ConstraintValueError: Given value <'4'> fails constraints: (type=int, nullable=False, coerce=False)
```
Typical has both a high-level *Object API* and high-level
*Functional API*. In general, any method registered to one API is also
available to the other.
### The Protocol API
```python
import dataclasses
from typing import Iterable
import typic
@typic.constrained(ge=1)
class ID(int):
...
@typic.constrained(max_length=280)
class Tweet(str):
...
@dataclasses.dataclass # or typing.TypedDict or typing.NamedTuple or annotated class...
class Tweeter:
id: ID
tweets: Iterable[Tweet]
json = '{"id":1,"tweets":["I don\'t understand Twitter"]}'
protocol = typic.protocol(Tweeter)
t = protocol.transmute(json)
print(t)
#> Tweeter(id=1, tweets=["I don't understand Twitter"])
print(protocol.tojson(t))
#> '{"id":1,"tweets":["I don\'t understand Twitter"]}'
protocol.validate({"id": 0, "tweets": []})
#> Traceback (most recent call last):
#> ...
#> typic.constraints.error.ConstraintValueError: Tweeter.id: value <0> fails constraints: (type=int, nullable=False, coerce=False, ge=1)
```
### The Functional API
```python
import dataclasses
from typing import Iterable
import typic
@typic.constrained(ge=1)
class ID(int):
...
@typic.constrained(max_length=280)
class Tweet(str):
...
@dataclasses.dataclass # or typing.TypedDict or typing.NamedTuple or annotated class...
class Tweeter:
id: ID
tweets: Iterable[Tweet]
json = '{"id":1,"tweets":["I don\'t understand Twitter"]}'
t = typic.transmute(Tweeter, json)
print(t)
#> Tweeter(id=1, tweets=["I don't understand Twitter"])
print(typic.tojson(t))
#> '{"id":1,"tweets":["I don\'t understand Twitter"]}'
typic.validate(Tweeter, {"id": 0, "tweets": []})
#> Traceback (most recent call last):
#> ...
#> typic.constraints.error.ConstraintValueError: Tweeter.id: value <0> fails constraints: (type=int, nullable=False, coerce=False, ge=1)
```
### The Object API
```python
from typing import Iterable
import typic
@typic.constrained(ge=1)
class ID(int):
...
@typic.constrained(max_length=280)
class Tweet(str):
...
@typic.klass
class Tweeter:
id: ID
tweets: Iterable[Tweet]
json = '{"id":1,"tweets":["I don\'t understand Twitter"]}'
t = Tweeter.transmute(json)
print(t)
#> Tweeter(id=1, tweets=["I don't understand Twitter"])
print(t.tojson())
#> '{"id":1,"tweets":["I don\'t understand Twitter"]}'
Tweeter.validate({"id": 0, "tweets": []})
#> Traceback (most recent call last):
#> ...
#> typic.constraints.error.ConstraintValueError: Given value <0> fails constraints: (type=int, nullable=False, coerce=False, ge=1)
```
## Changelog
See our
[Releases](https://github.com/seandstewart/typical/releases).
Raw data
{
"_id": null,
"home_page": "https://github.com/seandstewart/typical",
"name": "typical",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.8.1",
"maintainer_email": null,
"keywords": "typing, data, annotations, validation, json-schema",
"author": "Sean Stewart",
"author_email": "sean_stewart@me.com",
"download_url": "https://files.pythonhosted.org/packages/f3/03/f9460181600e15b303920ba5fe02ab6b55048214416f8812457073b61944/typical-2.9.0.tar.gz",
"platform": null,
"description": "# typical: Python's Typing Toolkit\n[![image](https://img.shields.io/pypi/v/typical.svg)](https://pypi.org/project/typical/)\n[![image](https://img.shields.io/pypi/l/typical.svg)](https://pypi.org/project/typical/)\n[![image](https://img.shields.io/pypi/pyversions/typical.svg)](https://pypi.org/project/typical/)\n[![image](https://img.shields.io/github/languages/code-size/seandstewart/typical.svg?style=flat)](https://github.com/seandstewart/typical)\n[![Test & Lint](https://github.com/seandstewart/typical/workflows/Test%20&%20Lint/badge.svg)](https://github.com/seandstewart/typical/actions)\n[![Coverage](https://codecov.io/gh/seandstewart/typical/branch/master/graph/badge.svg)](https://codecov.io/gh/seandstewart/typical)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)\n[![Netlify Status](https://api.netlify.com/api/v1/badges/982a0ced-bb7f-4391-87e8-1957071d2f66/deploy-status)](https://app.netlify.com/sites/typical-python/deploys)\n\n![How Typical](static/typical.png)\n\n## Introduction\n\nTypical is a library devoted to runtime analysis, inference,\nvalidation, and enforcement of Python types,\n[PEP 484](https://www.python.org/dev/peps/pep-0484/) Type Hints, and\ncustom user-defined data-types.\n\nTypical is fully compliant with the following Python Typing PEPs:\n\n- [PEP 484 -- Type Hints](https://www.python.org/dev/peps/pep-0484/)\n- [PEP 563 -- Postponed Evaluation of Annotations](https://www.python.org/dev/peps/pep-0563/)\n- [PEP 585 -- Type Hinting Generics In Standard Collections](https://www.python.org/dev/peps/pep-0585/)\n- [PEP 586 -- Literal Types](https://www.python.org/dev/peps/pep-0586/)\n- [PEP 589 -- TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys](https://www.python.org/dev/peps/pep-0589/)\n- [PEP 604 -- Allow writing union types as X | Y](https://www.python.org/dev/peps/pep-0604/)\n\nIt provides a high-level Protocol API, Functional API, and Object API to suit most any\noccasion.\n\n## Getting Started\n\nInstallation is as simple as `pip install -U typical`.\n## Help\n\nThe latest documentation is hosted at\n[python-typical.org](https://python-typical.org/).\n\n> Starting with version 2.0, All documentation is hand-crafted\n> markdown & versioned documentation can be found at typical's\n> [Git Repo](https://github.com/seandstewart/typical/tree/master/docs).\n> (Versioned documentation is still in-the-works directly on our\n> domain.)\n\n## A Typical Use-Case\n\nThe decorator that started it all:\n\n### `typic.al(...)`\n\n```python\nimport typic\n\n\n@typic.al\ndef hard_math(a: int, b: int, *c: int) -> int:\n return a + b + sum(c)\n\nhard_math(1, \"3\")\n#> 4\n\n\n@typic.al(strict=True)\ndef strict_math(a: int, b: int, *c: int) -> int:\n return a + b + sum(c)\n\nstrict_math(1, 2, 3, \"4\")\n#> Traceback (most recent call last):\n#> ...\n#> typic.constraints.error.ConstraintValueError: Given value <'4'> fails constraints: (type=int, nullable=False, coerce=False)\n \n```\n\nTypical has both a high-level *Object API* and high-level\n*Functional API*. In general, any method registered to one API is also\navailable to the other.\n\n### The Protocol API\n\n```python\nimport dataclasses\nfrom typing import Iterable\n\nimport typic\n\n\n@typic.constrained(ge=1)\nclass ID(int):\n ...\n\n\n@typic.constrained(max_length=280)\nclass Tweet(str):\n ...\n\n\n@dataclasses.dataclass # or typing.TypedDict or typing.NamedTuple or annotated class...\nclass Tweeter:\n id: ID\n tweets: Iterable[Tweet]\n\n\njson = '{\"id\":1,\"tweets\":[\"I don\\'t understand Twitter\"]}'\nprotocol = typic.protocol(Tweeter)\n\nt = protocol.transmute(json)\nprint(t)\n#> Tweeter(id=1, tweets=[\"I don't understand Twitter\"])\n\nprint(protocol.tojson(t))\n#> '{\"id\":1,\"tweets\":[\"I don\\'t understand Twitter\"]}'\n\nprotocol.validate({\"id\": 0, \"tweets\": []})\n#> Traceback (most recent call last):\n#> ...\n#> typic.constraints.error.ConstraintValueError: Tweeter.id: value <0> fails constraints: (type=int, nullable=False, coerce=False, ge=1)\n```\n\n### The Functional API\n\n```python\nimport dataclasses\nfrom typing import Iterable\n\nimport typic\n\n\n@typic.constrained(ge=1)\nclass ID(int):\n ...\n\n\n@typic.constrained(max_length=280)\nclass Tweet(str):\n ...\n\n\n@dataclasses.dataclass # or typing.TypedDict or typing.NamedTuple or annotated class...\nclass Tweeter:\n id: ID\n tweets: Iterable[Tweet]\n\n\njson = '{\"id\":1,\"tweets\":[\"I don\\'t understand Twitter\"]}'\n\nt = typic.transmute(Tweeter, json)\nprint(t)\n#> Tweeter(id=1, tweets=[\"I don't understand Twitter\"])\n\nprint(typic.tojson(t))\n#> '{\"id\":1,\"tweets\":[\"I don\\'t understand Twitter\"]}'\n\ntypic.validate(Tweeter, {\"id\": 0, \"tweets\": []})\n#> Traceback (most recent call last):\n#> ...\n#> typic.constraints.error.ConstraintValueError: Tweeter.id: value <0> fails constraints: (type=int, nullable=False, coerce=False, ge=1)\n```\n\n### The Object API\n\n```python\nfrom typing import Iterable\n\nimport typic\n\n\n@typic.constrained(ge=1)\nclass ID(int):\n ...\n\n\n@typic.constrained(max_length=280)\nclass Tweet(str):\n ...\n\n\n@typic.klass\nclass Tweeter:\n id: ID\n tweets: Iterable[Tweet]\n \n\njson = '{\"id\":1,\"tweets\":[\"I don\\'t understand Twitter\"]}'\nt = Tweeter.transmute(json)\n\nprint(t)\n#> Tweeter(id=1, tweets=[\"I don't understand Twitter\"])\n\nprint(t.tojson())\n#> '{\"id\":1,\"tweets\":[\"I don\\'t understand Twitter\"]}'\n\nTweeter.validate({\"id\": 0, \"tweets\": []})\n#> Traceback (most recent call last):\n#> ...\n#> typic.constraints.error.ConstraintValueError: Given value <0> fails constraints: (type=int, nullable=False, coerce=False, ge=1)\n```\n\n\n## Changelog\n\nSee our\n[Releases](https://github.com/seandstewart/typical/releases).\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Typical: Python's Typing Toolkit.",
"version": "2.9.0",
"project_urls": {
"Homepage": "https://github.com/seandstewart/typical",
"Repository": "https://github.com/seandstewart/typical"
},
"split_keywords": [
"typing",
" data",
" annotations",
" validation",
" json-schema"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "45afbc9dbafd2bb7bf03449aed06208e0fb2ffc98abadff14d9f4f5df69ecfcc",
"md5": "38949dfd05210df7a953ec50a3ad2754",
"sha256": "3cd23f6dc8b28f3ffaafeed1aa159e36fd64a999907dec492a359734524ae498"
},
"downloads": -1,
"filename": "typical-2.9.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "38949dfd05210df7a953ec50a3ad2754",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.8.1",
"size": 107939,
"upload_time": "2024-09-09T15:55:59",
"upload_time_iso_8601": "2024-09-09T15:55:59.337075Z",
"url": "https://files.pythonhosted.org/packages/45/af/bc9dbafd2bb7bf03449aed06208e0fb2ffc98abadff14d9f4f5df69ecfcc/typical-2.9.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "f303f9460181600e15b303920ba5fe02ab6b55048214416f8812457073b61944",
"md5": "b9662e5a51fb412c15e89cb950a5eecf",
"sha256": "b8fcf86dce410c59cedd0c4a2a80d1b70e11bbe6fe343b81bfa5b303eedc5343"
},
"downloads": -1,
"filename": "typical-2.9.0.tar.gz",
"has_sig": false,
"md5_digest": "b9662e5a51fb412c15e89cb950a5eecf",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.8.1",
"size": 90001,
"upload_time": "2024-09-09T15:56:00",
"upload_time_iso_8601": "2024-09-09T15:56:00.547287Z",
"url": "https://files.pythonhosted.org/packages/f3/03/f9460181600e15b303920ba5fe02ab6b55048214416f8812457073b61944/typical-2.9.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-09 15:56:00",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "seandstewart",
"github_project": "typical",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"requirements": [
{
"name": "atomicwrites",
"specs": [
[
"==",
"1.4.0"
]
]
},
{
"name": "attrs",
"specs": [
[
"==",
"21.2.0"
]
]
},
{
"name": "black",
"specs": [
[
"==",
"21.12b0"
]
]
},
{
"name": "bracex",
"specs": [
[
"==",
"2.2.1"
]
]
},
{
"name": "click",
"specs": [
[
"==",
"8.0.3"
]
]
},
{
"name": "colorama",
"specs": [
[
"==",
"0.4.4"
]
]
},
{
"name": "coverage",
"specs": [
[
"==",
"6.2"
]
]
},
{
"name": "django",
"specs": [
[
"==",
"2.2.25"
]
]
},
{
"name": "djangorestframework",
"specs": [
[
"==",
"3.13.0"
]
]
},
{
"name": "dnspython",
"specs": [
[
"==",
"2.1.0"
]
]
},
{
"name": "email-validator",
"specs": [
[
"==",
"1.1.3"
]
]
},
{
"name": "fastjsonschema",
"specs": [
[
"==",
"2.15.2"
]
]
},
{
"name": "flake8",
"specs": [
[
"==",
"3.9.2"
]
]
},
{
"name": "future-typing",
"specs": [
[
"==",
"0.4.1"
]
]
},
{
"name": "ghp-import",
"specs": [
[
"==",
"2.0.2"
]
]
},
{
"name": "greenlet",
"specs": [
[
"==",
"1.1.2"
]
]
},
{
"name": "idna",
"specs": [
[
"==",
"3.3"
]
]
},
{
"name": "importlib-metadata",
"specs": [
[
"==",
"4.8.2"
]
]
},
{
"name": "inflection",
"specs": [
[
"==",
"0.5.1"
]
]
},
{
"name": "iniconfig",
"specs": [
[
"==",
"1.1.1"
]
]
},
{
"name": "jinja2",
"specs": [
[
"==",
"3.0.3"
]
]
},
{
"name": "markdown",
"specs": [
[
"==",
"3.3.6"
]
]
},
{
"name": "markupsafe",
"specs": [
[
"==",
"2.0.1"
]
]
},
{
"name": "marshmallow",
"specs": [
[
"==",
"3.14.1"
]
]
},
{
"name": "mccabe",
"specs": [
[
"==",
"0.6.1"
]
]
},
{
"name": "mergedeep",
"specs": [
[
"==",
"1.3.4"
]
]
},
{
"name": "mkdocs-awesome-pages-plugin",
"specs": [
[
"==",
"2.6.1"
]
]
},
{
"name": "mkdocs-material-extensions",
"specs": [
[
"==",
"1.0.3"
]
]
},
{
"name": "mkdocs-material",
"specs": [
[
"==",
"8.1.3"
]
]
},
{
"name": "mkdocs",
"specs": [
[
"==",
"1.2.3"
]
]
},
{
"name": "mypy-extensions",
"specs": [
[
"==",
"0.4.3"
]
]
},
{
"name": "mypy",
"specs": [
[
"==",
"0.910"
]
]
},
{
"name": "numpy",
"specs": [
[
"==",
"1.21.4"
]
]
},
{
"name": "orjson",
"specs": [
[
"==",
"3.6.5"
]
]
},
{
"name": "packaging",
"specs": [
[
"==",
"21.3"
]
]
},
{
"name": "pandas",
"specs": [
[
"==",
"1.3.5"
]
]
},
{
"name": "pathspec",
"specs": [
[
"==",
"0.9.0"
]
]
},
{
"name": "pendulum",
"specs": [
[
"==",
"2.1.2"
]
]
},
{
"name": "platformdirs",
"specs": [
[
"==",
"2.4.0"
]
]
},
{
"name": "pluggy",
"specs": [
[
"==",
"1.0.0"
]
]
},
{
"name": "py-cpuinfo",
"specs": [
[
"==",
"8.0.0"
]
]
},
{
"name": "py",
"specs": [
[
"==",
"1.11.0"
]
]
},
{
"name": "pycodestyle",
"specs": [
[
"==",
"2.7.0"
]
]
},
{
"name": "pydantic",
"specs": [
[
"==",
"1.8.2"
]
]
},
{
"name": "pyflakes",
"specs": [
[
"==",
"2.3.1"
]
]
},
{
"name": "pygal",
"specs": [
[
"==",
"3.0.0"
]
]
},
{
"name": "pygaljs",
"specs": [
[
"==",
"1.0.2"
]
]
},
{
"name": "pygments",
"specs": [
[
"==",
"2.10.0"
]
]
},
{
"name": "pymdown-extensions",
"specs": [
[
"==",
"9.1"
]
]
},
{
"name": "pyparsing",
"specs": [
[
"==",
"3.0.6"
]
]
},
{
"name": "pytest-benchmark",
"specs": [
[
"==",
"3.4.1"
]
]
},
{
"name": "pytest-cov",
"specs": [
[
"==",
"2.12.1"
]
]
},
{
"name": "pytest",
"specs": [
[
"==",
"6.2.5"
]
]
},
{
"name": "python-dateutil",
"specs": [
[
"==",
"2.8.2"
]
]
},
{
"name": "pytz",
"specs": [
[
"==",
"2021.3"
]
]
},
{
"name": "pytzdata",
"specs": [
[
"==",
"2020.1"
]
]
},
{
"name": "pyyaml-env-tag",
"specs": [
[
"==",
"0.1"
]
]
},
{
"name": "pyyaml",
"specs": [
[
"==",
"6.0"
]
]
},
{
"name": "six",
"specs": [
[
"==",
"1.16.0"
]
]
},
{
"name": "sqlalchemy",
"specs": [
[
"==",
"1.4.28"
]
]
},
{
"name": "sqlparse",
"specs": [
[
"==",
"0.4.2"
]
]
},
{
"name": "toastedmarshmallow",
"specs": [
[
"==",
"2.15.2.post1"
]
]
},
{
"name": "toml",
"specs": [
[
"==",
"0.10.2"
]
]
},
{
"name": "tomli",
"specs": [
[
"==",
"1.2.3"
]
]
},
{
"name": "typed-ast",
"specs": [
[
"==",
"1.4.3"
]
]
},
{
"name": "typing-extensions",
"specs": [
[
"==",
"3.10.0.2"
]
]
},
{
"name": "ujson",
"specs": [
[
"==",
"3.2.0"
]
]
},
{
"name": "watchdog",
"specs": [
[
"==",
"2.1.6"
]
]
},
{
"name": "wcmatch",
"specs": [
[
"==",
"8.3"
]
]
},
{
"name": "zipp",
"specs": [
[
"==",
"3.6.0"
]
]
}
],
"lcname": "typical"
}