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