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

Uploaded Source

File details

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

File metadata

  • Download URL: rttc-1.0.1.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.1.tar.gz
Algorithm Hash digest
SHA256 a19afa93838865263af1e3cdb3b7830bde2724aee36cfadb81ed89463919ed26
MD5 e015e1931c583c30fb2b58e61b35744c
BLAKE2b-256 de02ec5c652e209df6899e61130e92ba18ab964c942073c98ad47fdf8cbfd843

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