1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 |
|
2 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt |
|
3 |
|
4 """Add things to old Pythons so I can pretend they are newer.""" |
|
5 |
|
6 # This file's purpose is to provide modules to be imported from here. |
|
7 # pylint: disable=unused-import |
|
8 |
|
9 import os |
|
10 import sys |
|
11 |
|
12 from datetime import datetime |
|
13 |
|
14 from coverage import env |
|
15 |
|
16 |
|
17 # Pythons 2 and 3 differ on where to get StringIO. |
|
18 try: |
|
19 from cStringIO import StringIO |
|
20 except ImportError: |
|
21 from io import StringIO |
|
22 |
|
23 # In py3, ConfigParser was renamed to the more-standard configparser. |
|
24 # But there's a py3 backport that installs "configparser" in py2, and I don't |
|
25 # want it because it has annoying deprecation warnings. So try the real py2 |
|
26 # import first. |
|
27 try: |
|
28 import ConfigParser as configparser |
|
29 except ImportError: |
|
30 import configparser |
|
31 |
|
32 # What's a string called? |
|
33 try: |
|
34 string_class = basestring |
|
35 except NameError: |
|
36 string_class = str |
|
37 |
|
38 # What's a Unicode string called? |
|
39 try: |
|
40 unicode_class = unicode |
|
41 except NameError: |
|
42 unicode_class = str |
|
43 |
|
44 # range or xrange? |
|
45 try: |
|
46 range = xrange # pylint: disable=redefined-builtin |
|
47 except NameError: |
|
48 range = range |
|
49 |
|
50 try: |
|
51 from itertools import zip_longest |
|
52 except ImportError: |
|
53 from itertools import izip_longest as zip_longest |
|
54 |
|
55 # Where do we get the thread id from? |
|
56 try: |
|
57 from thread import get_ident as get_thread_id |
|
58 except ImportError: |
|
59 from threading import get_ident as get_thread_id |
|
60 |
|
61 try: |
|
62 os.PathLike |
|
63 except AttributeError: |
|
64 # This is Python 2 and 3 |
|
65 path_types = (bytes, string_class, unicode_class) |
|
66 else: |
|
67 # 3.6+ |
|
68 path_types = (bytes, str, os.PathLike) |
|
69 |
|
70 # shlex.quote is new, but there's an undocumented implementation in "pipes", |
|
71 # who knew!? |
|
72 try: |
|
73 from shlex import quote as shlex_quote |
|
74 except ImportError: |
|
75 # Useful function, available under a different (undocumented) name |
|
76 # in Python versions earlier than 3.3. |
|
77 from pipes import quote as shlex_quote |
|
78 |
|
79 try: |
|
80 import reprlib |
|
81 except ImportError: # pragma: not covered |
|
82 # We need this on Python 2, but in testing environments, a backport is |
|
83 # installed, so this import isn't used. |
|
84 import repr as reprlib |
|
85 |
|
86 # A function to iterate listlessly over a dict's items, and one to get the |
|
87 # items as a list. |
|
88 try: |
|
89 {}.iteritems |
|
90 except AttributeError: |
|
91 # Python 3 |
|
92 def iitems(d): |
|
93 """Produce the items from dict `d`.""" |
|
94 return d.items() |
|
95 |
|
96 def litems(d): |
|
97 """Return a list of items from dict `d`.""" |
|
98 return list(d.items()) |
|
99 else: |
|
100 # Python 2 |
|
101 def iitems(d): |
|
102 """Produce the items from dict `d`.""" |
|
103 return d.iteritems() |
|
104 |
|
105 def litems(d): |
|
106 """Return a list of items from dict `d`.""" |
|
107 return d.items() |
|
108 |
|
109 # Getting the `next` function from an iterator is different in 2 and 3. |
|
110 try: |
|
111 iter([]).next |
|
112 except AttributeError: |
|
113 def iternext(seq): |
|
114 """Get the `next` function for iterating over `seq`.""" |
|
115 return iter(seq).__next__ |
|
116 else: |
|
117 def iternext(seq): |
|
118 """Get the `next` function for iterating over `seq`.""" |
|
119 return iter(seq).next |
|
120 |
|
121 # Python 3.x is picky about bytes and strings, so provide methods to |
|
122 # get them right, and make them no-ops in 2.x |
|
123 if env.PY3: |
|
124 def to_bytes(s): |
|
125 """Convert string `s` to bytes.""" |
|
126 return s.encode('utf8') |
|
127 |
|
128 def to_string(b): |
|
129 """Convert bytes `b` to string.""" |
|
130 return b.decode('utf8') |
|
131 |
|
132 def binary_bytes(byte_values): |
|
133 """Produce a byte string with the ints from `byte_values`.""" |
|
134 return bytes(byte_values) |
|
135 |
|
136 def byte_to_int(byte): |
|
137 """Turn a byte indexed from a bytes object into an int.""" |
|
138 return byte |
|
139 |
|
140 def bytes_to_ints(bytes_value): |
|
141 """Turn a bytes object into a sequence of ints.""" |
|
142 # In Python 3, iterating bytes gives ints. |
|
143 return bytes_value |
|
144 |
|
145 else: |
|
146 def to_bytes(s): |
|
147 """Convert string `s` to bytes (no-op in 2.x).""" |
|
148 return s |
|
149 |
|
150 def to_string(b): |
|
151 """Convert bytes `b` to string.""" |
|
152 return b |
|
153 |
|
154 def binary_bytes(byte_values): |
|
155 """Produce a byte string with the ints from `byte_values`.""" |
|
156 return "".join(chr(b) for b in byte_values) |
|
157 |
|
158 def byte_to_int(byte): |
|
159 """Turn a byte indexed from a bytes object into an int.""" |
|
160 return ord(byte) |
|
161 |
|
162 def bytes_to_ints(bytes_value): |
|
163 """Turn a bytes object into a sequence of ints.""" |
|
164 for byte in bytes_value: |
|
165 yield ord(byte) |
|
166 |
|
167 |
|
168 try: |
|
169 # In Python 2.x, the builtins were in __builtin__ |
|
170 BUILTINS = sys.modules['__builtin__'] |
|
171 except KeyError: |
|
172 # In Python 3.x, they're in builtins |
|
173 BUILTINS = sys.modules['builtins'] |
|
174 |
|
175 |
|
176 # imp was deprecated in Python 3.3 |
|
177 try: |
|
178 import importlib |
|
179 import importlib.util |
|
180 imp = None |
|
181 except ImportError: |
|
182 importlib = None |
|
183 |
|
184 # We only want to use importlib if it has everything we need. |
|
185 try: |
|
186 importlib_util_find_spec = importlib.util.find_spec |
|
187 except Exception: |
|
188 import imp |
|
189 importlib_util_find_spec = None |
|
190 |
|
191 # What is the .pyc magic number for this version of Python? |
|
192 try: |
|
193 PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER |
|
194 except AttributeError: |
|
195 PYC_MAGIC_NUMBER = imp.get_magic() |
|
196 |
|
197 |
|
198 def code_object(fn): |
|
199 """Get the code object from a function.""" |
|
200 try: |
|
201 return fn.func_code |
|
202 except AttributeError: |
|
203 return fn.__code__ |
|
204 |
|
205 |
|
206 try: |
|
207 from types import SimpleNamespace |
|
208 except ImportError: |
|
209 # The code from https://docs.python.org/3/library/types.html#types.SimpleNamespace |
|
210 class SimpleNamespace: |
|
211 """Python implementation of SimpleNamespace, for Python 2.""" |
|
212 def __init__(self, **kwargs): |
|
213 self.__dict__.update(kwargs) |
|
214 |
|
215 def __repr__(self): |
|
216 keys = sorted(self.__dict__) |
|
217 items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) |
|
218 return "{}({})".format(type(self).__name__, ", ".join(items)) |
|
219 |
|
220 |
|
221 def format_local_datetime(dt): |
|
222 """Return a string with local timezone representing the date. |
|
223 If python version is lower than 3.6, the time zone is not included. |
|
224 """ |
|
225 try: |
|
226 return dt.astimezone().strftime('%Y-%m-%d %H:%M %z') |
|
227 except (TypeError, ValueError): |
|
228 # Datetime.astimezone in Python 3.5 can not handle naive datetime |
|
229 return dt.strftime('%Y-%m-%d %H:%M') |
|
230 |
|
231 |
|
232 def invalidate_import_caches(): |
|
233 """Invalidate any import caches that may or may not exist.""" |
|
234 if importlib and hasattr(importlib, "invalidate_caches"): |
|
235 importlib.invalidate_caches() |
|
236 |
|
237 |
|
238 def import_local_file(modname, modfile=None): |
|
239 """Import a local file as a module. |
|
240 |
|
241 Opens a file in the current directory named `modname`.py, imports it |
|
242 as `modname`, and returns the module object. `modfile` is the file to |
|
243 import if it isn't in the current directory. |
|
244 |
|
245 """ |
|
246 try: |
|
247 import importlib.util as importlib_util |
|
248 except ImportError: |
|
249 importlib_util = None |
|
250 |
|
251 if modfile is None: |
|
252 modfile = modname + '.py' |
|
253 if importlib_util: |
|
254 spec = importlib_util.spec_from_file_location(modname, modfile) |
|
255 mod = importlib_util.module_from_spec(spec) |
|
256 sys.modules[modname] = mod |
|
257 spec.loader.exec_module(mod) |
|
258 else: |
|
259 for suff in imp.get_suffixes(): # pragma: part covered |
|
260 if suff[0] == '.py': |
|
261 break |
|
262 |
|
263 with open(modfile, 'r') as f: |
|
264 # pylint: disable=undefined-loop-variable |
|
265 mod = imp.load_module(modname, f, modfile, suff) |
|
266 |
|
267 return mod |
|