DebugClients/Python3/coverage/misc.py

changeset 4489
d0d6e4ad31bd
parent 3495
fac17a82b431
child 5051
3586ebd9fac8
equal deleted inserted replaced
4481:456c58fc64b0 4489:d0d6e4ad31bd
1 """Miscellaneous stuff for Coverage.""" 1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
3
4 """Miscellaneous stuff for coverage.py."""
2 5
3 import errno 6 import errno
7 import hashlib
4 import inspect 8 import inspect
5 import os 9 import os
6 import sys 10
7 11 from coverage import env
8 from .backward import md5, sorted # pylint: disable=W0622 12 from coverage.backward import string_class, to_bytes, unicode_class
9 from .backward import string_class, to_bytes 13
14
15 # Use PyContracts for assertion testing on parameters and returns, but only if
16 # we are running our own test suite.
17 if env.TESTING:
18 from contracts import contract # pylint: disable=unused-import
19 from contracts import new_contract
20
21 try:
22 # Define contract words that PyContract doesn't have.
23 new_contract('bytes', lambda v: isinstance(v, bytes))
24 if env.PY3:
25 new_contract('unicode', lambda v: isinstance(v, unicode_class))
26 except ValueError:
27 # During meta-coverage, this module is imported twice, and PyContracts
28 # doesn't like redefining contracts. It's OK.
29 pass
30 else: # pragma: not covered
31 # We aren't using real PyContracts, so just define a no-op decorator as a
32 # stunt double.
33 def contract(**unused):
34 """Dummy no-op implementation of `contract`."""
35 return lambda func: func
10 36
11 37
12 def nice_pair(pair): 38 def nice_pair(pair):
13 """Make a nice string representation of a pair of numbers. 39 """Make a nice string representation of a pair of numbers.
14 40
40 start = None 66 start = None
41 statements = sorted(statements) 67 statements = sorted(statements)
42 lines = sorted(lines) 68 lines = sorted(lines)
43 while i < len(statements) and j < len(lines): 69 while i < len(statements) and j < len(lines):
44 if statements[i] == lines[j]: 70 if statements[i] == lines[j]:
45 if start == None: 71 if start is None:
46 start = lines[j] 72 start = lines[j]
47 end = lines[j] 73 end = lines[j]
48 j += 1 74 j += 1
49 elif start: 75 elif start:
50 pairs.append((start, end)) 76 pairs.append((start, end))
54 pairs.append((start, end)) 80 pairs.append((start, end))
55 ret = ', '.join(map(nice_pair, pairs)) 81 ret = ', '.join(map(nice_pair, pairs))
56 return ret 82 return ret
57 83
58 84
59 def short_stack():
60 """Return a string summarizing the call stack."""
61 stack = inspect.stack()[:0:-1]
62 return "\n".join(["%30s : %s @%d" % (t[3],t[1],t[2]) for t in stack])
63
64
65 def expensive(fn): 85 def expensive(fn):
66 """A decorator to cache the result of an expensive operation. 86 """A decorator to indicate that a method shouldn't be called more than once.
67 87
68 Only applies to methods with no arguments. 88 Normally, this does nothing. During testing, this raises an exception if
69 89 called more than once.
70 """ 90
71 attr = "_cache_" + fn.__name__ 91 """
72 def _wrapped(self): 92 if env.TESTING:
73 """Inner fn that checks the cache.""" 93 attr = "_once_" + fn.__name__
74 if not hasattr(self, attr): 94
75 setattr(self, attr, fn(self)) 95 def _wrapped(self):
76 return getattr(self, attr) 96 """Inner function that checks the cache."""
77 return _wrapped 97 if hasattr(self, attr):
98 raise Exception("Shouldn't have called %s more than once" % fn.__name__)
99 setattr(self, attr, True)
100 return fn(self)
101 return _wrapped
102 else:
103 return fn
78 104
79 105
80 def bool_or_none(b): 106 def bool_or_none(b):
81 """Return bool(b), but preserve None.""" 107 """Return bool(b), but preserve None."""
82 if b is None: 108 if b is None:
85 return bool(b) 111 return bool(b)
86 112
87 113
88 def join_regex(regexes): 114 def join_regex(regexes):
89 """Combine a list of regexes into one that matches any of them.""" 115 """Combine a list of regexes into one that matches any of them."""
90 if len(regexes) > 1: 116 return "|".join("(?:%s)" % r for r in regexes)
91 return "|".join(["(%s)" % r for r in regexes])
92 elif regexes:
93 return regexes[0]
94 else:
95 return ""
96 117
97 118
98 def file_be_gone(path): 119 def file_be_gone(path):
99 """Remove a file, and don't get annoyed if it doesn't exist.""" 120 """Remove a file, and don't get annoyed if it doesn't exist."""
100 try: 121 try:
101 os.remove(path) 122 os.remove(path)
102 except OSError: 123 except OSError as e:
103 _, e, _ = sys.exc_info()
104 if e.errno != errno.ENOENT: 124 if e.errno != errno.ENOENT:
105 raise 125 raise
106 126
107 127
108 class Hasher(object): 128 class Hasher(object):
109 """Hashes Python data into md5.""" 129 """Hashes Python data into md5."""
110 def __init__(self): 130 def __init__(self):
111 self.md5 = md5() 131 self.md5 = hashlib.md5()
112 132
113 def update(self, v): 133 def update(self, v):
114 """Add `v` to the hash, recursively if needed.""" 134 """Add `v` to the hash, recursively if needed."""
115 self.md5.update(to_bytes(str(type(v)))) 135 self.md5.update(to_bytes(str(type(v))))
116 if isinstance(v, string_class): 136 if isinstance(v, string_class):
117 self.md5.update(to_bytes(v)) 137 self.md5.update(to_bytes(v))
138 elif isinstance(v, bytes):
139 self.md5.update(v)
118 elif v is None: 140 elif v is None:
119 pass 141 pass
120 elif isinstance(v, (int, float)): 142 elif isinstance(v, (int, float)):
121 self.md5.update(to_bytes(str(v))) 143 self.md5.update(to_bytes(str(v)))
122 elif isinstance(v, (tuple, list)): 144 elif isinstance(v, (tuple, list)):
135 if inspect.isroutine(a): 157 if inspect.isroutine(a):
136 continue 158 continue
137 self.update(k) 159 self.update(k)
138 self.update(a) 160 self.update(a)
139 161
140 def digest(self): 162 def hexdigest(self):
141 """Retrieve the digest of the hash.""" 163 """Retrieve the hex digest of the hash."""
142 return self.md5.digest() 164 return self.md5.hexdigest()
165
166
167 def _needs_to_implement(that, func_name):
168 """Helper to raise NotImplementedError in interface stubs."""
169 if hasattr(that, "_coverage_plugin_name"):
170 thing = "Plugin"
171 name = that._coverage_plugin_name
172 else:
173 thing = "Class"
174 klass = that.__class__
175 name = "{klass.__module__}.{klass.__name__}".format(klass=klass)
176
177 raise NotImplementedError(
178 "{thing} {name!r} needs to implement {func_name}()".format(
179 thing=thing, name=name, func_name=func_name
180 )
181 )
143 182
144 183
145 class CoverageException(Exception): 184 class CoverageException(Exception):
146 """An exception specific to Coverage.""" 185 """An exception specific to coverage.py."""
147 pass 186 pass
187
148 188
149 class NoSource(CoverageException): 189 class NoSource(CoverageException):
150 """We couldn't find the source for a module.""" 190 """We couldn't find the source for a module."""
151 pass 191 pass
152 192
193
153 class NoCode(NoSource): 194 class NoCode(NoSource):
154 """We couldn't find any code at all.""" 195 """We couldn't find any code at all."""
155 pass 196 pass
156 197
198
157 class NotPython(CoverageException): 199 class NotPython(CoverageException):
158 """A source file turned out not to be parsable Python.""" 200 """A source file turned out not to be parsable Python."""
159 pass 201 pass
160 202
203
161 class ExceptionDuringRun(CoverageException): 204 class ExceptionDuringRun(CoverageException):
162 """An exception happened while running customer code. 205 """An exception happened while running customer code.
163 206
164 Construct it with three arguments, the values from `sys.exc_info`. 207 Construct it with three arguments, the values from `sys.exc_info`.
165 208

eric ide

mercurial