##// END OF EJS Templates
add shutil.which backport to py3compat
Min RK -
Show More
@@ -1,260 +1,329 b''
1 1 # coding: utf-8
2 2 """Compatibility tricks for Python 3. Mainly to do with unicode."""
3 3 import functools
4 4 import os
5 5 import sys
6 6 import re
7 import shutil
7 8 import types
8 9
9 10 from .encoding import DEFAULT_ENCODING
10 11
11 12 def no_code(x, encoding=None):
12 13 return x
13 14
14 15 def decode(s, encoding=None):
15 16 encoding = encoding or DEFAULT_ENCODING
16 17 return s.decode(encoding, "replace")
17 18
18 19 def encode(u, encoding=None):
19 20 encoding = encoding or DEFAULT_ENCODING
20 21 return u.encode(encoding, "replace")
21 22
22 23
23 24 def cast_unicode(s, encoding=None):
24 25 if isinstance(s, bytes):
25 26 return decode(s, encoding)
26 27 return s
27 28
28 29 def cast_bytes(s, encoding=None):
29 30 if not isinstance(s, bytes):
30 31 return encode(s, encoding)
31 32 return s
32 33
33 34 def buffer_to_bytes(buf):
34 35 """Cast a buffer object to bytes"""
35 36 if not isinstance(buf, bytes):
36 37 buf = bytes(buf)
37 38 return buf
38 39
39 40 def _modify_str_or_docstring(str_change_func):
40 41 @functools.wraps(str_change_func)
41 42 def wrapper(func_or_str):
42 43 if isinstance(func_or_str, string_types):
43 44 func = None
44 45 doc = func_or_str
45 46 else:
46 47 func = func_or_str
47 48 doc = func.__doc__
48 49
49 50 doc = str_change_func(doc)
50 51
51 52 if func:
52 53 func.__doc__ = doc
53 54 return func
54 55 return doc
55 56 return wrapper
56 57
57 58 def safe_unicode(e):
58 59 """unicode(e) with various fallbacks. Used for exceptions, which may not be
59 60 safe to call unicode() on.
60 61 """
61 62 try:
62 63 return unicode_type(e)
63 64 except UnicodeError:
64 65 pass
65 66
66 67 try:
67 68 return str_to_unicode(str(e))
68 69 except UnicodeError:
69 70 pass
70 71
71 72 try:
72 73 return str_to_unicode(repr(e))
73 74 except UnicodeError:
74 75 pass
75 76
76 77 return u'Unrecoverably corrupt evalue'
77 78
79 # shutil.which from Python 3.4
80 def _shutil_which(cmd, mode=os.F_OK | os.X_OK, path=None):
81 """Given a command, mode, and a PATH string, return the path which
82 conforms to the given mode on the PATH, or None if there is no such
83 file.
84
85 `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
86 of os.environ.get("PATH"), or can be overridden with a custom search
87 path.
88
89 This is a backport of shutil.which from Python 3.4
90 """
91 # Check that a given file can be accessed with the correct mode.
92 # Additionally check that `file` is not a directory, as on Windows
93 # directories pass the os.access check.
94 def _access_check(fn, mode):
95 return (os.path.exists(fn) and os.access(fn, mode)
96 and not os.path.isdir(fn))
97
98 # If we're given a path with a directory part, look it up directly rather
99 # than referring to PATH directories. This includes checking relative to the
100 # current directory, e.g. ./script
101 if os.path.dirname(cmd):
102 if _access_check(cmd, mode):
103 return cmd
104 return None
105
106 if path is None:
107 path = os.environ.get("PATH", os.defpath)
108 if not path:
109 return None
110 path = path.split(os.pathsep)
111
112 if sys.platform == "win32":
113 # The current directory takes precedence on Windows.
114 if not os.curdir in path:
115 path.insert(0, os.curdir)
116
117 # PATHEXT is necessary to check on Windows.
118 pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
119 # See if the given file matches any of the expected path extensions.
120 # This will allow us to short circuit when given "python.exe".
121 # If it does match, only test that one, otherwise we have to try
122 # others.
123 if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
124 files = [cmd]
125 else:
126 files = [cmd + ext for ext in pathext]
127 else:
128 # On other platforms you don't have things like PATHEXT to tell you
129 # what file suffixes are executable, so just pass on cmd as-is.
130 files = [cmd]
131
132 seen = set()
133 for dir in path:
134 normdir = os.path.normcase(dir)
135 if not normdir in seen:
136 seen.add(normdir)
137 for thefile in files:
138 name = os.path.join(dir, thefile)
139 if _access_check(name, mode):
140 return name
141 return None
142
78 143 if sys.version_info[0] >= 3:
79 144 PY3 = True
80 145
81 146 # keep reference to builtin_mod because the kernel overrides that value
82 147 # to forward requests to a frontend.
83 148 def input(prompt=''):
84 149 return builtin_mod.input(prompt)
85 150
86 151 builtin_mod_name = "builtins"
87 152 import builtins as builtin_mod
88 153
89 154 str_to_unicode = no_code
90 155 unicode_to_str = no_code
91 156 str_to_bytes = encode
92 157 bytes_to_str = decode
93 158 cast_bytes_py2 = no_code
94 159 cast_unicode_py2 = no_code
95 160 buffer_to_bytes_py2 = no_code
96 161
97 162 string_types = (str,)
98 163 unicode_type = str
99 164
165 which = shutil.which
166
100 167 def isidentifier(s, dotted=False):
101 168 if dotted:
102 169 return all(isidentifier(a) for a in s.split("."))
103 170 return s.isidentifier()
104 171
105 172 xrange = range
106 173 def iteritems(d): return iter(d.items())
107 174 def itervalues(d): return iter(d.values())
108 175 getcwd = os.getcwd
109 176
110 177 MethodType = types.MethodType
111 178
112 179 def execfile(fname, glob, loc=None, compiler=None):
113 180 loc = loc if (loc is not None) else glob
114 181 with open(fname, 'rb') as f:
115 182 compiler = compiler or compile
116 183 exec(compiler(f.read(), fname, 'exec'), glob, loc)
117 184
118 185 # Refactor print statements in doctests.
119 186 _print_statement_re = re.compile(r"\bprint (?P<expr>.*)$", re.MULTILINE)
120 187 def _print_statement_sub(match):
121 188 expr = match.groups('expr')
122 189 return "print(%s)" % expr
123 190
124 191 @_modify_str_or_docstring
125 192 def doctest_refactor_print(doc):
126 193 """Refactor 'print x' statements in a doctest to print(x) style. 2to3
127 194 unfortunately doesn't pick up on our doctests.
128 195
129 196 Can accept a string or a function, so it can be used as a decorator."""
130 197 return _print_statement_re.sub(_print_statement_sub, doc)
131 198
132 199 # Abstract u'abc' syntax:
133 200 @_modify_str_or_docstring
134 201 def u_format(s):
135 202 """"{u}'abc'" --> "'abc'" (Python 3)
136 203
137 204 Accepts a string or a function, so it can be used as a decorator."""
138 205 return s.format(u='')
139 206
140 207 def get_closure(f):
141 208 """Get a function's closure attribute"""
142 209 return f.__closure__
143 210
144 211 else:
145 212 PY3 = False
146 213
147 214 # keep reference to builtin_mod because the kernel overrides that value
148 215 # to forward requests to a frontend.
149 216 def input(prompt=''):
150 217 return builtin_mod.raw_input(prompt)
151 218
152 219 builtin_mod_name = "__builtin__"
153 220 import __builtin__ as builtin_mod
154 221
155 222 str_to_unicode = decode
156 223 unicode_to_str = encode
157 224 str_to_bytes = no_code
158 225 bytes_to_str = no_code
159 226 cast_bytes_py2 = cast_bytes
160 227 cast_unicode_py2 = cast_unicode
161 228 buffer_to_bytes_py2 = buffer_to_bytes
162 229
163 230 string_types = (str, unicode)
164 231 unicode_type = unicode
165 232
166 233 import re
167 234 _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
168 235 def isidentifier(s, dotted=False):
169 236 if dotted:
170 237 return all(isidentifier(a) for a in s.split("."))
171 238 return bool(_name_re.match(s))
172 239
173 240 xrange = xrange
174 241 def iteritems(d): return d.iteritems()
175 242 def itervalues(d): return d.itervalues()
176 243 getcwd = os.getcwdu
177 244
178 245 def MethodType(func, instance):
179 246 return types.MethodType(func, instance, type(instance))
180 247
181 248 def doctest_refactor_print(func_or_str):
182 249 return func_or_str
183 250
184 251 def get_closure(f):
185 252 """Get a function's closure attribute"""
186 253 return f.func_closure
254
255 which = _shutil_which
187 256
188 257 # Abstract u'abc' syntax:
189 258 @_modify_str_or_docstring
190 259 def u_format(s):
191 260 """"{u}'abc'" --> "u'abc'" (Python 2)
192 261
193 262 Accepts a string or a function, so it can be used as a decorator."""
194 263 return s.format(u='u')
195 264
196 265 if sys.platform == 'win32':
197 266 def execfile(fname, glob=None, loc=None, compiler=None):
198 267 loc = loc if (loc is not None) else glob
199 268 scripttext = builtin_mod.open(fname).read()+ '\n'
200 269 # compile converts unicode filename to str assuming
201 270 # ascii. Let's do the conversion before calling compile
202 271 if isinstance(fname, unicode):
203 272 filename = unicode_to_str(fname)
204 273 else:
205 274 filename = fname
206 275 compiler = compiler or compile
207 276 exec(compiler(scripttext, filename, 'exec'), glob, loc)
208 277
209 278 else:
210 279 def execfile(fname, glob=None, loc=None, compiler=None):
211 280 if isinstance(fname, unicode):
212 281 filename = fname.encode(sys.getfilesystemencoding())
213 282 else:
214 283 filename = fname
215 284 where = [ns for ns in [glob, loc] if ns is not None]
216 285 if compiler is None:
217 286 builtin_mod.execfile(filename, *where)
218 287 else:
219 288 scripttext = builtin_mod.open(fname).read().rstrip() + '\n'
220 289 exec(compiler(scripttext, filename, 'exec'), glob, loc)
221 290
222 291
223 292 def annotate(**kwargs):
224 293 """Python 3 compatible function annotation for Python 2."""
225 294 if not kwargs:
226 295 raise ValueError('annotations must be provided as keyword arguments')
227 296 def dec(f):
228 297 if hasattr(f, '__annotations__'):
229 298 for k, v in kwargs.items():
230 299 f.__annotations__[k] = v
231 300 else:
232 301 f.__annotations__ = kwargs
233 302 return f
234 303 return dec
235 304
236 305
237 306 # Parts below taken from six:
238 307 # Copyright (c) 2010-2013 Benjamin Peterson
239 308 #
240 309 # Permission is hereby granted, free of charge, to any person obtaining a copy
241 310 # of this software and associated documentation files (the "Software"), to deal
242 311 # in the Software without restriction, including without limitation the rights
243 312 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
244 313 # copies of the Software, and to permit persons to whom the Software is
245 314 # furnished to do so, subject to the following conditions:
246 315 #
247 316 # The above copyright notice and this permission notice shall be included in all
248 317 # copies or substantial portions of the Software.
249 318 #
250 319 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
251 320 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
252 321 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
253 322 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
254 323 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
255 324 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
256 325 # SOFTWARE.
257 326
258 327 def with_metaclass(meta, *bases):
259 328 """Create a base class with a metaclass."""
260 329 return meta("_NewBase", bases, {})
General Comments 0
You need to be logged in to leave comments. Login now