1
2 """Programming-by-contract for Python, based on Eiffel's DBC.
3
4 Programming by contract documents class and modules with invariants,
5 expressions that must be true during the lifetime of a module or
6 instance; and documents functions and methods with pre- and post-
7 conditions that must be true during entry and return.
8
9 Copyright (c) 2003, Terence Way
10 This module is free software, and you may redistribute it and/or modify
11 it under the same terms as Python itself, so long as this copyright message
12 and disclaimer are retained in their original form.
13
14 IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
15 SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
16 THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 DAMAGE.
18
19 THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
20 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
21 PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
22 AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
23 SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 """
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 __author__ = "Terence Way"
91 __email__ = "terry@wayforward.net"
92 __version__ = "1.1: October 19, 2005"
93 MODULE = 'contract'
94
95 import new
96 import re
97 import sys
98 import tokenize
99
100 from cStringIO import StringIO
101 from types import *
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 INV = 'inv'
204 PRE = 'pre'
205 POST = 'post'
206 TYPE_CONTRACTS = [INV]
207 CODE_CONTRACTS = [PRE, POST]
208 OLD = '__old__'
209 RETURN = '__return__'
210 PREFIX = '__assert_'
211
212
213 CHECK_DEFAULT, CHECK_NONE, CHECK_PRECONDITIONS, CHECK_ALL = range(4)
214
215 _ORIG = 'orig'
216 _SAVE = 'save'
217 _CHK = 'chk'
218
219 _CONTRACTS = tuple(TYPE_CONTRACTS + CODE_CONTRACTS)
220
221
222
223 _re_start = re.compile(r'^\s*(%s|%s|%s)\s*(\[|:)' % _CONTRACTS,
224 re.MULTILINE)
225 _RE_KEYWORD = 1
226
227
228
229
230
231 _OPS = {':': tokenize.COLON,
232 '[': tokenize.LSQB,
233 ']': tokenize.RSQB,
234 '.': tokenize.DOT,
235 ',': tokenize.COMMA}
236
237 _EXCEPTIONS = {PRE: 'PreconditionViolationError',
238 POST: 'PostconditionViolationError',
239 INV: 'InvariantViolationError'}
240
241
242 try:
243 bool, True, False = bool, True, False
244 except NameError:
245 False, True = 0, 1
248
249
250
251
252
253
255 """Add invariant, pre- and post-condition checking to a module.
256
257 pre::
258 isstring(module) or isinstance(module, ModuleType)
259 checklevel in [CHECK_DEFAULT, CHECK_NONE, CHECK_PRECONDITIONS,
260 CHECK_ALL]
261 """
262
263 if isinstance(module, StringType) or isinstance(module, UnicodeType):
264
265 __import__(module)
266 module = sys.modules[module]
267
268
269
270
271 if checklevel == CHECK_DEFAULT:
272 if __debug__:
273 checklevel = CHECK_ALL
274 else:
275 checklevel = CHECK_PRECONDITIONS
276
277
278 if checklevel != CHECK_NONE:
279
280 path = [module]
281 members = _get_members(module, path)
282 invs = parse_docstring(module.__doc__, TYPE_CONTRACTS)[0]
283 name = PREFIX + INV
284 func = getattr(module, name, None)
285
286 if not func or func.__name__.startswith(PREFIX):
287 if invs[2]:
288 func = _define_checker(name, '', invs, path)
289 else:
290 func = __assert_inv
291 module.__assert_inv = func
292 _check_members(members, path, checklevel)
293
294 func()
295
297 """Modify a class to add invariant checking.
298
299 pre::
300 isstring(code[0])
301 type(code[1]) == code[2]
302 isinstance(code[0], MethodType) or isinstance(code[0], FunctionType)
303 isinstance(path[0], ModuleType)
304 forall(path[1:], isclass)
305 """
306 name, obj = code
307
308
309 path = path + [obj]
310 members = _get_members(obj, path)
311 invs = parse_docstring(obj.__doc__, TYPE_CONTRACTS)[0]
312 if invs[2]:
313 func = _define_checker(_mkname(path, INV), 'self', invs, path)
314 setattr(obj, PREFIX + INV, func)
315 delattr(path[0], func.func_name)
316 _check_members(members, path, checklevel)
317
324
326 """Get function location as tuple (name, filename, lineno).
327
328 pre::
329 isinstance(f, MethodType) or isinstance(f, FunctionType)
330 post[]::
331 isstring(__return__[0])
332 isstring(__return__[1])
333 isinstance(__return__[2], int)
334 """
335 if isinstance(f, MethodType):
336 f = f.im_func
337 c = f.func_code
338 return f.func_name, c.co_filename, c.co_firstlineno
339
341 """Returns two lists (procs, types) where each list contains (name,
342 value) tuples.
343
344 For classes, only attributes defined by the specific class are returned,
345 i.e. not inherited attributes. Attributes created by this module
346 (prefixed by '__assert_') are skipped as well.
347
348 Examples:
349 >>> import contract
350 >>> path = [contract]
351 >>> hasattr(contract, '_re_start')
352 1
353 >>> '_re_start' in [x[0] for x in _get_members(contract, path)[0]]
354 0
355 >>> '_get_members' in [x[0] for x in _get_members(contract, path)[0]]
356 1
357 >>> 'checkmod' in [x[0] for x in _get_members(contract, path)[0]]
358 1
359 >>> class base:
360 ... def foo(self): pass
361 >>> class derived(base):
362 ... def bar(self): pass
363
364 hasattr can get inherited attributes:
365 >>> hasattr(derived, 'foo')
366 1
367
368 but we don't:
369 >>> path = [__import__('__main__')]
370 >>> 'foo' in [x[0] for x in _get_members(derived, path)[0]]
371 0
372 """
373 module_name = path[0].__name__
374 module_dict = path[0].__dict__
375 parent = path[-1]
376 procs = []
377 types = []
378 for key in obj.__dict__.keys():
379 if not key.startswith(PREFIX):
380 m = getattr(obj, key)
381
382 if (isinstance(m, MethodType) and
383 m.im_class is parent) or \
384 (isinstance(m, FunctionType) and \
385 m.func_globals is module_dict):
386 procs.append((key, m))
387 elif isclass(m) and getattr(m, '__module__', None) is module_name:
388 types.append((key, m))
389
390 return (procs, types)
391
393 for p in procs:
394 _check_proc(p, path, checklevel)
395 for t in types:
396 _check_type(t, path, checklevel)
397
399 """Checks if a name is public (starts and ends with '__' or doesn't
400 start with a _ at all).
401
402 Examples:
403 >>> _ispublic('__init__')
404 1
405 >>> _ispublic('foo')
406 1
407 >>> _ispublic('_ispublic')
408 0
409 """
410 return not name.startswith('_') or \
411 (name.startswith('__') and name.endswith('__'))
412
414 """Parse a docstring, looking for design-by-contract expressions.
415
416 Returns a list of tuples: the list is the same length as keywords, and
417 matches each keyword. The tuple is (keyword, [decls], [exprs]), namely
418 the keyword, a list of string declarations, and a list of tuples (string,
419 lineno).
420
421 Examples::
422 >>> from pprint import pprint
423 >>> pprint( parse_docstring(parse_docstring.__doc__, ['post', 'pre']) )
424 [('post', [], [('[ x [ 0 ] for x in __return__ ] == keywords', 22)]),
425 ('pre',
426 [],
427 [('docstring is None or isstring ( docstring )', 18),
428 ('forall ( keywords , isstring )', 19)])]
429
430 pre::
431 docstring is None or isstring(docstring)
432 forall(keywords, isstring)
433
434 post[]::
435 [x[0] for x in __return__] == keywords
436 """
437 result = [(x, [], []) for x in keywords]
438
439 if docstring is None:
440 return result
441
442
443 input = StringIO(docstring)
444
445 offset = 0
446 assert input.tell() == 0
447
448 line = input.readline()
449 lineno = 0
450 while line != '':
451 a = _re_start.split(line)
452
453 if len(a) > _RE_KEYWORD and a[_RE_KEYWORD] in keywords:
454
455
456
457 input.seek(offset)
458
459
460
461 try:
462 l = _read_block(input, lineno)
463 lineno = l[3]
464
465 except tokenize.TokenError, ex:
466
467 raise tokenize.TokenError(ex[0],
468 (lineno + ex[1][0],) + ex[1][1:])
469
470
471
472 r = result[keywords.index(l[0])]
473 r[1].extend(l[1])
474 r[2].extend(l[2])
475 else:
476 lineno += 1
477 offset = input.tell()
478 line = input.readline()
479 return result
480
481
482
483
484
485
486
487
488 -class Done(Exception): pass
489
491 - def __init__(self, input, startlineno):
492 self.input = input
493 self.startlineno = startlineno
494 self.endlineno = self.lineno = startlineno + 1
495 self.state = self.start
496 self.decl, self.decls, self.expr, self.exprs = [], [], [], []
497 self.keyword, self.offset = '', input.tell()
498
499 - def next(self, token, string, start, end, line):
500 if token != tokenize.COMMENT and token != tokenize.NL:
501 if token == tokenize.OP and _OPS.has_key(string):
502 token = _OPS[string]
503 self.state(token, string)
504 if token == tokenize.NEWLINE or token == tokenize.NL:
505 self.lineno = self.startlineno + start[0] + 1
506
507
508
509 - def start(self, token, string):
510 if token == tokenize.INDENT:
511 self.state = self.indent
512 else:
513 self.indent(token, string)
514
515 - def indent(self, token, string):
516 if token == tokenize.NAME:
517 self.keyword = string
518 self.state = self.name
519 else:
520 raise SyntaxError("expected pre, post, or inv")
521
522 - def name(self, token, string):
523 if token == tokenize.LSQB:
524 self.state = self.decl0
525 else:
526 self.colon(token, string)
527
528 - def decl0(self, token, string):
529 if token == tokenize.NAME:
530 self.decl.append(string)
531 self.state = self.decl1
532 elif token == tokenize.RSQB:
533 self.state = self.colon
534 else:
535 raise SyntaxError("expected variable name or ]")
536
537 - def decl1(self, token, string):
538 if token == tokenize.DOT:
539 self.state = self.decln
540 elif token == tokenize.COMMA:
541 self.decls.append(self.decl)
542 self.decl = []
543 self.state = self.decln
544 elif token == tokenize.RSQB:
545 self.decls.append(self.decl)
546 self.state = self.colon
547 else:
548 raise SyntaxError("expected one of (,.])")
549
550 - def decln(self, token, string):
551 if token == tokenize.NAME:
552 self.decl.append(string)
553 self.state = self.decl1
554 else:
555 raise SyntaxError("expected name")
556
557 - def colon(self, token, string):
558 if token == tokenize.COLON:
559 self.state = self.colon1
560 else:
561 raise SyntaxError("expected colon(:)")
562
563 - def colon1(self, token, string):
564 if token == tokenize.COLON:
565 self.state = self.colon2
566 else:
567 self.colon2(token, string)
568
569 - def colon2(self, token, string):
570 if token == tokenize.NEWLINE:
571 self.state = self.newline
572 else:
573 self.endtoken = tokenize.NEWLINE
574 self.state = self.rest
575 self.rest(token, string)
576
578 if token == tokenize.INDENT:
579 self.endtoken = tokenize.DEDENT
580 self.state = self.rest
581 else:
582 raise IndentationError("expected an indented block")
583
584 - def rest(self, token, string):
585 if token == self.endtoken or token == tokenize.ENDMARKER:
586 if self.expr:
587 self.exprs.append( (' '.join(self.expr), self.lineno) )
588 raise Done()
589 self.offset, self.endlineno = self.input.tell(), self.lineno
590 if token == tokenize.NEWLINE:
591 self.exprs.append( (' '.join(self.expr), self.lineno) )
592 self.expr = []
593 else:
594 self.expr.append(string)
595
597 r"""Read an indented block of expressions
598
599 startlineno is *zero* origined line number.
600
601 pre::
602 input.readline # must have readline function
603
604 Examples:
605 #>>> _read_block(StringIO('\tfoo:\n'), 0)
606 #0
607 >>> _read_block(StringIO('\tpost[]: True\n'), 0)
608 ('post', [], [('True', 1)], 1)
609 >>> _read_block(StringIO('\tpre: 5 + 6 > 10\n'), 0)
610 ('pre', [], [('5 + 6 > 10', 1)], 1)
611 >>> _read_block(StringIO('\tpost:\n\t\t5 + 6 < 12\n\t\t2 + 2 == 4\n'), 0)
612 ('post', [], [('5 + 6 < 12', 2), ('2 + 2 == 4', 3)], 3)
613 >>> _read_block(StringIO('\tpost[foo.bar]: # changes\n' \
614 ... '\t\tlen(foo.bar) > 0\n'), 0)
615 ('post', [['foo', 'bar']], [('len ( foo . bar ) > 0', 2)], 2)
616
617 Handles double colons (for re-structured text)::
618 >>> _read_block(StringIO('\tpre:: 5 + 6 > 10\n'), 0)
619 ('pre', [], [('5 + 6 > 10', 1)], 1)
620 """
621 t = tokenizer(input, startlineno)
622 try:
623 tokenize.tokenize(input.readline, t.next)
624 except Done:
625 pass
626 input.seek(t.offset)
627 return (t.keyword, t.decls, t.exprs, t.endlineno)
628
629
630
631
632
633
634
635
637 """Creates and installs a function/method checker.
638
639 pre::
640 contracts[0][0] == PRE and contracts[1][0] == POST
641 isinstance(path[0], ModuleType)
642 forall(path[1:], isclass)
643 """
644 name, obj = code
645
646 newpath = path + [obj]
647
648 if isinstance(obj, MethodType):
649 func = obj.im_func
650 invs = hasattr(path[-1], PREFIX + INV)
651 else:
652 func = obj
653 invs = hasattr(path[0], PREFIX + INV)
654
655
656
657
658 if contracts[0][2] or contracts[1][2] or (is_public and invs):
659 argspec = getargspec(func)
660 args = _format_args(argspec)
661
662
663 if args:
664 argl = ', ' + args
665 else:
666 argl = args
667
668 output = StringIO()
669
670 output.write('def %s(%s):\n' % (_mkname(path, name, _CHK), args))
671 output.write('\timport %s\n' % MODULE)
672
673 classname = '.'.join([c.__name__ for c in path[1:]])
674
675 if isinstance(obj, FunctionType):
676 if is_public:
677 chkname, chkargs = 'call_public_function', '__assert_inv, '
678 else:
679 chkname, chkargs = 'call_private_function', ''
680 else:
681 if not is_public:
682 chkname = 'call_private_method'
683 elif name == '__init__':
684 chkname = 'call_constructor'
685 elif name == '__del__':
686 chkname = 'call_destructor'
687 else:
688 chkname = 'call_public_method'
689 chkargs = classname + ', '
690
691
692 if checklevel == CHECK_ALL:
693 suffix = '_all'
694 else:
695 suffix = '_pre'
696
697
698 output.write('\treturn %s.%s%s(%s' % (MODULE, chkname, suffix,
699 chkargs))
700 if classname:
701 output.write(classname)
702 output.write('.')
703 output.write(name)
704 output.write(argl)
705 output.write(')\n')
706
707 newfunc = _define(_mkname(path, name, _CHK), output.getvalue(),
708 path[0])
709
710
711 if argspec[3]:
712
713
714
715 newfunc = new.function(newfunc.func_code, newfunc.func_globals,
716 newfunc.func_name, argspec[3])
717
718 setattr(newfunc, PREFIX + _ORIG, getattr(func, PREFIX + _ORIG, func))
719
720
721 if contracts[0][2]:
722 pre = _define_checker(_mkname(path, name, contracts[0][0]), args,
723 contracts[0], newpath)
724
725 delattr(path[0], pre.func_name)
726 setattr(newfunc, PREFIX + contracts[0][0], pre)
727
728 if checklevel == CHECK_ALL:
729
730 if contracts[1][1]:
731 saver = _define_saver(_mkname(path, name, _SAVE), OLD + argl,
732 contracts[1][1], path[0])
733 delattr(path[0], saver.func_name)
734 setattr(newfunc, PREFIX + _SAVE, saver)
735
736
737 if contracts[1][2]:
738 post = _define_checker(_mkname(path, name, contracts[1][0]),
739 OLD + ', ' + RETURN + argl,
740 contracts[1], newpath)
741
742 delattr(path[0], post.func_name)
743 setattr(newfunc, PREFIX + contracts[1][0], post)
744
745 newname = newfunc.func_name
746 newfunc.__doc__ = obj.__doc__
747
748 if isclass(path[-1]) and isinstance(obj, FunctionType):
749
750 newfunc = staticmethod(newfunc)
751
752 setattr(path[-1], name, newfunc)
753
754 if name != newname or len(path) > 1:
755 delattr(path[0], newname)
756
757
759 return isinstance(obj, TypeType) or isinstance(obj, ClassType)
760
762 return isinstance(obj, StringType) or isinstance(obj, UnicodeType)
763
765 """Define a function that does contract assertion checking.
766
767 args is a string argument declaration (ex: 'a, b, c = 1, *va, **ka')
768 contract is an element of the contracts list returned by parse_docstring
769 module is the containing module (not parent class)
770
771 Returns the newly-defined function.
772
773 pre::
774 isstring(name)
775 isstring(args)
776 contract[0] in _CONTRACTS
777 len(contract[2]) > 0
778 post::
779 isinstance(__return__, FunctionType)
780 __return__.__name__ == name
781 """
782 output = StringIO()
783 output.write('def %s(%s):\n' % (name, args))
784
785 ex = _EXCEPTIONS.get(contract[0], 'ContractViolationError')
786 output.write('\tfrom %s import forall, exists, implies, %s\n' % \
787 (MODULE, ex))
788 loc = '.'.join([x.__name__ for x in path])
789 for c in contract[2]:
790 output.write('\tif not (')
791 output.write(c[0])
792 output.write('): raise %s("%s", %u)\n' % (ex, loc, c[1]))
793
794 return _define(name, output.getvalue(), path[0])
795
797 """Create a function that saves values into an __old__ variable.
798
799 pre:: decls
800 post:: isinstance(__return__, FunctionType)
801 """
802 output = StringIO()
803 output.write('def %s(%s):\n' % (name, args))
804 output.write('\timport %s, copy\n' % MODULE)
805
806 _save_decls(output, '', _decltodict(decls))
807
808 return _define(name, output.getvalue(), module)
809
811
812 exec text in vars(module)
813 return getattr(module, name)
814
841
842 CO_VARARGS, CO_VARKEYWORDS = 4, 8
843
844 try:
845 import inspect
846
866
870
871 getmro = inspect.getmro
872
873 except ImportError:
874
876 code = function.func_code
877 i = code.co_argcount
878 args = list(code.co_varnames[:i])
879 if code.co_flags & CO_VARARGS:
880 va = code.co_varnames[i]
881 i += 1
882 else:
883 va = None
884 if code.co_flags & CO_VARKEYWORDS:
885 ka = code.co_varnames[i]
886 i += 1
887 else:
888 ka = None
889 return (args, va, ka, function.func_defaults)
890
892
893 if cls in accum:
894 return
895 accum.append(cls)
896 for base in cls.__bases__:
897 _searchbases(base, accum)
898
900 """Return tuple of base classes (including cls) in method resolution
901 order."""
902 if hasattr(cls, "__mro__"):
903 return cls.__mro__
904 else:
905 result = []
906 _searchbases(cls, result)
907 return tuple(result)
908
910 """Get argument information about a function.
911
912 Returns a tuple (args, varargs, keywordvarargs, defaults) where
913 args is a list of strings, varargs is None or the name of the
914 *va argument, keywordvarargs is None or the name of the **ka
915 argument, and defaults is a list of default values.
916
917 This function is different from the Python-provided
918 inspect.getargspec in that 1) tuple arguments are returned as
919 a string grouping '(a, b, c)' instead of broken out "['a', 'b', 'c']"
920 and 2) it works in Jython, which doesn't support inspect (yet).
921
922 >>> getargspec(lambda a, b: a * b)
923 (['a', 'b'], None, None, None)
924 >>> getargspec(lambda a, (b, c, d) = (5, 6, 7), *va, **ka: a * b)
925 (['a', '(b, c, d)'], 'va', 'ka', ((5, 6, 7),))
926
927 pre::
928 isinstance(function, FunctionType)
929 post[]::
930 # tuple of form (args, va, ka, defaults)
931 isinstance(__return__, TupleType) and len(__return__) == 4
932 # args is a list of strings
933 isinstance(__return__[0], ListType)
934 forall(__return__[0], isstring)
935 # va is None or a string
936 __return__[1] is None or isstring(__return__[1])
937 # ka is None or a string
938 __return__[2] is None or isstring(__return__[2])
939 # defaults is None or a tuple
940 __return__[3] is None or isinstance(__return__[3], TupleType)
941 """
942 return _getargs(function)
943
945 """Define a name combining a path and arbitrary strings.
946
947 pre::
948 isinstance(path[0], ModuleType)
949 forall(path[1:], isclass)
950
951 Examples:
952 >>> import contract
953 >>> _mkname([contract], 'test')
954 '__assert_test'
955 >>> class foo:
956 ... pass
957 >>> _mkname([contract, foo], 'func', 'pre')
958 '__assert_foo_func_pre'
959 """
960 return PREFIX + '_'.join([c.__name__ for c in path[1:]] + list(va))
961
963 """Recursively output a dictionary into a set of 'old' assignments.
964
965 The dictionary d is a tree of variable declarations. So, for example,
966 the declaration [self, self.buf, self.obj.a] would turn into the dict
967 {'self': {'buf': {}, 'obj': {'a': {}}}}, and would get output as
968 __old__.self = contract._holder()
969 __old__.self.buf = copy.copy(self.buf)
970 __old__.self.obj = contract._holder()
971 __old__.self.obj.a = copy.copy(self.obj.a)
972 """
973 for k, v in d.items():
974 n = name + k
975 output.write('\t%s.%s = ' % (OLD, n))
976 if v == {}:
977 output.write('copy.copy(%s)\n' % n)
978 else:
979 output.write('%s._holder()\n' % MODULE)
980 _save_decls(output, n + '.', v)
981
983 """Converts a list of list of names into a hierarchy of dictionaries.
984
985 Examples:
986 >>> d = _decltodict([['self', 'buf'],
987 ... ['self', 'item', 'a'],
988 ... ['self', 'item', 'b']])
989 >>> d == {'self': {'buf': {}, 'item': {'a': {}, 'b': {}}}}
990 1
991 """
992 result = {}
993 for i in l:
994 d = result
995 for n in i:
996
997 d = d.setdefault(n, {})
998 return result
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1012
1015
1016 -class PostconditionViolationError(ContractViolationError):
1018
1021
1022
1023
1024
1026 """Method pre-conditions can only weaken overridden methods'
1027 preconditions.
1028 """
1029 pass
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1057 """Check the invocation of a public function or static method.
1058
1059 Checks module invariants on entry and exit. Checks any
1060 pre-conditions and post-conditions.
1061 """
1062 inv()
1063 try:
1064 return _call_all([func], func, va, ka)
1065 finally:
1066 inv()
1067
1069 """Check the invocation of a public function or static method.
1070
1071 Checks module invariants on entry. Checks any pre-conditions.
1072 """
1073 inv()
1074 return _call_pre([func], func, va, ka)
1075
1077 """Check the invocation of a private function or static method.
1078
1079 Only checks pre-conditions and post-conditions.
1080 """
1081 return _call_all([func], func, va, ka)
1082
1084 """Check the invocation of a private function or static method.
1085
1086 Only checks pre-conditions
1087 """
1088 return _call_pre([func], func, va, ka)
1089
1091 """Check the invocation of a public method.
1092
1093 Check this class and all super-classes invariants on entry and
1094 exit. Checks all post-conditions of this method and all over-
1095 ridden method.
1096 """
1097 mro = getmro(cls)
1098 _check_class_invariants(mro, va[0])
1099 try:
1100 return _method_call_all(mro, method, va, ka)
1101 finally:
1102 _check_class_invariants(mro, va[0])
1103
1105 """Check the invocation of a public method.
1106
1107 Check this class and all super-classes invariants on entry.
1108 exit.
1109 """
1110 mro = getmro(cls)
1111 _check_class_invariants(mro, va[0])
1112 return _method_call_pre(mro, method, va, ka)
1113
1115 """Check the invocation of an __init__ constructor.
1116
1117 Checks pre-conditions and post-conditions, and only checks
1118 invariants on successful completion.
1119 """
1120
1121 mro = getmro(cls)
1122 result = _method_call_all(mro, method, va, ka)
1123 _check_class_invariants(mro, va[0])
1124 return result
1125
1126
1128 """Check the invocation of an __init__ constructor.
1129
1130 Checks pre-conditions and post-conditions, and only checks
1131 invariants on successful completion.
1132 """
1133
1134 mro = getmro(cls)
1135 result = _method_call_pre(mro, method, va, ka)
1136 _check_class_invariants(mro, va[0])
1137 return result
1138
1139
1141 """Check the invocation of a __del__ destructor.
1142
1143 Checks pre-conditions and post-conditions, and only checks
1144 invariants on entry.
1145 """
1146 mro = getmro(cls)
1147 _check_class_invariants(mro, va[0])
1148 return _method_call_all(mro, method, va, ka)
1149
1151 """Check the invocation of a __del__ destructor.
1152
1153 Checks pre-conditions and post-conditions, and only checks
1154 invariants on entry.
1155 """
1156 mro = getmro(cls)
1157 _check_class_invariants(mro, va[0])
1158 return _method_call_pre(mro, method, va, ka)
1159
1161 """Check the invocation of a private method call.
1162
1163 Checks pre-conditions and post-conditions.
1164 """
1165 return _method_call_all(getmro(cls), method, va, ka)
1166
1168 """Check the invocation of a private method call.
1169
1170 Checks pre-conditions.
1171 """
1172 return _method_call_pre(getmro(cls), method, va, ka)
1173
1175 """Checks class invariants on an instance.
1176
1177 mro - list of classes in method-resolution order
1178 instance - object to test
1179
1180 pre::
1181 # instance must be an instance of each class in mro
1182 forall(mro, lambda x: isinstance(instance, x))
1183 """
1184 for c in mro:
1185 try:
1186 p = c.__assert_inv
1187 except AttributeError:
1188 pass
1189 else:
1190 p(instance)
1191
1193 """Check the invocation of a method.
1194
1195 mro -- list/tuple of class objects in method resolution order
1196 """
1197
1198 assert isinstance(method, MethodType)
1199
1200 func = method.im_func
1201 name = func.__assert_orig.__name__
1202
1203 a = [getattr(c, name).im_func for c in mro if _has_method(c, name)]
1204
1205 return _call_all(a, func, va, ka)
1206
1208 """Check the invocation of a method.
1209
1210 mro -- list/tuple of class objects in method resolution order
1211 """
1212
1213 func = method.im_func
1214 name = func.__assert_orig.__name__
1215
1216 a = [getattr(c, name).im_func for c in mro if _has_method(c, name)]
1217
1218 return _call_pre(a, func, va, ka)
1219
1221 _check_preconditions(a, func, va, ka)
1222
1223
1224 old = _holder()
1225 for f in a:
1226 try:
1227 p = f.__assert_save
1228 except AttributeError:
1229 pass
1230 else:
1231 p(old, *va, **ka)
1232
1233 result = func.__assert_orig(*va, **ka)
1234 for f in a:
1235
1236 try:
1237 p = f.__assert_post
1238 except AttributeError:
1239 pass
1240 else:
1241 p(old, result, *va, **ka)
1242
1243 return result
1244
1248
1250
1251 try:
1252 p = func.__assert_pre
1253 except AttributeError:
1254
1255 pass
1256 else:
1257 try:
1258 p(*va, **ka)
1259 except PreconditionViolationError, args:
1260
1261
1262 for f in a:
1263 if f is not func:
1264 try:
1265 p = f.__assert_pre
1266 except AttributeError:
1267 pass
1268 else:
1269 p(*va, **ka)
1270 raise InvalidPreconditionError(args)
1271 raise
1272
1273
1275 """Test if a class has a named method.
1276
1277 pre::
1278 isclass(cls)
1279 isstring(name)
1280 post:: __return__ == (hasattr(cls, name) and \
1281 isinstance(getattr(cls, name), MethodType))
1282 """
1283 return isinstance(getattr(cls, name, None), MethodType)
1284
1286 """Empty invariant assertions
1287 """
1288 pass
1289
1290
1291
1292
1293
1294
1295
1297 """Checks that all elements in a sequence are true.
1298
1299 Returns True(1) if all elements are true. Return False(0) otherwise.
1300
1301 Examples:
1302 >>> forall([True, True, True])
1303 1
1304 >>> forall( () )
1305 1
1306 >>> forall([True, True, False, True])
1307 0
1308 """
1309 for i in a:
1310 if not fn(i):
1311 return False
1312 return True
1313
1315 """Checks that at least one element in a sequence is true.
1316
1317 Returns True(1) if at least one element is true. Return False(0)
1318 otherwise.
1319
1320 Examples:
1321 >>> exists([False, False, True])
1322 1
1323 >>> exists([])
1324 0
1325 >>> exists([False, 0, '', []])
1326 0
1327 """
1328 for i in a:
1329 if fn(i):
1330 return True
1331 return False
1332
1334 """Logical implication.
1335
1336 implies(x, y) should be read 'x implies y' or 'if x then y'
1337 implies(x, a, b) should be read 'if x then a else b'
1338
1339 Examples:
1340 >>> implies(False, False)
1341 1
1342 >>> implies(False, True)
1343 1
1344 >>> implies(True, False)
1345 0
1346 >>> implies(True, True)
1347 1
1348 """
1349 if test:
1350 return then_val
1351 else:
1352 return else_val
1353
1355 """Placeholder for arbitrary 'old' values.
1356 """
1357 pass
1358
1359
1360
1361
1362
1363 __test__ = {
1364 '_ispublic': _ispublic, '_get_members': _get_members,
1365 '_decltodict': _decltodict, '_read_block': _read_block,
1366 '_format_args': _format_args, '_mkname': _mkname}
1367
1368 if __name__ == '__main__':
1369 import doctest, contract
1370
1371 doctest.testmod(contract)
1372