Pastebin launched a little side project called HostCabi.net, check it out ;-)Don't like ads? PRO users don't see any ads ;-)
Guest

Static type checking decorator for Python

By: a guest on Jan 28th, 2012  |  syntax: Python  |  size: 4.11 KB  |  hits: 61  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  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")