Advertisement
Guest User

Static type checking decorator for Python

a guest
Jan 28th, 2012
224
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.11 KB | None | 0 0
  1. import inspect
  2.  
  3. def typed(__return_type__ = None, __accept_subclasses__ = True, **argument_types):
  4.     """
  5.    typed - run-time simulation of static typing in python
  6.  
  7.    A decorator that will throw a TypeError if the type of an argument value passed to the decorated function
  8.    does not match the type defined by this decorator.
  9.    Usage example below. Would throw a TypeError if the "string" argument is not of type str and if the type of the
  10.    returned value is not int:
  11.        @typed(int, string = str)
  12.        def getStringLength(string):
  13.            return len(string)
  14.    """
  15.     def decorator(wrappee): # The decorator function that will be returned by typed()        
  16.         def wrapper(*args, **kwargs):
  17.             """
  18.            The wrapper function that will be returned by decorator when the decorated function is called
  19.            This is the actual function that will be run before the wrapped function runs
  20.            """
  21.             def checkType(arg, expected_type, found_value, return_value = False):
  22.                 """
  23.                Raise an exception if the found value doesn't match the expected type
  24.                Adapt exception text to return value/argument
  25.                """
  26.  
  27.                 # Will be set to True if the found_value doesn't match the expected type
  28.                 bad_type = False
  29.  
  30.                 # If the option accept subclasses is true, check if the found value is an instance of the expected
  31.                 # type and set bad_type = True if not
  32.                 if __accept_subclasses__:
  33.                     if not isinstance(found_value, expected_type):
  34.                         bad_type = True
  35.  
  36.                 # If the option accept subclasses is false, check that the class of the found_value matches the
  37.                 # expected type exactly, and set bad_type = True if not
  38.                 else:
  39.                     if not found_value.__class__ == expected_type: #isinstance(found_value, expected_type):
  40.                         bad_type = True
  41.  
  42.                 # Raise a TypeError if a bad type was found and adapt the text depending on if its a return type
  43.                 # or argument type that was bad
  44.                 if bad_type:
  45.                     if return_value:
  46.                         raise TypeError(u"Return value of %s() expected type %s but found type %s" % (wrappee.__name__, __return_type__, found_value.__class__))
  47.                     else:
  48.                         raise TypeError(u"Argument %s of %s() expected type %s but found type %s" % (arg, wrappee.__name__, expected_type, found_value.__class__))
  49.  
  50.  
  51.             # Fetch an ordered list of the arguments defined for the wrapped function
  52.             wrappee_args,_,_,_ = inspect.getargspec(wrappee)
  53.  
  54.             # Check all arguments defined by the wrapped function against the types defined
  55.             for i, arg in enumerate(wrappee_args):
  56.                 if i >= len(args):
  57.                     break
  58.                 checkType(arg, argument_types[arg], args[i])
  59.  
  60.             # Check all keywords arguments defined by the wrapped function against the types defined
  61.             for arg in argument_types.keys():
  62.                 if kwargs.has_key(arg):
  63.                     checkType(arg, argument_types[arg], kwargs[arg])
  64.  
  65.             # Run the wrapped function
  66.             result = wrappee(*args, **kwargs)
  67.  
  68.             # Check the return value against the type defined
  69.             if __return_type__ is not None:
  70.                 checkType("", __return_type__, result, return_value = True)
  71.             return result
  72.         return wrapper
  73.     return decorator
  74.  
  75. ###
  76. # Usage example
  77. ###
  78. @typed(int, string = str)
  79. def getStringLength(string):
  80.     return len(string)
  81.  
  82. #####
  83. # Simple tests below
  84. #####
  85. def raisesTypeError(func, *args, **kwargs):
  86.     try:
  87.         func(*args, **kwargs)
  88.         return False
  89.     except TypeError:
  90.         return True
  91.  
  92. assert getStringLength("four") == 4
  93. assert raisesTypeError(getStringLength, 4)
  94.  
  95. @typed(int, string = str)
  96. def getStringLengthFloat(string):
  97.     return float(len(string))
  98.  
  99. assert raisesTypeError(getStringLengthFloat, "four")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement