{"id":18408682,"url":"https://github.com/h2oai/typesentry","last_synced_at":"2025-04-07T09:33:00.930Z","repository":{"id":56825684,"uuid":"80073095","full_name":"h2oai/typesentry","owner":"h2oai","description":"Python 2.7 \u0026 3.5+ runtime type-checker","archived":false,"fork":false,"pushed_at":"2020-04-08T12:37:19.000Z","size":93,"stargazers_count":19,"open_issues_count":5,"forks_count":7,"subscribers_count":121,"default_branch":"master","last_synced_at":"2025-04-02T00:31:49.277Z","etag":null,"topics":["pep484","python","runtime","typechecker","types","typing","validation"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/h2oai.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-26T00:43:26.000Z","updated_at":"2024-12-18T16:00:45.000Z","dependencies_parsed_at":"2022-09-20T22:46:17.974Z","dependency_job_id":null,"html_url":"https://github.com/h2oai/typesentry","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2oai%2Ftypesentry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2oai%2Ftypesentry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2oai%2Ftypesentry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/h2oai%2Ftypesentry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/h2oai","download_url":"https://codeload.github.com/h2oai/typesentry/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247626520,"owners_count":20969319,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pep484","python","runtime","typechecker","types","typing","validation"],"created_at":"2024-11-06T03:20:35.089Z","updated_at":"2025-04-07T09:33:00.626Z","avatar_url":"https://github.com/h2oai.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":".. -*- mode: rst -*-\n\n.. image:: https://codecov.io/gh/h2oai/typesentry/branch/master/graph/badge.svg\n   :target: https://codecov.io/gh/h2oai/typesentry\n   :alt: Code coverage\n.. image:: https://img.shields.io/pypi/v/typesentry.svg\n   :target: https://pypi.python.org/pypi/typesentry\n.. image:: https://img.shields.io/pypi/pyversions/typesentry.svg\n   :target: https://pypi.python.org/pypi/typesentry\n\n\ntypesentry\n==========\n\nPython library for run-time type checking for type-annotated functions. It\nsupports both Python-3 annotations (as defined in\n`PEP 484 \u003chttps://www.python.org/dev/peps/pep-0484/\u003e`_), and legacy Python-2\nannotations via decorators. The library also provides function ``is_type``\nanalogous to builtin ``isinstance`` to allow type checking in other scenarios.\n\nThe goal of this library is to provide performat, convenient, configurable\nmodule that can be used in a variety of different contexts.\n\n\n\nRationale\n---------\n\nPython is a dynamically typed language; it is the source of its strength and\nof its appeal. Still, every object in the language does have its own type, and\nevery function has an implied signature — the types of arguments that it can\noperate on. Passing arguments of incorrect types leads either to unexpected\nresults, or more likely to an exception.\n\nThus, PEP-484 was introduced, which mentions in its introduction that\n\n    This PEP aims to provide a standard syntax for type annotations, opening up\n    Python code to easier static analysis and refactoring, **potential runtime\n    type checking** ...\n\nOf course, it is each developer's choice whether or not to use runtime type\nchecking. Obviously, there is a certain amount of overhead to check the type of\neach argument (especially when it's a container type). However we have found\nthat *not* doing so itself causes problems:\n\n- Exceptions that are raised are either only tangentially related to the actual\n  problem, or outright cryptic. You may see errors about unsupported operations\n  on a type, unavailable attributes, and worse. But very rarely you see a clear\n  message such as \"Argument ``x`` expected to be an integer, but instead\n  received ``None``\".\n- A function may not do anything to its argument other than storing it for\n  later use. If the argument had invalid type, it will manifest itself some time\n  later in the program, at which point it would already be impossible to know\n  how this bad argument came out to be.\n\nIn short, runtime type checks slow down the program, whereas their absence slow\ndown the programmer. We also recognize that Python code may be executed in\ndifferent environments, which would lead to a different choice in this tradeoff:\n\n- *Production environment*, where the code runs against a set of predefined and\n  carefully vetted inputs. Perhaps in this case runtime checks can be forfeited.\n- *Testing environment*, where speed is much less of an importance but ability\n  to quickly identify errors is more crucial. Runtime checks should be always\n  on.\n- *Interactive programming*, via a console or Jupyter Notebook, where the\n  software engineer writes the code in a trial-and-error loop. Again, runtime\n  checks are extremely useful here, since speed of debugging the problems\n  dominates the speed of code execution.\n\n\n\nUsage\n-----\n\nThe ``typesentry`` library is intended to be used from other packages. The\nminimal example of how to do it looks like this::\n\n    import typesentry\n    tc1 = typesentry.Config()\n    typed = tc1.typed        # decorator to check function arguments at runtime\n    is_typed = tc1.is_typed  # equivalent of isinstance()\n\nTypically you would put this into a separate file in your project, and then\nimport symbols ``typed`` and ``is_typed`` (or however you want to name them)\nfrom that file.\n\nThe ``Config`` object here allows you to specify how exactly the type checking\nshould behave. In particular you can enable/disable type checking, pass custom\nexception class that will be used when a type error is detected, etc. More\nsettings are expected to be added in the future.\n\nYou can create more than one ``Config`` object, with different settings,\nallowing you to have typecheck-decorators with different degrees of persistence.\n\nOnce you this file (let's assume it's ``utils/types.py`` for concreteness),\nthen the annotation ``@typed`` and function ``is_typed()`` can be used as\nfollows (in both Python 2 and 3):\n\n.. code-block:: python\n\n    from .utils.types import typed\n    from typing import List, Union\n\n    @typed(x=Union[List[int], List[str]])\n    def together(x):\n        if not x:\n            return None\n        elif isinstance(x[0], int):\n            return sum(x)\n        else:\n            return \"\".join(x)\n\nor using Python 3 type annotations:\n\n.. code-block:: python\n\n    from .utils.types import typed, is_type\n    from typing import List, Union\n\n    @typed()\n    def together(x: Union[List[int], List[str]]):\n        if not x:\n            return None\n        elif isinstance(x[0], int):\n            return sum(x)\n        else:\n            return \"\".join(x)\n\nNow let's try calling this function with various arguments:\n\n\u003e\u003e\u003e together([])\n\u003e\u003e\u003e together([1, 5, -2])\n4\n\u003e\u003e\u003e together([\"hello\", \",\", \" \", \"world\", \"!\"])\n'hello, world!'\n\u003e\u003e\u003e # Notice how in 2 examples below the error message is different depending\n\u003e\u003e\u003e # on whether the argument looks more like List[int] or List[str]\n\u003e\u003e\u003e together([\"hello\", \",\", \" \", \"world\", 1])\nTypeError: Parameter x expects type List[str] but received a list where 5th element is 1 of type int\n           File \u003cstdin\u003e, line 1, in\n                together(x)\n           File \u003cstdin\u003e, line 1, in \u003cmodule\u003e()\n\u003e\u003e\u003e together([\"hello\", 2, 9, 11, 1])\nTypeError: Parameter x expects type List[int] but received a list where 1st element is 'hello' of type str\n           File \u003cstdin\u003e, line 1, in\n                together(x)\n           File \u003cstdin\u003e, line 1, in \u003cmodule\u003e()\n\u003e\u003e\u003e # If it doesn't look like either, then a more generic message is displayed\n\u003e\u003e\u003e together([False, True])\nTypeError: Parameter x of type Union[List[int], List[str]] received value [False, True] of type list\n           File \u003cstdin\u003e, line 1, in\n                together(x)\n           File \u003cstdin\u003e, line 1, in \u003cmodule\u003e()\n\u003e\u003e\u003e # Also note that we treat booleans as types distinct from int:\n\u003e\u003e\u003e isinstance(True, int), isinstance(True, bool)\n(True, True)\n\u003e\u003e\u003e is_type(True, int), is_type(True, bool)\n(False, True)\n\n\n\nSoft exceptions\n---------------\nIn addition to trying to generate helpful messages to the user upon seeing a\ntype mismatch, this module also advocates for the use of \"kind\" (or \"soft\")\nexceptions. It applies in the context of interactive programming from within a\nconsole or a Jupyter notebook.\n\nThe idea is that when a user makes a small innocent mistake, such as a typo in\na parameter's name, or providing wrong parameter value — then throwing back at\nthem exceptions with long intimidating stack traces is rather rude. The error\nmessage should not attempt to overwhelm the user, but rather help them correct\nthe problem.\n\nHence, the notion of \"soft exceptions\". They can be turned on/off using\n``Config``s parameter ``soft_exceptions`` (which is ``True`` by default).\nIn this mode ``typesentry`` installs an exception hook (via ``sys.excepthook``)\nsuch that whenever an exception exposing a ``_handle_()`` method propagates\nto the outer level, then instead of printing the default stack trace, this\n``_handle_()`` method would be invoked. Thus, \"soft exceptions\" are just\nregular exceptions for all intents and purposes, except with respect to how\nthey appear in the console.\n\nThe default exception class used by ``typesentry`` implements custom\n``_handle_()`` method which prints the error message at the top, then the\nsignature of the function where type mismatch has occurred, and finally the\ncompactified stack trace. It also uses colors to accentuate the most important\nparts of the error message.\n\nThe user may override this behavior by either specifying turning off soft\nexceptions in the ``Config``, providing their own exception class which may or\nmay not implement ``_handle_()``, or submitting a Pull Request ;)\n\nWe intend to further improve and refine this functionality (for example,\ncurrently support for Jupyter Notebooks is missing).\n\n\n\nExtensions\n----------\nYou can extend functionality of this module by declaring custom types as\nclasses deriving from ``typesentry.MagicType``. At a minimum, you would\noverride methods ``check(self, var)`` which should return ``True`` iff a\nvariable ``var`` matches the type; and method ``name(self)`` which returns\nstring description of your new type (to be used in error messages).\n\nIn addition there are also methods ``fuzzy_check(self, var)`` returning a\nfloat value from 0 to 1 indicating how well ``var`` matches the type; and\n``get_error_msg(self, param, var)`` which should return an error message\nabout parameter ``param = var`` not matching the type. These two methods are\nadvanced and need not be implemented. However they are useful if you want to\nprovide smarter-than-usual feedback to the user.\n\nFor example, suppose you have a set of functions that work with rectangular\nmatrices, i.e. objects of the type ``List[List[float]]``. At some point you\nrealize that this is insufficient: you need to guarantee that all internal\narrays have the same dimensions, otherwise it's not really a matrix. The code\nto implement such type may look like this:\n\n.. code-block:: python\n\n    from typesentry import MagicType\n\n    class MatrixT(MagicType):\n        def check(self, var) -\u003e bool:\n            if not isinstance(var, list) or not var: return False\n            for elem in var:\n                if not isinstance(elem, list): return False\n                if len(elem) != len(var[0]): return False\n                if not all(isinstance(x, float) for x in elem): return False\n            return True\n\n        def name(self) -\u003e str:\n            return \"Matrix\"\n\n\n\nInstallation\n------------\n\n.. code-block:: bash\n\n    pip install typesentry\n\n\n\nSee Also\n--------\n- `PEP 484 \u003chttps://www.python.org/dev/peps/pep-0484/\u003e`_ — Python standard for\n  declaring type annotations.\n- `MyPy \u003chttp://mypy-lang.org/\u003e`_ — static type analyzer (i.e. at compile time).\n- `typeguard \u003chttps://github.com/agronholm/typeguard\u003e`_ — alternative runtime\n  type checker.\n- `enforce \u003chttps://github.com/RussBaz/enforce\u003e`_ — another runtime type\n  checker.\n- `runtime_typecheck \u003chttps://github.com/jacopofar/runtime_typecheck\u003e`_ — yet another one.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fh2oai%2Ftypesentry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fh2oai%2Ftypesentry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fh2oai%2Ftypesentry/lists"}