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