Finish refactoring of DomCodeToUsLayoutKeyboardCode().
[chromium-blink-merge.git] / third_party / pycoverage / coverage / misc.py
blob0378173fcc3ddc92d00ca94d88d7ef3fec746a25
1 """Miscellaneous stuff for Coverage."""
3 import errno
4 import inspect
5 import os
6 import sys
8 from coverage.backward import md5, sorted # pylint: disable=W0622
9 from coverage.backward import string_class, to_bytes
12 def nice_pair(pair):
13 """Make a nice string representation of a pair of numbers.
15 If the numbers are equal, just return the number, otherwise return the pair
16 with a dash between them, indicating the range.
18 """
19 start, end = pair
20 if start == end:
21 return "%d" % start
22 else:
23 return "%d-%d" % (start, end)
26 def format_lines(statements, lines):
27 """Nicely format a list of line numbers.
29 Format a list of line numbers for printing by coalescing groups of lines as
30 long as the lines represent consecutive statements. This will coalesce
31 even if there are gaps between statements.
33 For example, if `statements` is [1,2,3,4,5,10,11,12,13,14] and
34 `lines` is [1,2,5,10,11,13,14] then the result will be "1-2, 5-11, 13-14".
36 """
37 pairs = []
38 i = 0
39 j = 0
40 start = None
41 statements = sorted(statements)
42 lines = sorted(lines)
43 while i < len(statements) and j < len(lines):
44 if statements[i] == lines[j]:
45 if start == None:
46 start = lines[j]
47 end = lines[j]
48 j += 1
49 elif start:
50 pairs.append((start, end))
51 start = None
52 i += 1
53 if start:
54 pairs.append((start, end))
55 ret = ', '.join(map(nice_pair, pairs))
56 return ret
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])
65 def expensive(fn):
66 """A decorator to cache the result of an expensive operation.
68 Only applies to methods with no arguments.
70 """
71 attr = "_cache_" + fn.__name__
72 def _wrapped(self):
73 """Inner fn that checks the cache."""
74 if not hasattr(self, attr):
75 setattr(self, attr, fn(self))
76 return getattr(self, attr)
77 return _wrapped
80 def bool_or_none(b):
81 """Return bool(b), but preserve None."""
82 if b is None:
83 return None
84 else:
85 return bool(b)
88 def join_regex(regexes):
89 """Combine a list of regexes into one that matches any of them."""
90 if len(regexes) > 1:
91 return "|".join(["(%s)" % r for r in regexes])
92 elif regexes:
93 return regexes[0]
94 else:
95 return ""
98 def file_be_gone(path):
99 """Remove a file, and don't get annoyed if it doesn't exist."""
100 try:
101 os.remove(path)
102 except OSError:
103 _, e, _ = sys.exc_info()
104 if e.errno != errno.ENOENT:
105 raise
108 class Hasher(object):
109 """Hashes Python data into md5."""
110 def __init__(self):
111 self.md5 = md5()
113 def update(self, v):
114 """Add `v` to the hash, recursively if needed."""
115 self.md5.update(to_bytes(str(type(v))))
116 if isinstance(v, string_class):
117 self.md5.update(to_bytes(v))
118 elif v is None:
119 pass
120 elif isinstance(v, (int, float)):
121 self.md5.update(to_bytes(str(v)))
122 elif isinstance(v, (tuple, list)):
123 for e in v:
124 self.update(e)
125 elif isinstance(v, dict):
126 keys = v.keys()
127 for k in sorted(keys):
128 self.update(k)
129 self.update(v[k])
130 else:
131 for k in dir(v):
132 if k.startswith('__'):
133 continue
134 a = getattr(v, k)
135 if inspect.isroutine(a):
136 continue
137 self.update(k)
138 self.update(a)
140 def digest(self):
141 """Retrieve the digest of the hash."""
142 return self.md5.digest()
145 class CoverageException(Exception):
146 """An exception specific to Coverage."""
147 pass
149 class NoSource(CoverageException):
150 """We couldn't find the source for a module."""
151 pass
153 class NoCode(NoSource):
154 """We couldn't find any code at all."""
155 pass
157 class NotPython(CoverageException):
158 """A source file turned out not to be parsable Python."""
159 pass
161 class ExceptionDuringRun(CoverageException):
162 """An exception happened while running customer code.
164 Construct it with three arguments, the values from `sys.exc_info`.
167 pass