##// END OF EJS Templates
Remove py3compat.open in favour of io.open
Thomas Kluyver -
Show More
@@ -0,0 +1,2 b''
1 * ``IPython.utils.py3compat.open`` was removed: :func:`io.open` provides all
2 the same functionality.
@@ -1,187 +1,188 b''
1 1 # coding: utf-8
2 2 """Tests for the IPython tab-completion machinery.
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 # Module imports
6 6 #-----------------------------------------------------------------------------
7 7
8 8 # stdlib
9 import io
9 10 import os
10 11 import sys
11 12 import tempfile
12 13 from datetime import datetime
13 14
14 15 # third party
15 16 import nose.tools as nt
16 17
17 18 # our own packages
18 19 from IPython.config.loader import Config
19 20 from IPython.utils.tempdir import TemporaryDirectory
20 21 from IPython.core.history import HistoryManager, extract_hist_ranges
21 22 from IPython.utils import py3compat
22 23
23 24 def setUp():
24 25 nt.assert_equal(sys.getdefaultencoding(), "utf-8" if py3compat.PY3 else "ascii")
25 26
26 27 def test_history():
27 28 ip = get_ipython()
28 29 with TemporaryDirectory() as tmpdir:
29 30 hist_manager_ori = ip.history_manager
30 31 hist_file = os.path.join(tmpdir, 'history.sqlite')
31 32 try:
32 33 ip.history_manager = HistoryManager(shell=ip, hist_file=hist_file)
33 34 hist = [u'a=1', u'def f():\n test = 1\n return test', u"b='€Æ¾÷ß'"]
34 35 for i, h in enumerate(hist, start=1):
35 36 ip.history_manager.store_inputs(i, h)
36 37
37 38 ip.history_manager.db_log_output = True
38 39 # Doesn't match the input, but we'll just check it's stored.
39 40 ip.history_manager.output_hist_reprs[3] = "spam"
40 41 ip.history_manager.store_output(3)
41 42
42 43 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
43 44
44 45 # Detailed tests for _get_range_session
45 46 grs = ip.history_manager._get_range_session
46 47 nt.assert_equal(list(grs(start=2,stop=-1)), list(zip([0], [2], hist[1:-1])))
47 48 nt.assert_equal(list(grs(start=-2)), list(zip([0,0], [2,3], hist[-2:])))
48 49 nt.assert_equal(list(grs(output=True)), list(zip([0,0,0], [1,2,3], zip(hist, [None,None,'spam']))))
49 50
50 51 # Check whether specifying a range beyond the end of the current
51 52 # session results in an error (gh-804)
52 53 ip.magic('%hist 2-500')
53 54
54 55 # Check that we can write non-ascii characters to a file
55 56 ip.magic("%%hist -f %s" % os.path.join(tmpdir, "test1"))
56 57 ip.magic("%%hist -pf %s" % os.path.join(tmpdir, "test2"))
57 58 ip.magic("%%hist -nf %s" % os.path.join(tmpdir, "test3"))
58 59 ip.magic("%%save %s 1-10" % os.path.join(tmpdir, "test4"))
59 60
60 61 # New session
61 62 ip.history_manager.reset()
62 63 newcmds = [u"z=5",
63 64 u"class X(object):\n pass",
64 65 u"k='p'",
65 66 u"z=5"]
66 67 for i, cmd in enumerate(newcmds, start=1):
67 68 ip.history_manager.store_inputs(i, cmd)
68 69 gothist = ip.history_manager.get_range(start=1, stop=4)
69 70 nt.assert_equal(list(gothist), list(zip([0,0,0],[1,2,3], newcmds)))
70 71 # Previous session:
71 72 gothist = ip.history_manager.get_range(-1, 1, 4)
72 73 nt.assert_equal(list(gothist), list(zip([1,1,1],[1,2,3], hist)))
73 74
74 75 newhist = [(2, i, c) for (i, c) in enumerate(newcmds, 1)]
75 76
76 77 # Check get_hist_tail
77 78 gothist = ip.history_manager.get_tail(5, output=True,
78 79 include_latest=True)
79 80 expected = [(1, 3, (hist[-1], "spam"))] \
80 81 + [(s, n, (c, None)) for (s, n, c) in newhist]
81 82 nt.assert_equal(list(gothist), expected)
82 83
83 84 gothist = ip.history_manager.get_tail(2)
84 85 expected = newhist[-3:-1]
85 86 nt.assert_equal(list(gothist), expected)
86 87
87 88 # Check get_hist_search
88 89 gothist = ip.history_manager.search("*test*")
89 90 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
90 91
91 92 gothist = ip.history_manager.search("*=*")
92 93 nt.assert_equal(list(gothist),
93 94 [(1, 1, hist[0]),
94 95 (1, 2, hist[1]),
95 96 (1, 3, hist[2]),
96 97 newhist[0],
97 98 newhist[2],
98 99 newhist[3]])
99 100
100 101 gothist = ip.history_manager.search("*=*", n=4)
101 102 nt.assert_equal(list(gothist),
102 103 [(1, 3, hist[2]),
103 104 newhist[0],
104 105 newhist[2],
105 106 newhist[3]])
106 107
107 108 gothist = ip.history_manager.search("*=*", unique=True)
108 109 nt.assert_equal(list(gothist),
109 110 [(1, 1, hist[0]),
110 111 (1, 2, hist[1]),
111 112 (1, 3, hist[2]),
112 113 newhist[2],
113 114 newhist[3]])
114 115
115 116 gothist = ip.history_manager.search("*=*", unique=True, n=3)
116 117 nt.assert_equal(list(gothist),
117 118 [(1, 3, hist[2]),
118 119 newhist[2],
119 120 newhist[3]])
120 121
121 122 gothist = ip.history_manager.search("b*", output=True)
122 123 nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] )
123 124
124 125 # Cross testing: check that magic %save can get previous session.
125 126 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
126 127 ip.magic("save " + testfilename + " ~1/1-3")
127 with py3compat.open(testfilename, encoding='utf-8') as testfile:
128 with io.open(testfilename, encoding='utf-8') as testfile:
128 129 nt.assert_equal(testfile.read(),
129 130 u"# coding: utf-8\n" + u"\n".join(hist)+u"\n")
130 131
131 132 # Duplicate line numbers - check that it doesn't crash, and
132 133 # gets a new session
133 134 ip.history_manager.store_inputs(1, "rogue")
134 135 ip.history_manager.writeout_cache()
135 136 nt.assert_equal(ip.history_manager.session_number, 3)
136 137 finally:
137 138 # Ensure saving thread is shut down before we try to clean up the files
138 139 ip.history_manager.save_thread.stop()
139 140 # Forcibly close database rather than relying on garbage collection
140 141 ip.history_manager.db.close()
141 142 # Restore history manager
142 143 ip.history_manager = hist_manager_ori
143 144
144 145
145 146 def test_extract_hist_ranges():
146 147 instr = "1 2/3 ~4/5-6 ~4/7-~4/9 ~9/2-~7/5 ~10/"
147 148 expected = [(0, 1, 2), # 0 == current session
148 149 (2, 3, 4),
149 150 (-4, 5, 7),
150 151 (-4, 7, 10),
151 152 (-9, 2, None), # None == to end
152 153 (-8, 1, None),
153 154 (-7, 1, 6),
154 155 (-10, 1, None)]
155 156 actual = list(extract_hist_ranges(instr))
156 157 nt.assert_equal(actual, expected)
157 158
158 159 def test_magic_rerun():
159 160 """Simple test for %rerun (no args -> rerun last line)"""
160 161 ip = get_ipython()
161 162 ip.run_cell("a = 10", store_history=True)
162 163 ip.run_cell("a += 1", store_history=True)
163 164 nt.assert_equal(ip.user_ns["a"], 11)
164 165 ip.run_cell("%rerun", store_history=True)
165 166 nt.assert_equal(ip.user_ns["a"], 12)
166 167
167 168 def test_timestamp_type():
168 169 ip = get_ipython()
169 170 info = ip.history_manager.get_session_info()
170 171 nt.assert_true(isinstance(info[1], datetime))
171 172
172 173 def test_hist_file_config():
173 174 cfg = Config()
174 175 tfile = tempfile.NamedTemporaryFile(delete=False)
175 176 cfg.HistoryManager.hist_file = tfile.name
176 177 try:
177 178 hm = HistoryManager(shell=get_ipython(), config=cfg)
178 179 nt.assert_equal(hm.hist_file, cfg.HistoryManager.hist_file)
179 180 finally:
180 181 try:
181 182 os.remove(tfile.name)
182 183 except OSError:
183 184 # same catch as in testing.tools.TempFileMixin
184 185 # On Windows, even though we close the file, we still can't
185 186 # delete it. I have no clue why
186 187 pass
187 188
@@ -1,271 +1,247 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 7 import types
8 8
9 9 from .encoding import DEFAULT_ENCODING
10 10
11 orig_open = open
12
13 11 def no_code(x, encoding=None):
14 12 return x
15 13
16 14 def decode(s, encoding=None):
17 15 encoding = encoding or DEFAULT_ENCODING
18 16 return s.decode(encoding, "replace")
19 17
20 18 def encode(u, encoding=None):
21 19 encoding = encoding or DEFAULT_ENCODING
22 20 return u.encode(encoding, "replace")
23 21
24 22
25 23 def cast_unicode(s, encoding=None):
26 24 if isinstance(s, bytes):
27 25 return decode(s, encoding)
28 26 return s
29 27
30 28 def cast_bytes(s, encoding=None):
31 29 if not isinstance(s, bytes):
32 30 return encode(s, encoding)
33 31 return s
34 32
35 33 def _modify_str_or_docstring(str_change_func):
36 34 @functools.wraps(str_change_func)
37 35 def wrapper(func_or_str):
38 36 if isinstance(func_or_str, string_types):
39 37 func = None
40 38 doc = func_or_str
41 39 else:
42 40 func = func_or_str
43 41 doc = func.__doc__
44 42
45 43 doc = str_change_func(doc)
46 44
47 45 if func:
48 46 func.__doc__ = doc
49 47 return func
50 48 return doc
51 49 return wrapper
52 50
53 51 def safe_unicode(e):
54 52 """unicode(e) with various fallbacks. Used for exceptions, which may not be
55 53 safe to call unicode() on.
56 54 """
57 55 try:
58 56 return unicode_type(e)
59 57 except UnicodeError:
60 58 pass
61 59
62 60 try:
63 61 return str_to_unicode(str(e))
64 62 except UnicodeError:
65 63 pass
66 64
67 65 try:
68 66 return str_to_unicode(repr(e))
69 67 except UnicodeError:
70 68 pass
71 69
72 70 return u'Unrecoverably corrupt evalue'
73 71
74 72 if sys.version_info[0] >= 3:
75 73 PY3 = True
76 74
77 75 # keep reference to builtin_mod because the kernel overrides that value
78 76 # to forward requests to a frontend.
79 77 def input(prompt=''):
80 78 return builtin_mod.input(prompt)
81 79
82 80 builtin_mod_name = "builtins"
83 81 import builtins as builtin_mod
84 82
85 83 str_to_unicode = no_code
86 84 unicode_to_str = no_code
87 85 str_to_bytes = encode
88 86 bytes_to_str = decode
89 87 cast_bytes_py2 = no_code
90 88 cast_unicode_py2 = no_code
91 89
92 90 string_types = (str,)
93 91 unicode_type = str
94 92
95 93 def isidentifier(s, dotted=False):
96 94 if dotted:
97 95 return all(isidentifier(a) for a in s.split("."))
98 96 return s.isidentifier()
99
100 open = orig_open
97
101 98 xrange = range
102 99 def iteritems(d): return iter(d.items())
103 100 def itervalues(d): return iter(d.values())
104 101 getcwd = os.getcwd
105 102
106 103 MethodType = types.MethodType
107 104
108 105 def execfile(fname, glob, loc=None):
109 106 loc = loc if (loc is not None) else glob
110 107 with open(fname, 'rb') as f:
111 108 exec(compile(f.read(), fname, 'exec'), glob, loc)
112 109
113 110 # Refactor print statements in doctests.
114 111 _print_statement_re = re.compile(r"\bprint (?P<expr>.*)$", re.MULTILINE)
115 112 def _print_statement_sub(match):
116 113 expr = match.groups('expr')
117 114 return "print(%s)" % expr
118 115
119 116 @_modify_str_or_docstring
120 117 def doctest_refactor_print(doc):
121 118 """Refactor 'print x' statements in a doctest to print(x) style. 2to3
122 119 unfortunately doesn't pick up on our doctests.
123 120
124 121 Can accept a string or a function, so it can be used as a decorator."""
125 122 return _print_statement_re.sub(_print_statement_sub, doc)
126 123
127 124 # Abstract u'abc' syntax:
128 125 @_modify_str_or_docstring
129 126 def u_format(s):
130 127 """"{u}'abc'" --> "'abc'" (Python 3)
131 128
132 129 Accepts a string or a function, so it can be used as a decorator."""
133 130 return s.format(u='')
134 131
135 132 def get_closure(f):
136 133 """Get a function's closure attribute"""
137 134 return f.__closure__
138 135
139 136 else:
140 137 PY3 = False
141 138
142 139 # keep reference to builtin_mod because the kernel overrides that value
143 140 # to forward requests to a frontend.
144 141 def input(prompt=''):
145 142 return builtin_mod.raw_input(prompt)
146 143
147 144 builtin_mod_name = "__builtin__"
148 145 import __builtin__ as builtin_mod
149 146
150 147 str_to_unicode = decode
151 148 unicode_to_str = encode
152 149 str_to_bytes = no_code
153 150 bytes_to_str = no_code
154 151 cast_bytes_py2 = cast_bytes
155 152 cast_unicode_py2 = cast_unicode
156 153
157 154 string_types = (str, unicode)
158 155 unicode_type = unicode
159 156
160 157 import re
161 158 _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
162 159 def isidentifier(s, dotted=False):
163 160 if dotted:
164 161 return all(isidentifier(a) for a in s.split("."))
165 162 return bool(_name_re.match(s))
166 163
167 class open(object):
168 """Wrapper providing key part of Python 3 open() interface."""
169 def __init__(self, fname, mode="r", encoding="utf-8"):
170 self.f = orig_open(fname, mode)
171 self.enc = encoding
172
173 def write(self, s):
174 return self.f.write(s.encode(self.enc))
175
176 def read(self, size=-1):
177 return self.f.read(size).decode(self.enc)
178
179 def close(self):
180 return self.f.close()
181
182 def __enter__(self):
183 return self
184
185 def __exit__(self, etype, value, traceback):
186 self.f.close()
187
188 164 xrange = xrange
189 165 def iteritems(d): return d.iteritems()
190 166 def itervalues(d): return d.itervalues()
191 167 getcwd = os.getcwdu
192 168
193 169 def MethodType(func, instance):
194 170 return types.MethodType(func, instance, type(instance))
195 171
196 172 def doctest_refactor_print(func_or_str):
197 173 return func_or_str
198 174
199 175 def get_closure(f):
200 176 """Get a function's closure attribute"""
201 177 return f.func_closure
202 178
203 179 # Abstract u'abc' syntax:
204 180 @_modify_str_or_docstring
205 181 def u_format(s):
206 182 """"{u}'abc'" --> "u'abc'" (Python 2)
207 183
208 184 Accepts a string or a function, so it can be used as a decorator."""
209 185 return s.format(u='u')
210 186
211 187 if sys.platform == 'win32':
212 188 def execfile(fname, glob=None, loc=None):
213 189 loc = loc if (loc is not None) else glob
214 190 # The rstrip() is necessary b/c trailing whitespace in files will
215 191 # cause an IndentationError in Python 2.6 (this was fixed in 2.7,
216 192 # but we still support 2.6). See issue 1027.
217 193 scripttext = builtin_mod.open(fname).read().rstrip() + '\n'
218 194 # compile converts unicode filename to str assuming
219 195 # ascii. Let's do the conversion before calling compile
220 196 if isinstance(fname, unicode):
221 197 filename = unicode_to_str(fname)
222 198 else:
223 199 filename = fname
224 200 exec(compile(scripttext, filename, 'exec'), glob, loc)
225 201 else:
226 202 def execfile(fname, *where):
227 203 if isinstance(fname, unicode):
228 204 filename = fname.encode(sys.getfilesystemencoding())
229 205 else:
230 206 filename = fname
231 207 builtin_mod.execfile(filename, *where)
232 208
233 209
234 210 def annotate(**kwargs):
235 211 """Python 3 compatible function annotation for Python 2."""
236 212 if not kwargs:
237 213 raise ValueError('annotations must be provided as keyword arguments')
238 214 def dec(f):
239 215 if hasattr(f, '__annotations__'):
240 216 for k, v in kwargs.items():
241 217 f.__annotations__[k] = v
242 218 else:
243 219 f.__annotations__ = kwargs
244 220 return f
245 221 return dec
246 222
247 223
248 224 # Parts below taken from six:
249 225 # Copyright (c) 2010-2013 Benjamin Peterson
250 226 #
251 227 # Permission is hereby granted, free of charge, to any person obtaining a copy
252 228 # of this software and associated documentation files (the "Software"), to deal
253 229 # in the Software without restriction, including without limitation the rights
254 230 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
255 231 # copies of the Software, and to permit persons to whom the Software is
256 232 # furnished to do so, subject to the following conditions:
257 233 #
258 234 # The above copyright notice and this permission notice shall be included in all
259 235 # copies or substantial portions of the Software.
260 236 #
261 237 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
262 238 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
263 239 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
264 240 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
265 241 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
266 242 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
267 243 # SOFTWARE.
268 244
269 245 def with_metaclass(meta, *bases):
270 246 """Create a base class with a metaclass."""
271 247 return meta("_NewBase", bases, {})
General Comments 0
You need to be logged in to leave comments. Login now