Package translate :: Package misc :: Package typecheck
[hide private]
[frames] | no frames]

Source Code for Package translate.misc.typecheck

   1  __all__ = ['accepts', 'returns', 'yields', 'TypeCheckError', 'Length', 'Empty' 
   2            ,'TypeSignatureError', 'And', 'Any', 'Class', 'Exact', 'HasAttr' 
   3            ,'IsAllOf', 'IsCallable', 'IsIterable', 'IsNoneOf', 'IsOneOf' 
   4            ,'IsOnlyOneOf', 'Not', 'Or', 'Self', 'Xor', 'YieldSeq' 
   5            ,'register_type', 'is_registered_type', 'unregister_type' 
   6            ,'Function'] 
   7   
   8  import inspect 
   9  import types 
  10   
  11  from types import GeneratorType, FunctionType, MethodType, ClassType, TypeType 
12 13 # Controls whether typechecking is on (True) or off (False) 14 enable_checking = True 15 16 # Pretty little wrapper function around __typecheck__ 17 -def check_type(type, func, val):
18 type.__typecheck__(func, val)
19
20 ### Internal exception classes (these MUST NOT get out to the user) 21 ### typecheck_{args,return,yield} should catch these and convert them to 22 ### appropriate Type{Check,Signature}Error instances 23 24 # We can't inherit from object because raise doesn't like new-style classes 25 # We can't use super() because we can't inherit from object 26 -class _TC_Exception(Exception):
27 - def error_message(self):
28 raise NotImplementedError("Incomplete _TC_Exception subclass (%s)" % str(self.__class__))
29
30 - def format_bad_object(self, bad_object):
31 return ("for %s, " % str(bad_object), self)
32
33 -class _TC_LengthError(_TC_Exception):
34 - def __init__(self, wrong, right=None):
35 _TC_Exception.__init__(self) 36 37 self.wrong = wrong 38 self.right = right
39
40 - def error_message(self):
41 m = None 42 if self.right is not None: 43 m = ", expected %d" % self.right 44 return "length was %d%s" % (self.wrong, m or "")
45
46 -class _TC_TypeError(_TC_Exception):
47 - def __init__(self, wrong, right):
48 _TC_Exception.__init__(self) 49 50 self.wrong = calculate_type(wrong) 51 self.right = right
52
53 - def error_message(self):
54 return "expected %s, got %s" % (self.right, self.wrong)
55
56 -class _TC_NestedError(_TC_Exception):
57 - def __init__(self, inner_exception):
58 self.inner = inner_exception
59
60 - def error_message(self):
61 try: 62 return ", " + self.inner.error_message() 63 except: 64 print "'%s'" % self.inner.message 65 raw_input() 66 raise
67
68 -class _TC_IndexError(_TC_NestedError):
69 - def __init__(self, index, inner_exception):
70 _TC_NestedError.__init__(self, inner_exception) 71 72 self.index = index
73
74 - def error_message(self):
75 return ("at index %d" % self.index) + _TC_NestedError.error_message(self)
76
77 # _TC_DictError exists as a wrapper around dict-related exceptions. 78 # It provides a single place to sort the bad dictionary's keys in the error 79 # message. 80 -class _TC_DictError(_TC_NestedError):
81 - def format_bad_object(self, bad_object):
82 message = "for {%s}, " % ', '.join(["%s: %s" % (repr(k), repr(bad_object[k])) for k in sorted(bad_object.keys())]) 83 84 if not isinstance(self.inner, _TC_LengthError): 85 return (message, self) 86 return (message, self.inner)
87
88 - def error_message(self):
89 raise NotImplementedError("Incomplete _TC_DictError subclass: " + str(self.__class__))
90
91 -class _TC_KeyError(_TC_DictError):
92 - def __init__(self, key, inner_exception):
93 _TC_NestedError.__init__(self, inner_exception) 94 95 self.key = key
96
97 - def error_message(self):
98 return ("for key %s" % repr(self.key)) + _TC_NestedError.error_message(self)
99
100 -class _TC_KeyValError(_TC_KeyError):
101 - def __init__(self, key, val, inner_exception):
102 _TC_KeyError.__init__(self, key, inner_exception) 103 104 self.val = val
105
106 - def error_message(self):
107 return ("at key %s, value %s" % (repr(self.key), repr(self.val))) + _TC_NestedError.error_message(self)
108
109 -class _TC_GeneratorError(_TC_NestedError):
110 - def __init__(self, yield_no, inner_exception):
111 _TC_NestedError.__init__(self, inner_exception) 112 113 self.yield_no = yield_no
114
115 - def error_message(self):
116 raise RuntimeError("_TC_GeneratorError.message should never be called")
117
118 - def format_bad_object(self, bad_object):
119 bad_obj, start_message = self.inner.format_bad_object(bad_object) 120 message = "At yield #%d: %s" % (self.yield_no, bad_obj) 121 return (message, start_message)
122
123 ### These next three exceptions exist to give HasAttr better error messages 124 -class _TC_AttrException(_TC_Exception):
125 - def __init__(self, attr):
126 _TC_Exception.__init__(self, attr) 127 128 self.attr = attr
129
130 -class _TC_AttrError(_TC_AttrException, _TC_NestedError):
131 - def __init__(self, attr, inner_exception):
132 _TC_AttrException.__init__(self, attr) 133 _TC_NestedError.__init__(self, inner_exception)
134
135 - def error_message(self):
136 return ("as for attribute %s" % self.attr) + _TC_NestedError.error_message(self)
137
138 -class _TC_MissingAttrError(_TC_AttrException):
139 - def error_message(self):
140 return "missing attribute %s" % self.attr
141
142 # This is like _TC_LengthError for YieldSeq 143 -class _TC_YieldCountError(_TC_Exception):
144 - def __init__(self, expected):
145 _TC_Exception.__init__(self, expected) 146 147 self.expected = expected
148
149 - def format_bad_object(self, bad_object):
150 return ("", self)
151
152 - def error_message(self):
153 plural = "s" 154 if self.expected == 1: 155 plural = "" 156 157 return "only expected the generator to yield %d time%s" % (self.expected, plural)
158
159 # This exists to provide more detailed error messages about why a given 160 # Xor() assertion failed 161 -class _TC_XorError(_TC_NestedError):
162 - def __init__(self, matched_conds, inner_exception):
163 assert matched_conds in (0, 2) 164 assert isinstance(inner_exception, _TC_TypeError) 165 166 _TC_Exception.__init__(self, matched_conds, inner_exception) 167 _TC_NestedError.__init__(self, inner_exception) 168 self.matched_conds = matched_conds
169
170 - def error_message(self):
171 if self.matched_conds == 0: 172 m = "neither assertion" 173 else: 174 m = "both assertions" 175 176 return _TC_NestedError.error_message(self) + " (matched %s)" % m
177
178 -class _TC_FunctionError(_TC_Exception):
179 - def __init__(self, checking_func, obj):
180 self.checking_func = checking_func 181 self.rejected_obj = obj
182
183 - def error_message(self):
184 return " was rejected by %s" % self.checking_func
185
186 - def format_bad_object(self, bad_object):
187 return (str(bad_object), self)
188
189 -class _TC_ExactError(_TC_Exception):
190 - def __init__(self, wrong, right):
191 self.wrong = wrong 192 self.right = right
193
194 - def error_message(self):
195 return "expected %s, got %s" % (self.right, self.wrong)
196
197 ### The following exist to provide detailed TypeSignatureErrors 198 -class _TS_Exception(Exception):
199 - def error_message(self):
200 raise NotImplementedError("Incomplete _TS_Exception subclass (%s)" % str(self.__class__))
201
202 # This is used when there was an error related to an auto-unpacked tuple 203 # in the function's signature 204 -class _TS_TupleError(_TS_Exception):
205 - def __init__(self, parameters, types):
206 parameters = _rec_tuple(parameters) 207 types = _rec_tuple(types) 208 _TS_Exception.__init__(self, parameters, types) 209 210 self.parameters = parameters 211 self.types = types
212
213 - def error_message(self):
214 return "the signature type %s does not match %s" % (str(self.types), str(self.parameters))
215
216 -class _TS_ExtraKeywordError(_TS_Exception):
217 - def __init__(self, keyword):
218 _TS_Exception.__init__(self, keyword) 219 220 self.keyword = keyword
221
222 - def error_message(self):
223 return "the keyword '%s' in the signature is not in the function" % self.keyword
224
225 -class _TS_ExtraPositionalError(_TS_Exception):
226 - def __init__(self, type):
227 _TS_Exception.__init__(self, type) 228 229 self.type = type
230
231 - def error_message(self):
232 return "an extra positional type has been supplied"
233
234 -class _TS_MissingTypeError(_TS_Exception):
235 - def __init__(self, parameter):
236 _TS_Exception.__init__(self, parameter) 237 238 self.parameter = parameter
239
240 - def error_message(self):
241 return "parameter '%s' lacks a type" % self.parameter
242
243 # If the user has given a keyword parameter a type both positionally and 244 # with a keyword argument, this will be raised 245 -class _TS_TwiceTypedError(_TS_Exception):
246 - def __init__(self, parameter, kw_type, pos_type):
247 _TS_Exception.__init__(self, parameter, kw_type, pos_type) 248 249 self.parameter = parameter 250 self.kw_type = kw_type 251 self.pos_type = pos_type
252
253 - def error_message(self):
254 return "parameter '%s' is provided two types (%s and %s)" % (self.parameter, str(self.kw_type), str(self.pos_type))
255 256 ### The following functions are the way new type handlers are registered 257 ### The Type function will iterate over all registered type handlers; 258 ### the first handler to return a non-None value is considered the winner 259 ######################################################################### 260 261 _hooks = ("__typesig__", "__startchecking__", "__stopchecking__", "__switchchecking__") 262 263 _registered_types = set() 264 _registered_hooks = dict([(_h, set()) for _h in _hooks])
265 266 -def _manage_registration(add_remove, reg_type):
267 if not isinstance(reg_type, (types.ClassType, types.TypeType)): 268 raise ValueError("registered types must be classes or types") 269 270 valid = False 271 for hook in _hooks: 272 if hasattr(reg_type, hook): 273 getattr(_registered_hooks[hook], add_remove)(reg_type) 274 valid = True 275 276 if valid: 277 getattr(_registered_types, add_remove)(reg_type) 278 else: 279 raise ValueError("registered types must have at least one of the following methods: " + ", ".join(_hooks))
280
281 -def register_type(reg_type):
282 _manage_registration('add', reg_type)
283
284 -def unregister_type(reg_type):
285 _manage_registration('remove', reg_type)
286
287 -def is_registered_type(reg_type):
288 return reg_type in _registered_types
289
290 ### Factory function; this is what should be used to dispatch 291 ### type-checker class requests 292 293 -def Type(obj):
294 # Note that registered types cannot count on being run in a certain order; 295 # their __typesig__ methods must be sufficiently flexible to account for 296 # this 297 for reg_type in _registered_hooks['__typesig__']: 298 v = reg_type.__typesig__(obj) 299 if v is not None: 300 return v 301 302 raise AssertionError("Object is of type '%s'; not a type" % str(type(obj)))
303
304 -def __checking(start_stop, *args):
305 attr = '__%schecking__' % start_stop 306 307 for reg_type in _registered_hooks[attr]: 308 getattr(reg_type, attr)(*args)
309
310 -def start_checking(function):
311 __checking('start', function)
312
313 -def stop_checking(function):
314 __checking('stop', function)
315
316 -def switch_checking(from_func, to_func):
317 for reg_type in _registered_types: 318 if hasattr(reg_type, '__switchchecking__'): 319 getattr(reg_type, '__switchchecking__')(from_func, to_func) 320 else: 321 if hasattr(reg_type, '__stopchecking__'): 322 getattr(reg_type, '__stopchecking__')(from_func) 323 if hasattr(reg_type, '__startchecking__'): 324 getattr(reg_type, '__startchecking__')(to_func)
325
326 ### Deduce the type of a data structure 327 ### 328 ### XXX: Find a way to allow registered utility classes 329 ### to hook into this 330 -def calculate_type(obj):
331 if isinstance(obj, types.InstanceType): 332 return obj.__class__ 333 elif isinstance(obj, dict): 334 if len(obj) == 0: 335 return {} 336 337 key_types = set() 338 val_types = set() 339 340 for (k,v) in obj.items(): 341 key_types.add( calculate_type(k) ) 342 val_types.add( calculate_type(v) ) 343 344 if len(key_types) == 1: 345 key_types = key_types.pop() 346 else: 347 key_types = Or(*key_types) 348 349 if len(val_types) == 1: 350 val_types = val_types.pop() 351 else: 352 val_types = Or(*val_types) 353 354 return {key_types: val_types} 355 elif isinstance(obj, tuple): 356 return tuple([calculate_type(t) for t in obj]) 357 elif isinstance(obj, list): 358 length = len(obj) 359 if length == 0: 360 return [] 361 obj = [calculate_type(o) for o in obj] 362 363 partitions = [1] 364 partitions.extend([i for i in range(2, int(length/2)+1) if length%i==0]) 365 partitions.append(length) 366 367 def evaluate(items_per): 368 parts = length / items_per 369 370 for i in range(0, parts): 371 for j in range(0, items_per): 372 if obj[items_per * i + j] != obj[j]: 373 raise StopIteration 374 return obj[0:items_per]
375 376 for items_per in partitions: 377 try: 378 return evaluate(items_per) 379 except StopIteration: 380 continue 381 else: 382 return type(obj) 383
384 ### The following classes are the work-horses of the typechecker 385 386 # The base class for all the other utility classes 387 -class CheckType(object):
388 - def __repr__(self):
389 return type(self).name + '(' + ', '.join(sorted(repr(t) for t in self._types)) + ')'
390 391 __str__ = __repr__ 392
393 - def __eq__(self, other):
394 return not self != other
395
396 - def __ne__(self, other):
397 return not self == other
398
399 - def __hash__(self):
400 raise NotImplementedError("Incomplete CheckType subclass: %s" % self.__class__)
401
402 - def __typecheck__(self, func, obj):
403 raise NotImplementedError("Incomplete CheckType subclass: %s" % self.__class__)
404 405 @classmethod
406 - def __typesig__(cls, obj):
407 if isinstance(obj, CheckType): 408 return obj
409
410 -class Single(CheckType):
411 name = "Single" 412
413 - def __init__(self, type):
414 if not isinstance(type, (types.ClassType, types.TypeType)): 415 raise TypeError("Cannot type-check a %s" % type(type)) 416 else: 417 self.type = type 418 419 self._types = [self.type]
420
421 - def __typecheck__(self, func, to_check):
422 if not isinstance(to_check, self.type): 423 raise _TC_TypeError(to_check, self.type)
424
425 - def __eq__(self, other):
426 if other.__class__ is not self.__class__: 427 return False 428 return self.type == other.type
429
430 - def __hash__(self):
431 return hash(str(hash(self.__class__)) + str(hash(self.type)))
432 433 # XXX Is this really a good idea? 434 # Removing this only breaks 3 tests; that seems suspiciously low
435 - def __repr__(self):
436 return repr(self.type)
437 438 @classmethod
439 - def __typesig__(cls, obj):
440 if isinstance(obj, (types.ClassType, types.TypeType)): 441 return Single(obj)
442
443 ### Provide a way to enforce the empty-ness of iterators 444 -class Empty(Single):
445 name = "Empty" 446
447 - def __init__(self, type):
448 if not hasattr(type, '__len__'): 449 raise TypeError("Can only assert emptyness for types with __len__ methods") 450 451 Single.__init__(self, type)
452
453 - def __typecheck__(self, func, to_check):
454 Single.__typecheck__(self, func, to_check) 455 456 if len(to_check) > 0: 457 err = _TC_LengthError(len(to_check), 0) 458 if isinstance(to_check, dict): 459 raise _TC_DictError(err) 460 raise err
461
462 -class Dict(CheckType):
463 name = "Dict" 464
465 - def __init__(self, key, val):
466 self.__check_key = Type(key) 467 self.__check_val = Type(val) 468 469 self.type = {key: val} 470 self._types = [key, val]
471
472 - def __typecheck__(self, func, to_check):
473 if not isinstance(to_check, types.DictType): 474 raise _TC_TypeError(to_check, self.type) 475 476 for (k, v) in to_check.items(): 477 # Check the key 478 try: 479 check_type(self.__check_key, func, k) 480 except _TC_Exception, inner: 481 raise _TC_KeyError(k, inner) 482 483 # Check the value 484 try: 485 check_type(self.__check_val, func, v) 486 except _TC_Exception, inner: 487 raise _TC_KeyValError(k, v, inner)
488
489 - def __eq__(self, other):
490 if other.__class__ is not self.__class__: 491 return False 492 return self.type == other.type
493
494 - def __hash__(self):
495 cls = self.__class__ 496 key = self.__check_key 497 val = self.__check_val 498 499 def strhash(obj): 500 return str(hash(obj))
501 502 return hash(''.join(map(strhash, [cls, key, val])))
503 504 @classmethod
505 - def __typesig__(cls, obj):
506 if isinstance(obj, dict): 507 if len(obj) == 0: 508 return Empty(dict) 509 return Dict(obj.keys()[0], obj.values()[0])
510
511 ### Provide typechecking for the built-in list() type 512 -class List(CheckType):
513 name = "List" 514
515 - def __init__(self, *type):
516 self._types = [Type(t) for t in type] 517 self.type = [t.type for t in self._types]
518
519 - def __typecheck__(self, func, to_check):
520 if not isinstance(to_check, list): 521 raise _TC_TypeError(to_check, self.type) 522 if len(to_check) % len(self._types): 523 raise _TC_LengthError(len(to_check)) 524 525 # lists can be patterned, meaning that [int, float] 526 # requires that the to-be-checked list contain an alternating 527 # sequence of integers and floats. The pattern must be completed 528 # (e.g, [5, 5.0, 6, 6.0] but not [5, 5.0, 6]) for the list to 529 # typecheck successfully. 530 # 531 # A list with a single type, [int], is a sub-case of patterned 532 # lists 533 # 534 # XXX: Investigate speed increases by special-casing single-typed 535 # lists 536 pat_len = len(self._types) 537 type_tuples = [(i, val, self._types[i % pat_len]) for (i, val) 538 in enumerate(to_check)] 539 for (i, val, type) in type_tuples: 540 try: 541 check_type(type, func, val) 542 except _TC_Exception, e: 543 raise _TC_IndexError(i, e)
544
545 - def __eq__(self, other):
546 if other.__class__ is not self.__class__: 547 return False 548 549 if len(self._types) != len(other._types): 550 return False 551 552 for (s, o) in zip(self._types, other._types): 553 if s != o: 554 return False 555 return True
556
557 - def __hash__(self):
558 def strhash(obj): 559 return str(hash(obj))
560 561 return hash(''.join(map(strhash, [self.__class__] + self._types)))
562 563 @classmethod
564 - def __typesig__(cls, obj):
565 if isinstance(obj, list): 566 if len(obj) == 0: 567 return Empty(list) 568 return List(*obj)
569
570 ### Provide typechecking for the built-in tuple() class 571 -class Tuple(List):
572 name = "Tuple" 573
574 - def __init__(self, *type):
575 List.__init__(self, *type) 576 577 self.type = tuple(self.type)
578
579 - def __typecheck__(self, func, to_check):
580 # Note that tuples of varying length (e.g., (int, int) and (int, int, int)) 581 # are separate types, not merely differences in length like lists 582 if not isinstance(to_check, types.TupleType) or len(to_check) != len(self._types): 583 raise _TC_TypeError(to_check, self.type) 584 585 for (i, (val, type)) in enumerate(zip(to_check, self._types)): 586 try: 587 check_type(type, func, val) 588 except _TC_Exception, inner: 589 raise _TC_IndexError(i, inner)
590 591 @classmethod
592 - def __typesig__(cls, obj):
593 if isinstance(obj, tuple): 594 return Tuple(*obj)
595
596 -class TypeVariables(CheckType):
597 # This is a stack of {typevariable -> type} mappings 598 # It is intentional that it is class-wide; it maintains 599 # the mappings of the outer functions if we descend into 600 # nested typechecked functions 601 __mapping_stack = [] 602 603 # This is the {typevariable -> type} mapping for the function 604 # currently being checked 605 __active_mapping = None 606 607 # This dict maps generators to their mappings 608 __gen_mappings = {} 609
610 - def __init__(self, name):
611 self.type = name
612
613 - def __str__(self):
614 return "TypeVariable(%s)" % self.type
615 616 __repr__ = __str__ 617
618 - def __hash__(self):
619 return hash(''.join([str(o) for o in self.__class__ 620 , hash(type(self.type)) 621 , hash(self.type)]))
622
623 - def __eq__(self, other):
624 if self.__class__ is not other.__class__: 625 return False 626 return type(self.type) is type(other.type) and self.type == other.type
627
628 - def __typecheck__(self, func, to_check):
629 name = self.type 630 if isinstance(func, GeneratorType): 631 active = self.__class__.__gen_mappings[func] 632 else: 633 active = self.__class__.__active_mapping 634 635 # We have to do this because u'a' == 'a' 636 lookup = (name, type(name)) 637 if lookup in active: 638 check_type(active[lookup], func, to_check) 639 else: 640 # This is the first time we've encountered this 641 # typevariable for this function call. 642 # 643 # In this case, we automatically approve the object 644 active[lookup] = Type(calculate_type(to_check))
645 646 @classmethod
647 - def __typesig__(cls, obj):
648 if isinstance(obj, basestring): 649 return cls(obj)
650 651 @classmethod
652 - def __startchecking__(cls, func):
653 if isinstance(func, GeneratorType): 654 cls.__gen_mappings.setdefault(func, {}) 655 elif isinstance(func, FunctionType): 656 cls.__mapping_stack.append(cls.__active_mapping) 657 cls.__active_mapping = {} 658 else: 659 raise TypeError(func)
660 661 @classmethod
662 - def __switchchecking__(cls, from_func, to_func):
663 if isinstance(from_func, FunctionType): 664 if isinstance(to_func, GeneratorType): 665 cls.__gen_mappings[to_func] = cls.__active_mapping 666 cls.__stopchecking__(from_func) 667 elif isinstance(to_func, FunctionType): 668 cls.__stopchecking__(from_func) 669 cls.__startchecking__(to_func) 670 else: 671 raise TypeError(to_func) 672 else: 673 raise TypeError(from_func)
674 675 @classmethod
676 - def __stopchecking__(cls, func):
677 if isinstance(func, GeneratorType): 678 del cls.__gen_mappings[func] 679 elif isinstance(func, FunctionType): 680 cls.__active_mapping = cls.__mapping_stack.pop() 681 else: 682 raise TypeError(func)
683
684 -class Function(CheckType):
685 - def __init__(self, func):
686 self._func = func 687 self.type = self
688 689 @classmethod
690 - def __typesig__(cls, obj):
691 if isinstance(obj, (FunctionType, MethodType)): 692 return cls(obj) 693 694 # Snag callable class instances (that aren't types or classes) 695 if type(obj) not in (types.ClassType, type) and callable(obj): 696 return cls(obj)
697
698 - def __typecheck__(self, func, to_check):
699 if False == self._func(to_check): 700 raise _TC_FunctionError(self._func, to_check)
701
702 - def __str__(self):
703 return "Function(%s)" % self._func
704
705 - def __repr__(self):
706 return str(self)
707
708 - def __eq__(self, other):
709 if self.__class__ is not other.__class__: 710 return False 711 return self._func is other._func
712
713 - def __hash__(self):
714 return hash(str(self.__class__) + str(hash(self._func)))
715 716 # Register some of the above types so that Type() knows about them 717 for c in (CheckType, List, Tuple, Dict, Single, TypeVariables, Function): 718 register_type(c)
719 720 ### The following are utility classes intended to make writing complex 721 ### signatures easier. 722 ###################################################################### 723 724 ### Instances of Any() automatically approve of the object they're supposed 725 ### to be checking (ie, they don't actually check it; use this with caution) 726 -class Any(CheckType):
727 name = "Any" 728
729 - def __init__(self):
730 self.type = object
731
732 - def __typecheck__(self, func, to_check):
733 pass
734
735 - def __str__(self):
736 return "Any()"
737 738 __repr__ = __str__ 739 740 # All instances of this class are equal
741 - def __eq__(self, other):
742 return other.__class__ is self.__class__
743
744 - def __hash__(self):
745 return hash(self.__class__)
746
747 ### Base class for Or() and And() 748 -class _Boolean(CheckType):
749 - def __init__(self, first_type, second_type, *types):
750 self._types = set() 751 752 for t in (first_type, second_type)+types: 753 if type(t) is type(self): 754 self._types.update(t._types) 755 else: 756 self._types.add(Type(t)) 757 758 if len(self._types) < 2: 759 raise TypeError("there must be at least 2 distinct parameters to __init__()") 760 761 self.type = self
762
763 - def __eq__(self, other):
764 if other.__class__ is not self.__class__: 765 return False 766 767 return self._types == other._types
768
769 - def __hash__(self):
770 return hash(str(hash(self.__class__)) + str(hash(frozenset(self._types))))
771
772 -class Or(_Boolean):
773 name = "Or" 774
775 - def __typecheck__(self, func, to_check):
776 for type in self._types: 777 try: 778 check_type(type, func, to_check) 779 return 780 except _TC_Exception: 781 pass 782 783 raise _TC_TypeError(to_check, self)
784
785 -class And(_Boolean):
786 name = "And" 787
788 - def __typecheck__(self, func, to_check):
789 for type in self._types: 790 try: 791 check_type(type, func, to_check) 792 except _TC_Exception, e: 793 raise _TC_TypeError(to_check, self)
794
795 -class Not(Or):
796 name = "Not" 797 798 # We override _Boolean's __init__ so that we can accept a single 799 # condition
800 - def __init__(self, first_type, *types):
801 self._types = set([Type(t) for t in (first_type,)+types]) 802 803 self.type = self
804
805 - def __typecheck__(self, func, to_check):
806 # Or does our work for us, but we invert its result 807 try: 808 Or.__typecheck__(self, func, to_check) 809 except _TC_Exception: 810 return 811 raise _TC_TypeError(to_check, self)
812
813 -class Xor(_Boolean):
814 name = "Xor" 815
816 - def __typecheck__(self, func, to_check):
817 already_met_1_cond = False 818 819 for typ in self._types: 820 try: 821 check_type(typ, func, to_check) 822 except _TC_Exception: 823 pass 824 else: 825 if already_met_1_cond: 826 raise _TC_XorError(2, _TC_TypeError(to_check, self)) 827 already_met_1_cond = True 828 829 if not already_met_1_cond: 830 raise _TC_XorError(0, _TC_TypeError(to_check, self))
831
832 -class IsCallable(CheckType):
833 - def __init__(self):
834 self.type = self
835
836 - def __str__(self):
837 return "IsCallable()"
838 839 __repr__ = __str__ 840 841 # They're all the same 842 # XXX Change IsCallable to a singleton class
843 - def __hash__(self):
844 return id(self.__class__)
845
846 - def __eq__(self, other):
847 return self.__class__ is other.__class__
848
849 - def __typecheck__(self, func, to_check):
850 if not callable(to_check): 851 raise _TC_TypeError(to_check, 'a callable')
852
853 -class HasAttr(CheckType):
854 - def __init__(self, set_1, set_2=None):
855 attr_sets = {list: [], dict: {}} 856 857 for (arg_1, arg_2) in ((set_1, set_2), (set_2, set_1)): 858 for t in (list, dict): 859 if isinstance(arg_1, t): 860 attr_sets[t] = arg_1 861 if isinstance(arg_2, t): 862 raise TypeError("can only have one list and/or one dict") 863 864 self._attr_types = dict.fromkeys(attr_sets[list], Any()) 865 866 for (attr, typ) in attr_sets[dict].items(): 867 self._attr_types[attr] = Type(typ)
868
869 - def __typecheck__(self, func, to_check):
870 for (attr, typ) in self._attr_types.items(): 871 if not hasattr(to_check, attr): 872 raise _TC_MissingAttrError(attr) 873 874 try: 875 check_type(typ, func, getattr(to_check, attr)) 876 except _TC_Exception, e: 877 raise _TC_AttrError(attr, e)
878
879 - def __eq__(self, other):
880 if self.__class__ is not other.__class__: 881 return False 882 return self._attr_types == other._attr_types
883
884 - def __hash__(self):
885 return hash(str(hash(self.__class__)) + str(hash(str(self._attr_types))))
886
887 - def __str__(self):
888 any_type = [] 889 spec_type = {} 890 891 any = Any() 892 893 for (attr, typ) in self._attr_types.items(): 894 if typ == any: 895 any_type.append(attr) 896 else: 897 spec_type[attr] = typ 898 899 msg = [t for t in (any_type, spec_type) if len(t)] 900 901 return "HasAttr(" + ', '.join(map(str, msg)) + ")"
902 903 __repr__ = __str__
904
905 -class IsIterable(CheckType):
906 - def __init__(self):
907 self.type = self
908
909 - def __eq__(self, other):
910 return self.__class__ is other.__class__
911 912 # They're all the same 913 # XXX Change IsIterable to a singleton class
914 - def __hash__(self):
915 return id(self.__class__)
916
917 - def __str__(self):
918 return "IsIterable()"
919 920 __repr__ = __str__ 921
922 - def __typecheck__(self, func, to_check):
923 if not (hasattr(to_check, '__iter__') and callable(to_check.__iter__)): 924 raise _TC_TypeError(to_check, "an iterable")
925
926 -class YieldSeq(CheckType):
927 _index_map = {} 928
929 - def __init__(self, type_1, type_2, *types):
930 self.type = self 931 932 self._type = [type_1, type_2] + list(types) 933 self._types = [Type(t) for t in self._type]
934
935 - def __hash__(self):
936 return id(self)
937
938 - def __str__(self):
939 return "YieldSeq(" + ", ".join(map(str, self._type)) + ")"
940 941 __repr__ = __str__ 942
943 - def __eq__(self, other):
944 if self.__class__ is not other.__class__: 945 return False 946 return self._types == other._types
947
948 - def __hash__(self):
949 return hash(str(self.__class__) + str([hash(t) for t in self._types]))
950 951 # We have to use __{start,stop}checking__ so that the indexes get 952 # reset every time we run through the typechecking sequence 953 @classmethod
954 - def __startchecking__(cls, gen):
955 if isinstance(gen, GeneratorType): 956 cls._index_map[gen] = {}
957 958 @classmethod
959 - def __stopchecking__(cls, gen):
960 if gen in cls._index_map: 961 del cls._index_map[gen]
962
963 - def __typecheck__(self, gen, to_check):
964 index_map = self.__class__._index_map 965 966 # There might be multiple YieldSeq's per signature 967 if self not in index_map[gen]: 968 index_map[gen][self] = -1 969 index = index_map[gen] 970 971 if index[self] >= len(self._types)-1: 972 raise _TC_YieldCountError(len(self._types)) 973 974 index[self] += 1 975 check_type(self._types[index[self]], gen, to_check)
976 977 register_type(YieldSeq)
978 979 -class Exact(CheckType):
980 - def __init__(self, obj):
981 self.type = self 982 self._obj = obj
983
984 - def __hash__(self):
985 try: 986 obj_hash = str(hash(self._obj)) 987 except TypeError: 988 obj_hash = str(type(self._obj)) + str(self._obj) 989 990 return hash(str(self.__class__) + obj_hash)
991
992 - def __eq__(self, other):
993 if self.__class__ is not other.__class__: 994 return False 995 return self._obj == other._obj
996
997 - def __typecheck__(self, func, to_check):
998 if self._obj != to_check: 999 raise _TC_ExactError(to_check, self._obj)
1000
1001 -class Length(CheckType):
1002 - def __init__(self, length):
1003 self.type = self 1004 self._length = int(length)
1005
1006 - def __hash__(self):
1007 return hash(str(self.__class__) + str(self._length))
1008
1009 - def __eq__(self, other):
1010 if self.__class__ is not other.__class__: 1011 return False 1012 return self._length == other._length
1013
1014 - def __typecheck__(self, func, to_check):
1015 try: 1016 length = len(to_check) 1017 except TypeError: 1018 raise _TC_TypeError(to_check, "something with a __len__ method") 1019 1020 if length != self._length: 1021 raise _TC_LengthError(length, self._length)
1022 1023 import sys
1024 -class Class(CheckType):
1025 - def __init__(self, class_name):
1026 self.type = self 1027 self.class_name = class_name 1028 self.class_obj = None 1029 self._frame = sys._getframe(1)
1030
1031 - def __hash__(self):
1032 return hash(str(self.__class__) + self.class_name)
1033
1034 - def __str__(self):
1035 return "Class('%s')" % self.class_name
1036 1037 __repr__ = __str__ 1038
1039 - def __eq__(self, other):
1040 if self.__class__ is not other.__class__: 1041 return False 1042 return self.class_name == other.class_name
1043
1044 - def __typecheck__(self, func, to_check):
1045 if self.class_obj is None: 1046 class_name = self.class_name 1047 frame = self._frame 1048 1049 for f_dict in (frame.f_locals, frame.f_globals): 1050 if class_name in frame.f_locals: 1051 if self is not frame.f_locals[class_name]: 1052 self.class_obj = frame.f_locals[class_name] 1053 self._frame = None 1054 break 1055 else: 1056 raise NameError("name '%s' is not defined" % class_name) 1057 1058 if not isinstance(to_check, self.class_obj): 1059 raise _TC_TypeError(to_check, self.class_obj)
1060
1061 -class Typeclass(CheckType):
1062 bad_members = dict.fromkeys(['__class__', '__new__', '__init__'], True) 1063
1064 - def __init__(self, *types):
1065 if len(types) == 0: 1066 raise TypeError("Must supply at least one type to __init__()") 1067 1068 self.type = self 1069 1070 self._cache = set() 1071 self._interface = set() 1072 self._instances = set() 1073 for t in types: 1074 self.add_instance(t) 1075 1076 self._calculate_interface()
1077
1078 - def recalculate_interface(self):
1079 self._cache = self._instances.copy() 1080 self._calculate_interface()
1081
1082 - def instances(self):
1083 return list(self._instances)
1084
1085 - def interface(self):
1086 return list(self._interface)
1087
1088 - def has_instance(self, instance):
1089 return instance in self._instances
1090
1091 - def add_instance(self, instance):
1092 if isinstance(instance, self.__class__): 1093 for inst in instance.instances(): 1094 self._instances.add(inst) 1095 self._cache.add(inst) 1096 elif isinstance(instance, (ClassType, TypeType)): 1097 self._instances.add(instance) 1098 self._cache.add(instance) 1099 else: 1100 raise TypeError("All instances must be classes or types")
1101
1102 - def intersect(self, other):
1103 if isinstance(other, self.__class__): 1104 new_instances = other.instances() 1105 else: 1106 new_instances = other 1107 1108 self._instances.update(new_instances) 1109 self._cache.update(new_instances) 1110 self._calculate_interface()
1111
1112 - def _calculate_interface(self):
1113 bad_members = self.bad_members 1114 1115 for instance in self._instances: 1116 inst_attrs = [] 1117 1118 for attr, obj in instance.__dict__.items(): 1119 if callable(obj) and attr not in bad_members: 1120 inst_attrs.append(attr) 1121 1122 if len(self._interface) == 0: 1123 self._interface = set(inst_attrs) 1124 else: 1125 self._interface.intersection_update(inst_attrs)
1126
1127 - def __typecheck__(self, func, to_check):
1128 if to_check.__class__ in self._cache: 1129 return 1130 1131 for method in self._interface: 1132 if not hasattr(to_check, method): 1133 raise _TC_MissingAttrError(method) 1134 1135 attr = getattr(to_check, method) 1136 if not callable(attr): 1137 raise _TC_AttrError(method, _TC_TypeError(attr, IsCallable())) 1138 1139 self._cache.add(to_check.__class__)
1140
1141 - def __eq__(self, other):
1142 if self.__class__ is not other.__class__: 1143 return False 1144 return self._instances == other._instances
1145
1146 - def __hash__(self):
1147 return hash(str(self.__class__) + str(hash(frozenset(self._instances))))
1148
1149 - def __repr__(self):
1150 return object.__repr__(self)
1151
1152 - def __str__(self):
1153 return 'Typeclass(' + ', '.join(map(str, self._instances)) + ')'
1154
1155 # The current implementation of Self relies on the TypeVariables machinery 1156 _Self = TypeVariables("this is the class of the invocant") 1157 -def Self():
1158 return _Self
1159 1160 ### Aliases 1161 ########### 1162 1163 IsOneOf = Or 1164 IsAllOf = And 1165 IsNoneOf = Not 1166 IsOnlyOneOf = Xor
1167 1168 ### This is the public side of the module 1169 ######################################### 1170 1171 # This is for backwards compatibility with v0.1.6 and earlier 1172 -class TypeCheckException(Exception):
1173 pass
1174
1175 -class TypeCheckError(TypeCheckException):
1176 - def __init__(self, prefix, bad_object, exception):
1177 TypeCheckException.__init__(self, prefix, bad_object, exception) 1178 1179 self.prefix = prefix 1180 self.internal = exception 1181 self.bad_object = bad_object 1182 1183 (bad_obj_str, start_message) = exception.format_bad_object(bad_object) 1184 self.__message = prefix + bad_obj_str + start_message.error_message()
1185
1186 - def __str__(self):
1187 return self.__message
1188
1189 -class TypeSignatureError(Exception):
1190 - def __init__(self, internal_exc):
1191 Exception.__init__(self, internal_exc) 1192 1193 self.internal = internal_exc 1194 self.__message = internal_exc.error_message()
1195
1196 - def __str__(self):
1197 return self.__message
1198
1199 ### Begin helper classes/functions for typecheck_args 1200 ##################################################### 1201 -def _rec_tuple(obj):
1202 if isinstance(obj, list): 1203 return tuple(_rec_tuple(o) for o in obj) 1204 return obj
1205
1206 -def _rec_tuple_str(obj):
1207 if not isinstance(obj, (list, tuple)): 1208 return obj 1209 1210 if len(obj) == 1: 1211 return '(%s,)' % obj 1212 1213 return '(' + ', '.join(_rec_tuple_str(o) for o in obj) + ')'
1214
1215 -def _gen_arg_to_param(func, (posargs, varargs, varkw, defaults)):
1216 sig_args = list() 1217 dic_args = list() 1218 1219 for obj in posargs: 1220 if isinstance(obj, list): 1221 rts = _rec_tuple_str(obj) 1222 1223 sig_args.append(rts) 1224 dic_args.append((_rec_tuple(obj), rts)) 1225 else: 1226 sig_args.append(str(obj)) 1227 dic_args.append(('"%s"' % obj, obj)) 1228 1229 func_code = '' 1230 if varargs: 1231 dic_args.append(('"%s"' % varargs, varargs)) 1232 sig_args.append('*' + varargs) 1233 func_code = '\n\t%s = list(%s)' % (varargs, varargs) 1234 if varkw: 1235 dic_args.append(('"%s"' % varkw, varkw)) 1236 sig_args.append('**' + varkw) 1237 1238 func_name = func.func_name + '_' 1239 while func_name in dic_args: 1240 func_name += '_' 1241 1242 func_def = 'def %s(' % func.func_name 1243 func_return = func_code \ 1244 + '\n\treturn {' \ 1245 + ', '.join('%s: %s' % kv for kv in dic_args) \ 1246 + '}' 1247 1248 locals = {} 1249 exec func_def + ','.join(sig_args) + '):' + func_return in locals 1250 func = locals[func.func_name] 1251 func.func_defaults = defaults 1252 return func
1253
1254 -def _validate_tuple(ref, obj):
1255 if not isinstance(ref, (list, tuple)): 1256 return 1257 if not isinstance(obj, (list, tuple)): 1258 raise _TS_TupleError(ref, obj) 1259 1260 if len(ref) != len(obj): 1261 raise _TS_TupleError(ref, obj) 1262 1263 try: 1264 for r, o in zip(ref, obj): 1265 _validate_tuple(r, o) 1266 except _TS_TupleError: 1267 raise _TS_TupleError(ref, obj)
1268
1269 -def _param_to_type((params, varg_name, kwarg_name), vargs, kwargs):
1270 vargs = list(vargs) 1271 kwargs = dict(kwargs) 1272 1273 # Make parameter names to values 1274 param_value = dict() 1275 1276 # There are excess positional arguments, but no *args parameter 1277 if len(params) < len(vargs) and varg_name is None: 1278 raise _TS_ExtraPositionalError(vargs[len(params)]) 1279 # There are not enough position args and no kwargs to draw from 1280 if len(params) > len(vargs) and len(kwargs) == 0: 1281 raise _TS_MissingTypeError(params[len(vargs)]) 1282 1283 # No reason to do this if there aren't any vargs 1284 if len(vargs): 1285 for p, a in zip(params, vargs): 1286 # Make sure all auto-unpacked tuples match up 1287 _validate_tuple(p, a) 1288 param_value[_rec_tuple(p)] = a 1289 1290 # No reason to do all this work if there aren't any kwargs 1291 if len(kwargs) > 0: 1292 # All params that still need values 1293 params = set([k for k in params if k not in param_value]) 1294 if kwarg_name and kwarg_name not in param_value: 1295 params.add(kwarg_name) 1296 if varg_name and varg_name not in param_value: 1297 params.add(varg_name) 1298 1299 # Lift this out of the loop 1300 no_double_star = kwarg_name is None 1301 1302 # All parameter slots have been filled, but there are still keyword 1303 # args remaining with no **kwargs parameter present 1304 if len(params) == 0 and no_double_star: 1305 raise _TS_ExtraKeywordError(kwargs.keys()[0]) 1306 1307 # Match up remaining keyword args with open parameter slots 1308 for p, a in kwargs.items(): 1309 if p in param_value: 1310 raise _TS_TwiceTypedError(p, a, param_value[p]) 1311 if p not in params and no_double_star: 1312 raise _TS_ExtraKeywordError(p) 1313 1314 # Make sure all auto-unpacked tuples match up 1315 _validate_tuple(p, a) 1316 1317 # Bookkeeping 1318 params.remove(p) 1319 param_value[p] = a 1320 1321 # Any elements left in params indicate that the parameter is missing 1322 # a value 1323 if len(params): 1324 raise _TS_MissingTypeError(params.pop()) 1325 1326 return param_value
1327
1328 -def _make_fake_function(func):
1329 def fake_function(*vargs, **kwargs): 1330 # We call start_checking here, but __check_result 1331 # has to call stop_checking on its own. The reason 1332 # for this is so that typecheck_yield can call 1333 # stop_checking on the function and then start_checking 1334 # on the generator 1335 start_checking(func) 1336 1337 # If either one of these operations fails, we need to call 1338 # stop_checking() 1339 try: 1340 fake_function.__check_args(vargs, kwargs) 1341 result = func(*vargs, **kwargs) 1342 except: 1343 stop_checking(func) 1344 raise 1345 1346 return fake_function.__check_result(func, result)
1347 1348 # These are the default implementations of __check_args 1349 # and __check_results 1350 def _pass_args(vargs, kwargs): 1351 pass 1352 def _pass_result(func, result): 1353 stop_checking(func) 1354 return result 1355 1356 fake_function.__check_args = _pass_args 1357 fake_function.__check_result = _pass_result 1358 fake_function.__wrapped_func = func 1359 1360 # Mock-up the fake function to look as much like the 1361 # real function as possible 1362 fake_function.__module__ = func.__module__ 1363 fake_function.__name__ = func.__name__ 1364 fake_function.__doc__ = func.__doc__ 1365 1366 return fake_function 1367
1368 ################################################### 1369 ### End helper classes/functions for typecheck_args 1370 1371 -def typecheck_args(*v_sig, **kw_sig):
1372 # typecheck_args is run to obtain the real decorator 1373 def decorator(func): 1374 if hasattr(func, '__wrapped_func'): 1375 if hasattr(func, 'type_args'): 1376 raise RuntimeError('Cannot use the same typecheck_* function more than once on the same function') 1377 wrapped_func = func.__wrapped_func 1378 else: 1379 wrapped_func = func 1380 1381 param_list, varg_name, kwarg_name, defaults = inspect.getargspec(wrapped_func) 1382 args_to_params = _gen_arg_to_param(wrapped_func, (param_list, varg_name, kwarg_name, defaults)) 1383 1384 try: 1385 param_types = _param_to_type((param_list, varg_name, kwarg_name), v_sig, kw_sig) 1386 except _TS_Exception, e: 1387 raise TypeSignatureError(e) 1388 1389 ### We need to fix-up the types of the *vargs and **kwargs parameters 1390 ##################################################################### 1391 if varg_name: 1392 if not isinstance(param_types[varg_name], list): 1393 param_types[varg_name] = [param_types[varg_name]] 1394 1395 if kwarg_name: 1396 if not isinstance(param_types[kwarg_name], dict): 1397 param_types[kwarg_name] = {str: param_types[kwarg_name]} 1398 1399 ##################################################################### 1400 ### /Fix-up 1401 1402 # Convert the signatures to types now, rather than rebuild them in every function call 1403 check_param_types = dict() 1404 for k, v in param_types.items(): 1405 check_param_types[k] = Type(v) 1406 1407 def __check_args(__vargs, __kwargs): 1408 # Type-checking can be turned on and off by toggling the 1409 # value of the global enable_checking variable 1410 if enable_checking: 1411 arg_dict = args_to_params(*__vargs, **__kwargs) 1412 1413 # Type-check the keyword arguments 1414 try: 1415 for name, val in arg_dict.items(): 1416 check_type(check_param_types[name], wrapped_func, val) 1417 except _TC_Exception, e: 1418 str_name = _rec_tuple_str(name) 1419 raise TypeCheckError("Argument %s: " % str_name, val, e)
1420 1421 if hasattr(func, '__check_result'): 1422 # This is one of our wrapper functions, probably created by 1423 # typecheck_yield or typecheck_return 1424 fake_function = func 1425 else: 1426 # We need to build a wrapper 1427 fake_function = _make_fake_function(func) 1428 1429 # Specify how argument checking should be done 1430 fake_function.__check_args = __check_args 1431 1432 ### Add the publically-accessible signature information 1433 fake_function.type_args = param_types 1434 1435 return fake_function 1436 return decorator 1437
1438 # Refactor this out of typecheck_{return,yield} 1439 -def _decorator(signature, conflict_field, twice_field, check_result_func):
1440 def decorator(func): 1441 if hasattr(func, '__check_result'): 1442 # This is one of our wrapper functions, probably created by 1443 # typecheck_args 1444 if hasattr(func, conflict_field): 1445 raise RuntimeError("Cannot use typecheck_return and typecheck_yield on the same function") 1446 elif hasattr(func, twice_field): 1447 raise RuntimeError('Cannot use the same typecheck_* function more than once on the same function') 1448 1449 fake_function = func 1450 else: 1451 fake_function = _make_fake_function(func) 1452 1453 setattr(fake_function, twice_field, signature) 1454 fake_function.__check_result = check_result_func 1455 return fake_function
1456 return decorator 1457
1458 -def typecheck_return(*signature):
1459 if len(signature) == 1: 1460 signature = signature[0] 1461 sig_types = Type(signature) 1462 1463 def __check_return(func, return_vals): 1464 if enable_checking: 1465 try: 1466 check_type(sig_types, func, return_vals) 1467 except _TC_Exception, e: 1468 stop_checking(func) 1469 raise TypeCheckError("Return value: ", return_vals, e) 1470 1471 stop_checking(func) 1472 return return_vals
1473 return _decorator(signature, 'type_yield', 'type_return', __check_return) 1474
1475 -class Fake_generator(object):
1476 - def __init__(self, real_gen, signature):
1477 # The generator should have the same yield signature 1478 # as the function that produced it; however, we don't 1479 # copy the args signature because the generator 1480 # doesn't take arguments 1481 self.type_yield = signature 1482 1483 self.__yield_no = 0 1484 self.__real_gen = real_gen 1485 self.__sig_types = Type(signature) 1486 self.__needs_stopping = True
1487
1488 - def next(self):
1489 gen = self.__real_gen 1490 1491 self.__yield_no += 1 1492 1493 try: 1494 return_vals = gen.next() 1495 except StopIteration: 1496 if self.__needs_stopping: 1497 stop_checking(gen) 1498 self.__needs_stopping = False 1499 raise 1500 1501 if enable_checking: 1502 try: 1503 check_type(self.__sig_types, gen, return_vals) 1504 except _TC_Exception, e: 1505 # Insert this error into the chain so we can know 1506 # which yield the error occurred at 1507 middle_exc = _TC_GeneratorError(self.__yield_no, e) 1508 raise TypeCheckError("", return_vals, middle_exc) 1509 1510 # Everything checks out. Return the results 1511 return return_vals
1512
1513 - def __del__(self):
1514 if self.__needs_stopping: 1515 stop_checking(self.__real_gen)
1516
1517 -def typecheck_yield(*signature):
1518 if len(signature) == 1: 1519 signature = signature[0] 1520 1521 def __check_yield(func, gen): 1522 # If the return value isn't a generator, we blow up 1523 if not isinstance(gen, types.GeneratorType): 1524 stop_checking(func) 1525 raise TypeError("typecheck_yield only works for generators") 1526 1527 # Inform all listening classes that they might want to preserve any information 1528 # from the function to the generator (*hint* TypeVariables *hint*) 1529 # 1530 # stop_checking() will not be invoked on the generator until it raises 1531 # StopIteration or its refcount drops to 0 1532 switch_checking(func, gen) 1533 1534 # Otherwise, we build ourselves a fake generator 1535 return Fake_generator(gen, signature)
1536 return _decorator(signature, 'type_return', 'type_yield', __check_yield) 1537 1538 _null_decorator = lambda *args, **kwargs: lambda f: f 1539 typecheck = _null_decorator 1540 accepts = _null_decorator 1541 returns = _null_decorator 1542 yields = _null_decorator
1543 1544 # Aliases 1545 -def enable_typechecking():
1546 global typecheck 1547 global accepts 1548 global returns 1549 global yields 1550 1551 typecheck = typecheck_args 1552 accepts = typecheck_args 1553 returns = typecheck_return 1554 yields = typecheck_yield
1555 1556 import os 1557 if "PYTHONTYPECHECK" in os.environ: 1558 enable_typechecking() 1559