vendorized typecheck

This commit is contained in:
Kenneth Reitz
2010-07-12 16:50:42 -04:00
parent 5849e3e505
commit 665ba34a2b
5 changed files with 1759 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,36 @@
"""
This module allows doctest to find typechecked functions.
Currently, doctest verifies functions to make sure that their
globals() dict is the __dict__ of their module. In the case of
decorated functions, the globals() dict *is* not the right one.
To enable support for doctest do:
import typecheck.doctest_support
This import must occur before any calls to doctest methods.
"""
def __DocTestFinder_from_module(self, module, object):
"""
Return true if the given object is defined in the given
module.
"""
import inspect
if module is None:
return True
elif inspect.isfunction(object) or inspect.isclass(object):
return module.__name__ == object.__module__
elif inspect.getmodule(object) is not None:
return module is inspect.getmodule(object)
elif hasattr(object, '__module__'):
return module.__name__ == object.__module__
elif isinstance(object, property):
return True # [XX] no way not be sure.
else:
raise ValueError("object must be a class or function")
import doctest as __doctest
__doctest.DocTestFinder._from_module = __DocTestFinder_from_module
+84
View File
@@ -0,0 +1,84 @@
from typecheck import _TC_NestedError, _TC_TypeError, check_type, Or
from typecheck import register_type, _TC_Exception
class _TC_IterationError(_TC_NestedError):
def __init__(self, iteration, value, inner_exception):
_TC_NestedError.__init__(self, inner_exception)
self.iteration = iteration
self.value = value
def error_message(self):
return ("at iteration %d (value: %s)" % (self.iteration, repr(self.value))) + _TC_NestedError.error_message(self)
### This is the shadow class behind UnorderedIteratorMixin.
### Again, it tries to pretend it doesn't exist by mimicing
### the class of <obj> as much as possible.
###
### This mixin provides typechecking for iterator classes
### where you don't care about the order of the types (ie,
### you simply Or() the types together, as opposed to patterned
### lists, which would be ordered mixins)
class _UnorderedIteratorMixin(object):
def __init__(self, class_name, obj):
vals = [o for o in obj]
self.type = self
self._type = Or(*vals)
self.__cls = obj.__class__
self.__vals = vals
# This is necessary because it's a huge pain in the ass
# to get the "raw" name of the class once it's created
self.__cls_name = class_name
def __typecheck__(self, func, to_check):
if not isinstance(to_check, self.__cls):
raise _TC_TypeError(to_check, self)
for i, item in enumerate(to_check):
try:
check_type(self._type, func, item)
except _TC_Exception, e:
raise _TC_IterationError(i, item, e)
@classmethod
def __typesig__(cls, obj):
if isinstance(obj, cls):
return obj
def __str__(self):
return "%s(%s)" % (self.__cls_name, str(self._type))
__repr__ = __str__
### This is included in a class's parent-class section like so:
### class MyClass(UnorderedIteratorMixin("MyClass")):
### blah blah blah
###
### This serves as a class factory, whose produced classes
### attempt to mask the fact they exist. Their purpose
### is to redirect __typesig__ calls to appropriate
### instances of _UnorderedIteratorMixin
def UnorderedIteratorMixin(class_name):
class UIM(object):
@classmethod
def __typesig__(cls, obj):
if isinstance(obj, cls):
return _UnorderedIteratorMixin(class_name, obj)
def __repr__(self):
return "%s%s" % (class_name, str(tuple(e for e in self)))
# We register each produced class anew
# If someone needs to unregister these classes, they should
# save a copy of it before including it in the class-definition:
#
# my_UIM = UnorderedIteratorMixin("FooClass")
# class FooClass(my_UIM):
# ...
#
# Alternatively, you could just look in FooClass.__bases__ later; whatever
register_type(UIM)
return UIM
register_type(_UnorderedIteratorMixin)
+62
View File
@@ -0,0 +1,62 @@
from typecheck import CheckType, _TC_TypeError, check_type, Type
from typecheck import register_type, Or, _TC_Exception, _TC_KeyError
from typecheck import _TC_LengthError
### Provide typechecking for the built-in set() class
###
### XXX: Investigate rewriting this in terms of
### UnorderedIteratorMixin or Or()
class Set(CheckType):
def __init__(self, set_list):
self.type = set(set_list)
self._types = [Type(t) for t in self.type]
# self._type is used to build _TC_TypeError
if len(self._types) > 1:
self._type = Or(*self.type)
elif len(self._types) == 1:
# XXX Is there an easier way to get this?
t = self.type.pop()
self._type = t
self.type.add(t)
def __str__(self):
return "Set(" + str([e for e in self.type]) + ")"
__repr__ = __str__
def __typecheck__(self, func, to_check):
if not isinstance(to_check, set):
raise _TC_TypeError(to_check, self.type)
if len(self._types) == 0 and len(to_check) > 0:
raise _TC_LengthError(len(to_check), 0)
for obj in to_check:
error = False
for type in self._types:
try:
check_type(type, func, obj)
except _TC_Exception:
error = True
continue
else:
error = False
break
if error:
raise _TC_KeyError(obj, _TC_TypeError(obj, self._type))
def __eq__(self, other):
if self.__class__ is not other.__class__:
return False
return self.type == other.type
def __hash__(self):
return hash(str(hash(self.__class__)) + str(hash(frozenset(self.type))))
@classmethod
def __typesig__(self, obj):
if isinstance(obj, set):
return Set(obj)
register_type(Set)
+35
View File
@@ -0,0 +1,35 @@
from typecheck import Typeclass
### Number
####################################################
_numbers = [int, float, complex, long, bool]
try:
from decimal import Decimal
_numbers.append(Decimal)
del Decimal
except ImportError:
pass
Number = Typeclass(*_numbers)
del _numbers
### String -- subinstance of ImSequence
####################################################
String = Typeclass(str, unicode)
### ImSequence -- immutable sequences
####################################################
ImSequence = Typeclass(tuple, xrange, String)
### MSequence -- mutable sequences
####################################################
MSequence = Typeclass(list)
### Mapping
####################################################
Mapping = Typeclass(dict)