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