1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 |
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 |
2 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt |
3 |
3 |
4 """Add things to old Pythons so I can pretend they are newer.""" |
4 """Add things to old Pythons so I can pretend they are newer.""" |
5 |
5 |
6 # This file does tricky stuff, so disable a pylint warning. |
6 # This file's purpose is to provide modules to be imported from here. |
7 # pylint: disable=unused-import |
7 # pylint: disable=unused-import |
8 |
8 |
|
9 import os |
9 import sys |
10 import sys |
10 |
11 |
11 from coverage import env |
12 from coverage import env |
12 |
13 |
13 |
14 |
36 try: |
37 try: |
37 unicode_class = unicode |
38 unicode_class = unicode |
38 except NameError: |
39 except NameError: |
39 unicode_class = str |
40 unicode_class = str |
40 |
41 |
41 # Where do pickles come from? |
|
42 try: |
|
43 import cPickle as pickle |
|
44 except ImportError: |
|
45 import pickle |
|
46 |
|
47 # range or xrange? |
42 # range or xrange? |
48 try: |
43 try: |
49 range = xrange # pylint: disable=redefined-builtin |
44 range = xrange # pylint: disable=redefined-builtin |
50 except NameError: |
45 except NameError: |
51 range = range |
46 range = range |
52 |
47 |
|
48 try: |
|
49 from itertools import zip_longest |
|
50 except ImportError: |
|
51 from itertools import izip_longest as zip_longest |
|
52 |
|
53 # Where do we get the thread id from? |
|
54 try: |
|
55 from thread import get_ident as get_thread_id |
|
56 except ImportError: |
|
57 from threading import get_ident as get_thread_id |
|
58 |
|
59 try: |
|
60 os.PathLike |
|
61 except AttributeError: |
|
62 # This is Python 2 and 3 |
|
63 path_types = (bytes, string_class, unicode_class) |
|
64 else: |
|
65 # 3.6+ |
|
66 path_types = (bytes, str, os.PathLike) |
|
67 |
53 # shlex.quote is new, but there's an undocumented implementation in "pipes", |
68 # shlex.quote is new, but there's an undocumented implementation in "pipes", |
54 # who knew!? |
69 # who knew!? |
55 try: |
70 try: |
56 from shlex import quote as shlex_quote |
71 from shlex import quote as shlex_quote |
57 except ImportError: |
72 except ImportError: |
58 # Useful function, available under a different (undocumented) name |
73 # Useful function, available under a different (undocumented) name |
59 # in Python versions earlier than 3.3. |
74 # in Python versions earlier than 3.3. |
60 from pipes import quote as shlex_quote |
75 from pipes import quote as shlex_quote |
|
76 |
|
77 try: |
|
78 import reprlib |
|
79 except ImportError: |
|
80 import repr as reprlib |
61 |
81 |
62 # A function to iterate listlessly over a dict's items, and one to get the |
82 # A function to iterate listlessly over a dict's items, and one to get the |
63 # items as a list. |
83 # items as a list. |
64 try: |
84 try: |
65 {}.iteritems |
85 {}.iteritems |
99 if env.PY3: |
119 if env.PY3: |
100 def to_bytes(s): |
120 def to_bytes(s): |
101 """Convert string `s` to bytes.""" |
121 """Convert string `s` to bytes.""" |
102 return s.encode('utf8') |
122 return s.encode('utf8') |
103 |
123 |
|
124 def to_string(b): |
|
125 """Convert bytes `b` to string.""" |
|
126 return b.decode('utf8') |
|
127 |
104 def binary_bytes(byte_values): |
128 def binary_bytes(byte_values): |
105 """Produce a byte string with the ints from `byte_values`.""" |
129 """Produce a byte string with the ints from `byte_values`.""" |
106 return bytes(byte_values) |
130 return bytes(byte_values) |
|
131 |
|
132 def byte_to_int(byte): |
|
133 """Turn a byte indexed from a bytes object into an int.""" |
|
134 return byte |
107 |
135 |
108 def bytes_to_ints(bytes_value): |
136 def bytes_to_ints(bytes_value): |
109 """Turn a bytes object into a sequence of ints.""" |
137 """Turn a bytes object into a sequence of ints.""" |
110 # In Python 3, iterating bytes gives ints. |
138 # In Python 3, iterating bytes gives ints. |
111 return bytes_value |
139 return bytes_value |
113 else: |
141 else: |
114 def to_bytes(s): |
142 def to_bytes(s): |
115 """Convert string `s` to bytes (no-op in 2.x).""" |
143 """Convert string `s` to bytes (no-op in 2.x).""" |
116 return s |
144 return s |
117 |
145 |
|
146 def to_string(b): |
|
147 """Convert bytes `b` to string.""" |
|
148 return b |
|
149 |
118 def binary_bytes(byte_values): |
150 def binary_bytes(byte_values): |
119 """Produce a byte string with the ints from `byte_values`.""" |
151 """Produce a byte string with the ints from `byte_values`.""" |
120 return "".join(chr(b) for b in byte_values) |
152 return "".join(chr(b) for b in byte_values) |
|
153 |
|
154 def byte_to_int(byte): |
|
155 """Turn a byte indexed from a bytes object into an int.""" |
|
156 return ord(byte) |
121 |
157 |
122 def bytes_to_ints(bytes_value): |
158 def bytes_to_ints(bytes_value): |
123 """Turn a bytes object into a sequence of ints.""" |
159 """Turn a bytes object into a sequence of ints.""" |
124 for byte in bytes_value: |
160 for byte in bytes_value: |
125 yield ord(byte) |
161 yield ord(byte) |
153 PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER |
189 PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER |
154 except AttributeError: |
190 except AttributeError: |
155 PYC_MAGIC_NUMBER = imp.get_magic() |
191 PYC_MAGIC_NUMBER = imp.get_magic() |
156 |
192 |
157 |
193 |
|
194 def code_object(fn): |
|
195 """Get the code object from a function.""" |
|
196 try: |
|
197 return fn.func_code |
|
198 except AttributeError: |
|
199 return fn.__code__ |
|
200 |
|
201 |
|
202 try: |
|
203 from types import SimpleNamespace |
|
204 except ImportError: |
|
205 # The code from https://docs.python.org/3/library/types.html#types.SimpleNamespace |
|
206 class SimpleNamespace: |
|
207 """Python implementation of SimpleNamespace, for Python 2.""" |
|
208 def __init__(self, **kwargs): |
|
209 self.__dict__.update(kwargs) |
|
210 |
|
211 def __repr__(self): |
|
212 keys = sorted(self.__dict__) |
|
213 items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) |
|
214 return "{}({})".format(type(self).__name__, ", ".join(items)) |
|
215 |
|
216 def __eq__(self, other): |
|
217 return self.__dict__ == other.__dict__ |
|
218 |
|
219 |
158 def invalidate_import_caches(): |
220 def invalidate_import_caches(): |
159 """Invalidate any import caches that may or may not exist.""" |
221 """Invalidate any import caches that may or may not exist.""" |
160 if importlib and hasattr(importlib, "invalidate_caches"): |
222 if importlib and hasattr(importlib, "invalidate_caches"): |
161 importlib.invalidate_caches() |
223 importlib.invalidate_caches() |
162 |
224 |
175 SourceFileLoader = None |
237 SourceFileLoader = None |
176 |
238 |
177 if modfile is None: |
239 if modfile is None: |
178 modfile = modname + '.py' |
240 modfile = modname + '.py' |
179 if SourceFileLoader: |
241 if SourceFileLoader: |
|
242 # pylint: disable=no-value-for-parameter, deprecated-method |
180 mod = SourceFileLoader(modname, modfile).load_module() |
243 mod = SourceFileLoader(modname, modfile).load_module() |
181 else: |
244 else: |
182 for suff in imp.get_suffixes(): # pragma: part covered |
245 for suff in imp.get_suffixes(): # pragma: part covered |
183 if suff[0] == '.py': |
246 if suff[0] == '.py': |
184 break |
247 break |