mirror of
https://github.com/kennethreitz/tablib.git
synced 2026-06-05 23:10:17 +00:00
vendorized typecheck
This commit is contained in:
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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user