|
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: |
|
82 import repr as reprlib |
|
83 |
|
84 # A function to iterate listlessly over a dict's items, and one to get the |
|
85 # items as a list. |
|
86 try: |
|
87 {}.iteritems |
|
88 except AttributeError: |
|
89 # Python 3 |
|
90 def iitems(d): |
|
91 """Produce the items from dict `d`.""" |
|
92 return d.items() |
|
93 |
|
94 def litems(d): |
|
95 """Return a list of items from dict `d`.""" |
|
96 return list(d.items()) |
|
97 else: |
|
98 # Python 2 |
|
99 def iitems(d): |
|
100 """Produce the items from dict `d`.""" |
|
101 return d.iteritems() |
|
102 |
|
103 def litems(d): |
|
104 """Return a list of items from dict `d`.""" |
|
105 return d.items() |
|
106 |
|
107 # Getting the `next` function from an iterator is different in 2 and 3. |
|
108 try: |
|
109 iter([]).next |
|
110 except AttributeError: |
|
111 def iternext(seq): |
|
112 """Get the `next` function for iterating over `seq`.""" |
|
113 return iter(seq).__next__ |
|
114 else: |
|
115 def iternext(seq): |
|
116 """Get the `next` function for iterating over `seq`.""" |
|
117 return iter(seq).next |
|
118 |
|
119 # Python 3.x is picky about bytes and strings, so provide methods to |
|
120 # get them right, and make them no-ops in 2.x |
|
121 if env.PY3: |
|
122 def to_bytes(s): |
|
123 """Convert string `s` to bytes.""" |
|
124 return s.encode('utf8') |
|
125 |
|
126 def to_string(b): |
|
127 """Convert bytes `b` to string.""" |
|
128 return b.decode('utf8') |
|
129 |
|
130 def binary_bytes(byte_values): |
|
131 """Produce a byte string with the ints from `byte_values`.""" |
|
132 return bytes(byte_values) |
|
133 |
|
134 def byte_to_int(byte): |
|
135 """Turn a byte indexed from a bytes object into an int.""" |
|
136 return byte |
|
137 |
|
138 def bytes_to_ints(bytes_value): |
|
139 """Turn a bytes object into a sequence of ints.""" |
|
140 # In Python 3, iterating bytes gives ints. |
|
141 return bytes_value |
|
142 |
|
143 else: |
|
144 def to_bytes(s): |
|
145 """Convert string `s` to bytes (no-op in 2.x).""" |
|
146 return s |
|
147 |
|
148 def to_string(b): |
|
149 """Convert bytes `b` to string.""" |
|
150 return b |
|
151 |
|
152 def binary_bytes(byte_values): |
|
153 """Produce a byte string with the ints from `byte_values`.""" |
|
154 return "".join(chr(b) for b in byte_values) |
|
155 |
|
156 def byte_to_int(byte): |
|
157 """Turn a byte indexed from a bytes object into an int.""" |
|
158 return ord(byte) |
|
159 |
|
160 def bytes_to_ints(bytes_value): |
|
161 """Turn a bytes object into a sequence of ints.""" |
|
162 for byte in bytes_value: |
|
163 yield ord(byte) |
|
164 |
|
165 |
|
166 try: |
|
167 # In Python 2.x, the builtins were in __builtin__ |
|
168 BUILTINS = sys.modules['__builtin__'] |
|
169 except KeyError: |
|
170 # In Python 3.x, they're in builtins |
|
171 BUILTINS = sys.modules['builtins'] |
|
172 |
|
173 |
|
174 # imp was deprecated in Python 3.3 |
|
175 try: |
|
176 import importlib |
|
177 import importlib.util |
|
178 imp = None |
|
179 except ImportError: |
|
180 importlib = None |
|
181 |
|
182 # We only want to use importlib if it has everything we need. |
|
183 try: |
|
184 importlib_util_find_spec = importlib.util.find_spec |
|
185 except Exception: |
|
186 import imp |
|
187 importlib_util_find_spec = None |
|
188 |
|
189 # What is the .pyc magic number for this version of Python? |
|
190 try: |
|
191 PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER |
|
192 except AttributeError: |
|
193 PYC_MAGIC_NUMBER = imp.get_magic() |
|
194 |
|
195 |
|
196 def code_object(fn): |
|
197 """Get the code object from a function.""" |
|
198 try: |
|
199 return fn.func_code |
|
200 except AttributeError: |
|
201 return fn.__code__ |
|
202 |
|
203 |
|
204 try: |
|
205 from types import SimpleNamespace |
|
206 except ImportError: |
|
207 # The code from https://docs.python.org/3/library/types.html#types.SimpleNamespace |
|
208 class SimpleNamespace: |
|
209 """Python implementation of SimpleNamespace, for Python 2.""" |
|
210 def __init__(self, **kwargs): |
|
211 self.__dict__.update(kwargs) |
|
212 |
|
213 def __repr__(self): |
|
214 keys = sorted(self.__dict__) |
|
215 items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) |
|
216 return "{}({})".format(type(self).__name__, ", ".join(items)) |
|
217 |
|
218 def __eq__(self, other): |
|
219 return self.__dict__ == other.__dict__ |
|
220 |
|
221 |
|
222 def format_local_datetime(dt): |
|
223 """Return a string with local timezone representing the date. |
|
224 If python version is lower than 3.6, the time zone is not included. |
|
225 """ |
|
226 try: |
|
227 return dt.astimezone().strftime('%Y-%m-%d %H:%M %z') |
|
228 except (TypeError, ValueError): |
|
229 # Datetime.astimezone in Python 3.5 can not handle naive datetime |
|
230 return dt.strftime('%Y-%m-%d %H:%M') |
|
231 |
|
232 |
|
233 def invalidate_import_caches(): |
|
234 """Invalidate any import caches that may or may not exist.""" |
|
235 if importlib and hasattr(importlib, "invalidate_caches"): |
|
236 importlib.invalidate_caches() |
|
237 |
|
238 |
|
239 def import_local_file(modname, modfile=None): |
|
240 """Import a local file as a module. |
|
241 |
|
242 Opens a file in the current directory named `modname`.py, imports it |
|
243 as `modname`, and returns the module object. `modfile` is the file to |
|
244 import if it isn't in the current directory. |
|
245 |
|
246 """ |
|
247 try: |
|
248 from importlib.machinery import SourceFileLoader |
|
249 except ImportError: |
|
250 SourceFileLoader = None |
|
251 |
|
252 if modfile is None: |
|
253 modfile = modname + '.py' |
|
254 if SourceFileLoader: |
|
255 # pylint: disable=no-value-for-parameter, deprecated-method |
|
256 mod = SourceFileLoader(modname, modfile).load_module() |
|
257 else: |
|
258 for suff in imp.get_suffixes(): # pragma: part covered |
|
259 if suff[0] == '.py': |
|
260 break |
|
261 |
|
262 with open(modfile, 'r') as f: |
|
263 # pylint: disable=undefined-loop-variable |
|
264 mod = imp.load_module(modname, f, modfile, suff) |
|
265 |
|
266 return mod |