Skip to main content

Python run-time type check

Project description

Python run-time type checking utility

Author: Yuxuan Zhang | GitHub Repository

The rttc 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.2.tar.gz (7.4 kB view details)

Uploaded Source

Built Distribution

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

rttc-1.0.2-py3-none-any.whl (7.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: rttc-1.0.2.tar.gz
  • Upload date:
  • Size: 7.4 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.2.tar.gz
Algorithm Hash digest
SHA256 839e0af5b07391b34f361e1b0857834496e4c98a42da405d2bacbfa9f4e86754
MD5 f83603ecc519838eb856284d7102e1c3
BLAKE2b-256 ac5e52d62b123fa2de6099c7f482c3e627ac49cc15e6b34d24853beb122e4fe5

See more details on using hashes here.

File details

Details for the file rttc-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: rttc-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 7.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.13.0

File hashes

Hashes for rttc-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 7ceefd633d1c4da0051a90b683559afcf474e32c1dbc7d81d35d27a17a0fcc88
MD5 0877ba36afff72d4461e14f83c75c72e
BLAKE2b-256 c095f1ce80032eb11d3972518f1734ec28ad4c3b06457b8b75e65dd7cd73df34

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