Skip to main content

A pure python type enforcer for python type annotations

Reason this release was yanked:

Allowed python>=3.7 when it should have been python>=3.9. This is resolved in 1.1.1.

Project description

Type Enforced

PyPI version License: MIT

A pure python (no special compiler required) type enforcer for type annotations. Enforce types in python functions and methods.

Setup

Make sure you have Python 3.7.x (or higher) installed on your system. You can download it here.

  • Note: Certain features are only available on newer python versions
    • EG: Staticmethod typechecking requires python>=3.10

Installation

pip install type_enforced

Getting Started

type_enforcer contains a basic Enforcer wrapper that can be used to enforce many basic python typing hints. Technical Docs Here.

type_enforcer currently supports many single and multi level python types. This includes class instances and classes themselves. For example, you can force an input to be an int, a number [int, float], an instance of the self defined MyClass, or a even a vector with list[int]. Items like typing.List, typing.Dict, typing.Union and typing.Optional are supported.

You can pass union types to validate one of multiple types. For example, you could validate an input was an int or a float with [int, str], [int | float] or even typing.Union[int,str].

Nesting is allowed as long as the nested items are iterables (e.g. typing.List, dict, ...). For examle, you could validate that a list is a vector with list[int] or possibly typing.List[int].

Variables without an annotation for type are not enforced.

Supported Type Checking Features:

  • Function/Method Input Typing
  • Function/Method Return Typing
  • All standard python types (str, list, int, dict, ...)
  • Union types
    • typing.Union
    • , separated list (e.g. [int, float])
    • | separated list (e.g. [int | float])
  • Nested types (e.g. dict[str] or list[int,float])
    • Note: Each parent level must be an iterable
      • Specifically a variant of list, set, tuple or dict
    • Note: dict keys are not validated, only values
    • Deeply nested types are supported too:
      • dict[dict[int]]
      • list[set[]]
  • Many of the typing (package) functions including:
    • Standard generics:
      • List, Set, Dict, Tuple
    • Union
    • Optional
    • Note: Other functions might have support, but there are not currently tests to validate them
      • Feel free to create an issue (or better yet a PR) if you want to add tests/support

Basic Usage

import type_enforced

@type_enforced.Enforcer
def my_fn(a: int , b: [int, str] =2, c: int =3) -> None:
    pass

Interactive Example

>>> import type_enforced
>>> @type_enforced.Enforcer
... def my_fn(a: int , b: [int, str] =2, c: int =3) -> None:
...     pass
...
>>> my_fn(a=1, b=2, c=3)
>>> my_fn(a=1, b='2', c=3)
>>> my_fn(a='a', b=2, c=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py", line 85, in __call__
    self.__check_type__(assigned_vars.get(key), value, key)
  File "/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py", line 107, in __check_type__
    self.__exception__(
  File "/home/conmak/development/personal/type_enforced/type_enforced/enforcer.py", line 34, in __exception__
    raise TypeError(f"({self.__fn__.__qualname__}): {message}")
TypeError: (my_fn): Type mismatch for typed variable `a`. Expected one of the following `[<class 'int'>]` but got `<class 'str'>` instead.

Nested Examples

import type_enforced
import typing

@type_enforced.Enforcer
def my_fn(
    a: dict[dict[int, float]], # Note: dict keys are not validated, only values
    b: list[typing.Set[str]] # Could also just use set
) -> None:
    return None

my_fn(a={'i':{'j':1}}, b=[{'x'}]) # Success

my_fn(a={'i':{'j':'k'}}, b=[{'x'}]) # Error:
# TypeError: (my_fn): Type mismatch for typed variable `a[i][j]`. Expected one of the following `[<class 'int'>]` but got `<class 'str'>` instead. 

Class and Method Use

Type enforcer can be applied to methods individually:

import type_enforced

class my_class:
    @type_enforced.Enforcer
    def my_fn(self, b:int):
        pass

You can also enforce all typing for all methods in a class by decorating the class itself.

import type_enforced

@type_enforced.Enforcer
class my_class:
    def my_fn(self, b:int):
        pass

    def my_other_fn(self, a: int, b: [int, str]):
      pass

You can also enforce types on staticmethods and classmethods if you are using python >= 3.10. If you are using a python version less than this, classmethods and staticmethods methods will not have their types enforced.

import type_enforced

@type_enforced.Enforcer
class my_class:
    @classmethod
    def my_fn(self, b:int):
        pass

    @staticmethod
    def my_other_fn(a: int, b: [int, str]):
      pass

Validate class instances and classes

Type enforcer can enforce class instances and classes. There are a few caveats between the two.

To enforce a class instance, simply pass the class itself as a type hint:

import type_enforced

class Foo():
    def __init__(self) -> None:
        pass

@type_enforced.Enforcer
class my_class():
    def __init__(self, object: Foo) -> None:
        self.object = object

x=my_class(Foo()) # Works great!
y=my_class(Foo) # Fails!

Notice how an initialized class instance Foo() must be passed for the enforcer to not raise an exception.

To enforce an uninitialized class object use typing.Type[classHere] on the class to enforce inputs to be an uninitialized class:

import type_enforced
import typing

class Foo():
    def __init__(self) -> None:
        pass

@type_enforced.Enforcer
class my_class():
    def __init__(self, object_class: typing.Type[Foo]) -> None:
        self.object = object_class()

y=my_class(Foo) # Works great!
x=my_class(Foo()) # Fails

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

type_enforced-1.0.0.tar.gz (11.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

type_enforced-1.0.0-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file type_enforced-1.0.0.tar.gz.

File metadata

  • Download URL: type_enforced-1.0.0.tar.gz
  • Upload date:
  • Size: 11.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for type_enforced-1.0.0.tar.gz
Algorithm Hash digest
SHA256 c09169f0e8f9c9d532a1664ab1e37f9dea9099a13c1f8d057479ae611d19c675
MD5 f567ca379d2f9a3c6ce1c3b66ad0d6e7
BLAKE2b-256 918eb5378a413de2d742e171560eb9a881ef43c8f64eb5b5c1bd52271961a135

See more details on using hashes here.

File details

Details for the file type_enforced-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: type_enforced-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 7.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for type_enforced-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f4db59b6f6b60fa9409c94b9a5cc7d3295724f36a2318037875718a3f058d528
MD5 ad63fdc31817db4b35ca9cd4a05b9cab
BLAKE2b-256 fd8299139f75b043d332acd9ab0df232cceefa3095b873c875b323764023adb9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page