Skip to main content

Type check decorator for python

Project description

Python run-time type checking utility

Author: Yuxuan Zhang

This project originates from this post on the python discussion forum.

Usage

Want to do something like this?

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

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

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:

    type_check(1   , int | bool) # True
    type_check(True, int | bool) # True
    type_check("1" , int | bool) # False
    
  • Literals are supported:

    type_check("alex", Literal["alex", "bob"]) # True
    type_check("hack", Literal["alex", "bob"]) # False
    
  • Inherited classes are supported:

    class C(list[int]):
        pass
    
    type_check(C([1])  , C) # True
    type_check(C([1.0]), C) # False
    
  • Type-hinted classes are supported:

    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 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.

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 C:
    x: int

C(x=1) # ok

C(x=1.0) # TypeCheckError: C.x = float(1.0) is not int

Super friendly stack trace

This is the output of test cases. You can run the test yourself!

================================== 01-simple ===================================                                                                     

[ PASS ] 1 is int => True
[REASON] Type check passed

[ PASS ] 1.0 is int => False                                                                                                                         
[REASON] float(1.0) is not int                                                                                                                       

[ PASS ] [1, 2, 3] is list[int] => True                                                                                                              
[REASON] Type check passed

[ PASS ] [1, 2, 3.0] is list[int] => False                                                                                                           
[REASON] list[2] = float(3.0) is not int                                                                                                             

[ PASS ] 1 is Literal[1] => True                                                                                                                     
[REASON] Type check passed

[ PASS ] 2 is Literal[1] => False                                                                                                                    
[REASON] int(2) is not Literal[1]                                                                                                                    

[ PASS ] 'alex' is Literal['alex', 'bob'] => True                                                                                                    
[REASON] Type check passed

[ PASS ] 'alex' is Literal['bob'] => False                                                                                                           
[REASON] str('alex') is not Literal['bob']                                                                                                           

================================= 02-multiple ==================================                                                                     

[ PASS ] [1, '2', 3.0] is list[int, str, float] => True                                                                                              
[REASON] Type check passed

[ PASS ] [1, '2', '3'] is list[int, str, float] => False                                                                                             
[REASON] list[2] = str('3') is not float                                                                                                             

[ PASS ] [1, '2'] is list[int, str, float] => False                                                                                                  
[REASON] list([1, '2']) is not list[int, str, float]                                                                                                 

[ PASS ] (1, '2', 3.0) is tuple[int, str, float] => True                                                                                             
[REASON] Type check passed

[ PASS ] (1, '2', '3') is tuple[int, str, float] => False                                                                                            
[REASON] tuple[2] = str('3') is not float                                                                                                            

[ PASS ] {1, 3.0, '2'} is set[int | str | float] => True                                                                                             
[REASON] Type check passed

[ PASS ] {None, 1, '2'} is set[int | str | float] => False                                                                                           
[REASON] set['?'] = NoneType(None) is not int | str | float                                                                                          

[ PASS ] {1: '2', 3: 4.0} is dict[int, str | float] => True                                                                                          
[REASON] Type check passed

[ PASS ] {1: '2', '3': 4.0} is dict[int, str | float] => False                                                                                       
[REASON] dict<key> = str('3') is not int                                                                                                             

[ PASS ] {1: '2', 3: None} is dict[int, str | float] => False                                                                                        
[REASON] dict[3] = NoneType(None) is not str | float                                                                                                 

================================== 03-nested ===================================                                                                     

[ PASS ] [[1, 2], [3, 4]] is list[list[int]] => True                                                                                                 
[REASON] Type check passed

[ PASS ] [[1, 2], [3, '4']] is list[list[int]] => False                                                                                              
[REASON] list[1][1] = str('4') is not int                                                                                                            

================================== 04-unions ===================================                                                                     

[ PASS ] [[1, 2], [3.0, 4.0]] is list[list[int] | list[float]] => True                                                                               
[REASON] Type check passed

[ PASS ] [[1, 2.0], [3, 4.0]] is list[list[int] | list[float]] => False                                                                              
[REASON] list[0] = list([1, 2.0]) is not list[int] | list[float]                                                                                     

================================== 05-inherit ==================================                                                                     

[ PASS ] [1, 2, 3] is A => True                                                                                                                      
[REASON] Type check passed

[ PASS ] [1, 2, 0.0] is A => False                                                                                                                   
[REASON] A[2] = float(0.0) is not int                                                                                                                

[ PASS ] B(x=1, y='2') is tests.05-inherit.B[int, str] => True                                                                                       
[REASON] Type check passed

[ PASS ] B(x=1, y=2.0) is tests.05-inherit.B[int, str] => False                                                                                      
[REASON] B.y = float(2.0) is not str                                                                                                                 

=================================== 06-guard ===================================                                                                     

[ PASS ] C([1, 2, 3]) is C => True                                                                                                                   
[REASON] Type check passed

[ PASS ] C([1, 2, 0.0]) is C => False                                                                                                                
[REASON] C[2] = float(0.0) is not int                                                                                                                

[ PASS ] C(x=1, y=2.0) is C => True                                                                                                                  
[REASON] Type check passed

[ PASS ] C(x=1.0, y=2) is C => False                                                                                                                 
[REASON] C.x = float(1.0) is not int                                                                                                                 

[ PASS ] add(1, 2) is int => True                                                                                                                    
[REASON] Type check passed

[ PASS ] add('1', '2') is str => True                                                                                                                
[REASON] Type check passed

[ PASS ] add([1], [2]) is int | str => False                                                                                                         
[REASON] list([1, 2]) is not int | str                                                                                                               

[ PASS ] All 33 tests passed

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

rttc-1.0.0.tar.gz (7.3 kB view details)

Uploaded Source

File details

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

File metadata

  • Download URL: rttc-1.0.0.tar.gz
  • Upload date:
  • Size: 7.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.13.0

File hashes

Hashes for rttc-1.0.0.tar.gz
Algorithm Hash digest
SHA256 42da7ca437ddb41458a372c9744b77b7371da2dcc7472cf3c68ed29e17f80d3b
MD5 5cb41c3b86fb60ec688b2317b7607219
BLAKE2b-256 9b65e30d4c76cb4875bd0e89eddf99ed3c6c79fc2860d246ea0ac034bd68a272

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