##// END OF EJS Templates
Also catch SyntaxErrors from InputTransformers in run_cell()...
Volker Braun -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,58 b''
1 # coding: utf-8
2 """Tests for the IPython terminal"""
3
4 import os
5 import tempfile
6 import shutil
7
8 import nose.tools as nt
9
10 from IPython.testing.tools import make_tempfile, ipexec
11
12
13 TEST_SYNTAX_ERROR_CMDS = """
14 from IPython.core.inputtransformer import InputTransformer
15
16 %cpaste
17 class SyntaxErrorTransformer(InputTransformer):
18
19 def push(self, line):
20 pos = line.find('syntaxerror')
21 if pos >= 0:
22 e = SyntaxError('input contains "syntaxerror"')
23 e.text = line
24 e.offset = pos + 1
25 raise e
26 return line
27
28 def reset(self):
29 pass
30 --
31
32 ip = get_ipython()
33 transformer = SyntaxErrorTransformer()
34 ip.input_splitter.python_line_transforms.append(transformer)
35 ip.input_transformer_manager.python_line_transforms.append(transformer)
36
37 # now the actual commands
38 1234
39 2345 # syntaxerror <- triggered here
40 3456
41 """
42
43 def test_syntax_error():
44 """Check that the IPython terminal does not abort if a SyntaxError is raised in an InputTransformer"""
45 try:
46 tmp = tempfile.mkdtemp()
47 filename = os.path.join(tmp, 'test_syntax_error.py')
48 with open(filename, 'w') as f:
49 f.write(TEST_SYNTAX_ERROR_CMDS)
50 out, err = ipexec(filename, pipe=True)
51 nt.assert_equal(err, '')
52 nt.assert_in('1234', out)
53 nt.assert_in(' 2345 # syntaxerror <- triggered here', out)
54 nt.assert_in(' ^', out)
55 nt.assert_in('SyntaxError: input contains "syntaxerror"', out)
56 nt.assert_in('3456', out)
57 finally:
58 shutil.rmtree(tmp)
@@ -1,535 +1,538 b''
1 import abc
1 import abc
2 import functools
2 import functools
3 import re
3 import re
4
4
5 from IPython.core.splitinput import LineInfo
5 from IPython.core.splitinput import LineInfo
6 from IPython.utils import tokenize2
6 from IPython.utils import tokenize2
7 from IPython.utils.openpy import cookie_comment_re
7 from IPython.utils.openpy import cookie_comment_re
8 from IPython.utils.py3compat import with_metaclass, PY3
8 from IPython.utils.py3compat import with_metaclass, PY3
9 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
9 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
10
10
11 if PY3:
11 if PY3:
12 from io import StringIO
12 from io import StringIO
13 else:
13 else:
14 from StringIO import StringIO
14 from StringIO import StringIO
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Globals
17 # Globals
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 # The escape sequences that define the syntax transformations IPython will
20 # The escape sequences that define the syntax transformations IPython will
21 # apply to user input. These can NOT be just changed here: many regular
21 # apply to user input. These can NOT be just changed here: many regular
22 # expressions and other parts of the code may use their hardcoded values, and
22 # expressions and other parts of the code may use their hardcoded values, and
23 # for all intents and purposes they constitute the 'IPython syntax', so they
23 # for all intents and purposes they constitute the 'IPython syntax', so they
24 # should be considered fixed.
24 # should be considered fixed.
25
25
26 ESC_SHELL = '!' # Send line to underlying system shell
26 ESC_SHELL = '!' # Send line to underlying system shell
27 ESC_SH_CAP = '!!' # Send line to system shell and capture output
27 ESC_SH_CAP = '!!' # Send line to system shell and capture output
28 ESC_HELP = '?' # Find information about object
28 ESC_HELP = '?' # Find information about object
29 ESC_HELP2 = '??' # Find extra-detailed information about object
29 ESC_HELP2 = '??' # Find extra-detailed information about object
30 ESC_MAGIC = '%' # Call magic function
30 ESC_MAGIC = '%' # Call magic function
31 ESC_MAGIC2 = '%%' # Call cell-magic function
31 ESC_MAGIC2 = '%%' # Call cell-magic function
32 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
32 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
33 ESC_QUOTE2 = ';' # Quote all args as a single string, call
33 ESC_QUOTE2 = ';' # Quote all args as a single string, call
34 ESC_PAREN = '/' # Call first argument with rest of line as arguments
34 ESC_PAREN = '/' # Call first argument with rest of line as arguments
35
35
36 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
36 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
37 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
37 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
38 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
38 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
39
39
40
40
41 class InputTransformer(with_metaclass(abc.ABCMeta, object)):
41 class InputTransformer(with_metaclass(abc.ABCMeta, object)):
42 """Abstract base class for line-based input transformers."""
42 """Abstract base class for line-based input transformers."""
43
43
44 @abc.abstractmethod
44 @abc.abstractmethod
45 def push(self, line):
45 def push(self, line):
46 """Send a line of input to the transformer, returning the transformed
46 """Send a line of input to the transformer, returning the transformed
47 input or None if the transformer is waiting for more input.
47 input or None if the transformer is waiting for more input.
48
48
49 Must be overridden by subclasses.
49 Must be overridden by subclasses.
50
51 Implementations may raise ``SyntaxError`` if the input is invalid. No
52 other exceptions may be raised.
50 """
53 """
51 pass
54 pass
52
55
53 @abc.abstractmethod
56 @abc.abstractmethod
54 def reset(self):
57 def reset(self):
55 """Return, transformed any lines that the transformer has accumulated,
58 """Return, transformed any lines that the transformer has accumulated,
56 and reset its internal state.
59 and reset its internal state.
57
60
58 Must be overridden by subclasses.
61 Must be overridden by subclasses.
59 """
62 """
60 pass
63 pass
61
64
62 @classmethod
65 @classmethod
63 def wrap(cls, func):
66 def wrap(cls, func):
64 """Can be used by subclasses as a decorator, to return a factory that
67 """Can be used by subclasses as a decorator, to return a factory that
65 will allow instantiation with the decorated object.
68 will allow instantiation with the decorated object.
66 """
69 """
67 @functools.wraps(func)
70 @functools.wraps(func)
68 def transformer_factory(**kwargs):
71 def transformer_factory(**kwargs):
69 return cls(func, **kwargs)
72 return cls(func, **kwargs)
70
73
71 return transformer_factory
74 return transformer_factory
72
75
73 class StatelessInputTransformer(InputTransformer):
76 class StatelessInputTransformer(InputTransformer):
74 """Wrapper for a stateless input transformer implemented as a function."""
77 """Wrapper for a stateless input transformer implemented as a function."""
75 def __init__(self, func):
78 def __init__(self, func):
76 self.func = func
79 self.func = func
77
80
78 def __repr__(self):
81 def __repr__(self):
79 return "StatelessInputTransformer(func={0!r})".format(self.func)
82 return "StatelessInputTransformer(func={0!r})".format(self.func)
80
83
81 def push(self, line):
84 def push(self, line):
82 """Send a line of input to the transformer, returning the
85 """Send a line of input to the transformer, returning the
83 transformed input."""
86 transformed input."""
84 return self.func(line)
87 return self.func(line)
85
88
86 def reset(self):
89 def reset(self):
87 """No-op - exists for compatibility."""
90 """No-op - exists for compatibility."""
88 pass
91 pass
89
92
90 class CoroutineInputTransformer(InputTransformer):
93 class CoroutineInputTransformer(InputTransformer):
91 """Wrapper for an input transformer implemented as a coroutine."""
94 """Wrapper for an input transformer implemented as a coroutine."""
92 def __init__(self, coro, **kwargs):
95 def __init__(self, coro, **kwargs):
93 # Prime it
96 # Prime it
94 self.coro = coro(**kwargs)
97 self.coro = coro(**kwargs)
95 next(self.coro)
98 next(self.coro)
96
99
97 def __repr__(self):
100 def __repr__(self):
98 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
101 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
99
102
100 def push(self, line):
103 def push(self, line):
101 """Send a line of input to the transformer, returning the
104 """Send a line of input to the transformer, returning the
102 transformed input or None if the transformer is waiting for more
105 transformed input or None if the transformer is waiting for more
103 input.
106 input.
104 """
107 """
105 return self.coro.send(line)
108 return self.coro.send(line)
106
109
107 def reset(self):
110 def reset(self):
108 """Return, transformed any lines that the transformer has
111 """Return, transformed any lines that the transformer has
109 accumulated, and reset its internal state.
112 accumulated, and reset its internal state.
110 """
113 """
111 return self.coro.send(None)
114 return self.coro.send(None)
112
115
113 class TokenInputTransformer(InputTransformer):
116 class TokenInputTransformer(InputTransformer):
114 """Wrapper for a token-based input transformer.
117 """Wrapper for a token-based input transformer.
115
118
116 func should accept a list of tokens (5-tuples, see tokenize docs), and
119 func should accept a list of tokens (5-tuples, see tokenize docs), and
117 return an iterable which can be passed to tokenize.untokenize().
120 return an iterable which can be passed to tokenize.untokenize().
118 """
121 """
119 def __init__(self, func):
122 def __init__(self, func):
120 self.func = func
123 self.func = func
121 self.current_line = ""
124 self.current_line = ""
122 self.line_used = False
125 self.line_used = False
123 self.reset_tokenizer()
126 self.reset_tokenizer()
124
127
125 def reset_tokenizer(self):
128 def reset_tokenizer(self):
126 self.tokenizer = generate_tokens(self.get_line)
129 self.tokenizer = generate_tokens(self.get_line)
127
130
128 def get_line(self):
131 def get_line(self):
129 if self.line_used:
132 if self.line_used:
130 raise TokenError
133 raise TokenError
131 self.line_used = True
134 self.line_used = True
132 return self.current_line
135 return self.current_line
133
136
134 def push(self, line):
137 def push(self, line):
135 self.current_line += line + "\n"
138 self.current_line += line + "\n"
136 if self.current_line.isspace():
139 if self.current_line.isspace():
137 return self.reset()
140 return self.reset()
138
141
139 self.line_used = False
142 self.line_used = False
140 tokens = []
143 tokens = []
141 stop_at_NL = False
144 stop_at_NL = False
142 try:
145 try:
143 for intok in self.tokenizer:
146 for intok in self.tokenizer:
144 tokens.append(intok)
147 tokens.append(intok)
145 t = intok[0]
148 t = intok[0]
146 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
149 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
147 # Stop before we try to pull a line we don't have yet
150 # Stop before we try to pull a line we don't have yet
148 break
151 break
149 elif t == tokenize2.ERRORTOKEN:
152 elif t == tokenize2.ERRORTOKEN:
150 stop_at_NL = True
153 stop_at_NL = True
151 except TokenError:
154 except TokenError:
152 # Multi-line statement - stop and try again with the next line
155 # Multi-line statement - stop and try again with the next line
153 self.reset_tokenizer()
156 self.reset_tokenizer()
154 return None
157 return None
155
158
156 return self.output(tokens)
159 return self.output(tokens)
157
160
158 def output(self, tokens):
161 def output(self, tokens):
159 self.current_line = ""
162 self.current_line = ""
160 self.reset_tokenizer()
163 self.reset_tokenizer()
161 return untokenize(self.func(tokens)).rstrip('\n')
164 return untokenize(self.func(tokens)).rstrip('\n')
162
165
163 def reset(self):
166 def reset(self):
164 l = self.current_line
167 l = self.current_line
165 self.current_line = ""
168 self.current_line = ""
166 self.reset_tokenizer()
169 self.reset_tokenizer()
167 if l:
170 if l:
168 return l.rstrip('\n')
171 return l.rstrip('\n')
169
172
170 class assemble_python_lines(TokenInputTransformer):
173 class assemble_python_lines(TokenInputTransformer):
171 def __init__(self):
174 def __init__(self):
172 super(assemble_python_lines, self).__init__(None)
175 super(assemble_python_lines, self).__init__(None)
173
176
174 def output(self, tokens):
177 def output(self, tokens):
175 return self.reset()
178 return self.reset()
176
179
177 @CoroutineInputTransformer.wrap
180 @CoroutineInputTransformer.wrap
178 def assemble_logical_lines():
181 def assemble_logical_lines():
179 """Join lines following explicit line continuations (\)"""
182 """Join lines following explicit line continuations (\)"""
180 line = ''
183 line = ''
181 while True:
184 while True:
182 line = (yield line)
185 line = (yield line)
183 if not line or line.isspace():
186 if not line or line.isspace():
184 continue
187 continue
185
188
186 parts = []
189 parts = []
187 while line is not None:
190 while line is not None:
188 if line.endswith('\\') and (not has_comment(line)):
191 if line.endswith('\\') and (not has_comment(line)):
189 parts.append(line[:-1])
192 parts.append(line[:-1])
190 line = (yield None) # Get another line
193 line = (yield None) # Get another line
191 else:
194 else:
192 parts.append(line)
195 parts.append(line)
193 break
196 break
194
197
195 # Output
198 # Output
196 line = ''.join(parts)
199 line = ''.join(parts)
197
200
198 # Utilities
201 # Utilities
199 def _make_help_call(target, esc, lspace, next_input=None):
202 def _make_help_call(target, esc, lspace, next_input=None):
200 """Prepares a pinfo(2)/psearch call from a target name and the escape
203 """Prepares a pinfo(2)/psearch call from a target name and the escape
201 (i.e. ? or ??)"""
204 (i.e. ? or ??)"""
202 method = 'pinfo2' if esc == '??' \
205 method = 'pinfo2' if esc == '??' \
203 else 'psearch' if '*' in target \
206 else 'psearch' if '*' in target \
204 else 'pinfo'
207 else 'pinfo'
205 arg = " ".join([method, target])
208 arg = " ".join([method, target])
206 if next_input is None:
209 if next_input is None:
207 return '%sget_ipython().magic(%r)' % (lspace, arg)
210 return '%sget_ipython().magic(%r)' % (lspace, arg)
208 else:
211 else:
209 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
212 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
210 (lspace, next_input, arg)
213 (lspace, next_input, arg)
211
214
212 # These define the transformations for the different escape characters.
215 # These define the transformations for the different escape characters.
213 def _tr_system(line_info):
216 def _tr_system(line_info):
214 "Translate lines escaped with: !"
217 "Translate lines escaped with: !"
215 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
218 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
216 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
219 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
217
220
218 def _tr_system2(line_info):
221 def _tr_system2(line_info):
219 "Translate lines escaped with: !!"
222 "Translate lines escaped with: !!"
220 cmd = line_info.line.lstrip()[2:]
223 cmd = line_info.line.lstrip()[2:]
221 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
224 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
222
225
223 def _tr_help(line_info):
226 def _tr_help(line_info):
224 "Translate lines escaped with: ?/??"
227 "Translate lines escaped with: ?/??"
225 # A naked help line should just fire the intro help screen
228 # A naked help line should just fire the intro help screen
226 if not line_info.line[1:]:
229 if not line_info.line[1:]:
227 return 'get_ipython().show_usage()'
230 return 'get_ipython().show_usage()'
228
231
229 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
232 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
230
233
231 def _tr_magic(line_info):
234 def _tr_magic(line_info):
232 "Translate lines escaped with: %"
235 "Translate lines escaped with: %"
233 tpl = '%sget_ipython().magic(%r)'
236 tpl = '%sget_ipython().magic(%r)'
234 if line_info.line.startswith(ESC_MAGIC2):
237 if line_info.line.startswith(ESC_MAGIC2):
235 return line_info.line
238 return line_info.line
236 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
239 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
237 return tpl % (line_info.pre, cmd)
240 return tpl % (line_info.pre, cmd)
238
241
239 def _tr_quote(line_info):
242 def _tr_quote(line_info):
240 "Translate lines escaped with: ,"
243 "Translate lines escaped with: ,"
241 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
244 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
242 '", "'.join(line_info.the_rest.split()) )
245 '", "'.join(line_info.the_rest.split()) )
243
246
244 def _tr_quote2(line_info):
247 def _tr_quote2(line_info):
245 "Translate lines escaped with: ;"
248 "Translate lines escaped with: ;"
246 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
249 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
247 line_info.the_rest)
250 line_info.the_rest)
248
251
249 def _tr_paren(line_info):
252 def _tr_paren(line_info):
250 "Translate lines escaped with: /"
253 "Translate lines escaped with: /"
251 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
254 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
252 ", ".join(line_info.the_rest.split()))
255 ", ".join(line_info.the_rest.split()))
253
256
254 tr = { ESC_SHELL : _tr_system,
257 tr = { ESC_SHELL : _tr_system,
255 ESC_SH_CAP : _tr_system2,
258 ESC_SH_CAP : _tr_system2,
256 ESC_HELP : _tr_help,
259 ESC_HELP : _tr_help,
257 ESC_HELP2 : _tr_help,
260 ESC_HELP2 : _tr_help,
258 ESC_MAGIC : _tr_magic,
261 ESC_MAGIC : _tr_magic,
259 ESC_QUOTE : _tr_quote,
262 ESC_QUOTE : _tr_quote,
260 ESC_QUOTE2 : _tr_quote2,
263 ESC_QUOTE2 : _tr_quote2,
261 ESC_PAREN : _tr_paren }
264 ESC_PAREN : _tr_paren }
262
265
263 @StatelessInputTransformer.wrap
266 @StatelessInputTransformer.wrap
264 def escaped_commands(line):
267 def escaped_commands(line):
265 """Transform escaped commands - %magic, !system, ?help + various autocalls.
268 """Transform escaped commands - %magic, !system, ?help + various autocalls.
266 """
269 """
267 if not line or line.isspace():
270 if not line or line.isspace():
268 return line
271 return line
269 lineinf = LineInfo(line)
272 lineinf = LineInfo(line)
270 if lineinf.esc not in tr:
273 if lineinf.esc not in tr:
271 return line
274 return line
272
275
273 return tr[lineinf.esc](lineinf)
276 return tr[lineinf.esc](lineinf)
274
277
275 _initial_space_re = re.compile(r'\s*')
278 _initial_space_re = re.compile(r'\s*')
276
279
277 _help_end_re = re.compile(r"""(%{0,2}
280 _help_end_re = re.compile(r"""(%{0,2}
278 [a-zA-Z_*][\w*]* # Variable name
281 [a-zA-Z_*][\w*]* # Variable name
279 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
282 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
280 )
283 )
281 (\?\??)$ # ? or ??
284 (\?\??)$ # ? or ??
282 """,
285 """,
283 re.VERBOSE)
286 re.VERBOSE)
284
287
285 # Extra pseudotokens for multiline strings and data structures
288 # Extra pseudotokens for multiline strings and data structures
286 _MULTILINE_STRING = object()
289 _MULTILINE_STRING = object()
287 _MULTILINE_STRUCTURE = object()
290 _MULTILINE_STRUCTURE = object()
288
291
289 def _line_tokens(line):
292 def _line_tokens(line):
290 """Helper for has_comment and ends_in_comment_or_string."""
293 """Helper for has_comment and ends_in_comment_or_string."""
291 readline = StringIO(line).readline
294 readline = StringIO(line).readline
292 toktypes = set()
295 toktypes = set()
293 try:
296 try:
294 for t in generate_tokens(readline):
297 for t in generate_tokens(readline):
295 toktypes.add(t[0])
298 toktypes.add(t[0])
296 except TokenError as e:
299 except TokenError as e:
297 # There are only two cases where a TokenError is raised.
300 # There are only two cases where a TokenError is raised.
298 if 'multi-line string' in e.args[0]:
301 if 'multi-line string' in e.args[0]:
299 toktypes.add(_MULTILINE_STRING)
302 toktypes.add(_MULTILINE_STRING)
300 else:
303 else:
301 toktypes.add(_MULTILINE_STRUCTURE)
304 toktypes.add(_MULTILINE_STRUCTURE)
302 return toktypes
305 return toktypes
303
306
304 def has_comment(src):
307 def has_comment(src):
305 """Indicate whether an input line has (i.e. ends in, or is) a comment.
308 """Indicate whether an input line has (i.e. ends in, or is) a comment.
306
309
307 This uses tokenize, so it can distinguish comments from # inside strings.
310 This uses tokenize, so it can distinguish comments from # inside strings.
308
311
309 Parameters
312 Parameters
310 ----------
313 ----------
311 src : string
314 src : string
312 A single line input string.
315 A single line input string.
313
316
314 Returns
317 Returns
315 -------
318 -------
316 comment : bool
319 comment : bool
317 True if source has a comment.
320 True if source has a comment.
318 """
321 """
319 return (tokenize2.COMMENT in _line_tokens(src))
322 return (tokenize2.COMMENT in _line_tokens(src))
320
323
321 def ends_in_comment_or_string(src):
324 def ends_in_comment_or_string(src):
322 """Indicates whether or not an input line ends in a comment or within
325 """Indicates whether or not an input line ends in a comment or within
323 a multiline string.
326 a multiline string.
324
327
325 Parameters
328 Parameters
326 ----------
329 ----------
327 src : string
330 src : string
328 A single line input string.
331 A single line input string.
329
332
330 Returns
333 Returns
331 -------
334 -------
332 comment : bool
335 comment : bool
333 True if source ends in a comment or multiline string.
336 True if source ends in a comment or multiline string.
334 """
337 """
335 toktypes = _line_tokens(src)
338 toktypes = _line_tokens(src)
336 return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
339 return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
337
340
338
341
339 @StatelessInputTransformer.wrap
342 @StatelessInputTransformer.wrap
340 def help_end(line):
343 def help_end(line):
341 """Translate lines with ?/?? at the end"""
344 """Translate lines with ?/?? at the end"""
342 m = _help_end_re.search(line)
345 m = _help_end_re.search(line)
343 if m is None or ends_in_comment_or_string(line):
346 if m is None or ends_in_comment_or_string(line):
344 return line
347 return line
345 target = m.group(1)
348 target = m.group(1)
346 esc = m.group(3)
349 esc = m.group(3)
347 lspace = _initial_space_re.match(line).group(0)
350 lspace = _initial_space_re.match(line).group(0)
348
351
349 # If we're mid-command, put it back on the next prompt for the user.
352 # If we're mid-command, put it back on the next prompt for the user.
350 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
353 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
351
354
352 return _make_help_call(target, esc, lspace, next_input)
355 return _make_help_call(target, esc, lspace, next_input)
353
356
354
357
355 @CoroutineInputTransformer.wrap
358 @CoroutineInputTransformer.wrap
356 def cellmagic(end_on_blank_line=False):
359 def cellmagic(end_on_blank_line=False):
357 """Captures & transforms cell magics.
360 """Captures & transforms cell magics.
358
361
359 After a cell magic is started, this stores up any lines it gets until it is
362 After a cell magic is started, this stores up any lines it gets until it is
360 reset (sent None).
363 reset (sent None).
361 """
364 """
362 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
365 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
363 cellmagic_help_re = re.compile('%%\w+\?')
366 cellmagic_help_re = re.compile('%%\w+\?')
364 line = ''
367 line = ''
365 while True:
368 while True:
366 line = (yield line)
369 line = (yield line)
367 # consume leading empty lines
370 # consume leading empty lines
368 while not line:
371 while not line:
369 line = (yield line)
372 line = (yield line)
370
373
371 if not line.startswith(ESC_MAGIC2):
374 if not line.startswith(ESC_MAGIC2):
372 # This isn't a cell magic, idle waiting for reset then start over
375 # This isn't a cell magic, idle waiting for reset then start over
373 while line is not None:
376 while line is not None:
374 line = (yield line)
377 line = (yield line)
375 continue
378 continue
376
379
377 if cellmagic_help_re.match(line):
380 if cellmagic_help_re.match(line):
378 # This case will be handled by help_end
381 # This case will be handled by help_end
379 continue
382 continue
380
383
381 first = line
384 first = line
382 body = []
385 body = []
383 line = (yield None)
386 line = (yield None)
384 while (line is not None) and \
387 while (line is not None) and \
385 ((line.strip() != '') or not end_on_blank_line):
388 ((line.strip() != '') or not end_on_blank_line):
386 body.append(line)
389 body.append(line)
387 line = (yield None)
390 line = (yield None)
388
391
389 # Output
392 # Output
390 magic_name, _, first = first.partition(' ')
393 magic_name, _, first = first.partition(' ')
391 magic_name = magic_name.lstrip(ESC_MAGIC2)
394 magic_name = magic_name.lstrip(ESC_MAGIC2)
392 line = tpl % (magic_name, first, u'\n'.join(body))
395 line = tpl % (magic_name, first, u'\n'.join(body))
393
396
394
397
395 def _strip_prompts(prompt_re, initial_re=None):
398 def _strip_prompts(prompt_re, initial_re=None):
396 """Remove matching input prompts from a block of input.
399 """Remove matching input prompts from a block of input.
397
400
398 Parameters
401 Parameters
399 ----------
402 ----------
400 prompt_re : regular expression
403 prompt_re : regular expression
401 A regular expression matching any input prompt (including continuation)
404 A regular expression matching any input prompt (including continuation)
402 initial_re : regular expression, optional
405 initial_re : regular expression, optional
403 A regular expression matching only the initial prompt, but not continuation.
406 A regular expression matching only the initial prompt, but not continuation.
404 If no initial expression is given, prompt_re will be used everywhere.
407 If no initial expression is given, prompt_re will be used everywhere.
405 Used mainly for plain Python prompts, where the continuation prompt
408 Used mainly for plain Python prompts, where the continuation prompt
406 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
409 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
407
410
408 If initial_re and prompt_re differ,
411 If initial_re and prompt_re differ,
409 only initial_re will be tested against the first line.
412 only initial_re will be tested against the first line.
410 If any prompt is found on the first two lines,
413 If any prompt is found on the first two lines,
411 prompts will be stripped from the rest of the block.
414 prompts will be stripped from the rest of the block.
412 """
415 """
413 if initial_re is None:
416 if initial_re is None:
414 initial_re = prompt_re
417 initial_re = prompt_re
415 line = ''
418 line = ''
416 while True:
419 while True:
417 line = (yield line)
420 line = (yield line)
418
421
419 # First line of cell
422 # First line of cell
420 if line is None:
423 if line is None:
421 continue
424 continue
422 out, n1 = initial_re.subn('', line, count=1)
425 out, n1 = initial_re.subn('', line, count=1)
423 line = (yield out)
426 line = (yield out)
424
427
425 if line is None:
428 if line is None:
426 continue
429 continue
427 # check for any prompt on the second line of the cell,
430 # check for any prompt on the second line of the cell,
428 # because people often copy from just after the first prompt,
431 # because people often copy from just after the first prompt,
429 # so we might not see it in the first line.
432 # so we might not see it in the first line.
430 out, n2 = prompt_re.subn('', line, count=1)
433 out, n2 = prompt_re.subn('', line, count=1)
431 line = (yield out)
434 line = (yield out)
432
435
433 if n1 or n2:
436 if n1 or n2:
434 # Found a prompt in the first two lines - check for it in
437 # Found a prompt in the first two lines - check for it in
435 # the rest of the cell as well.
438 # the rest of the cell as well.
436 while line is not None:
439 while line is not None:
437 line = (yield prompt_re.sub('', line, count=1))
440 line = (yield prompt_re.sub('', line, count=1))
438
441
439 else:
442 else:
440 # Prompts not in input - wait for reset
443 # Prompts not in input - wait for reset
441 while line is not None:
444 while line is not None:
442 line = (yield line)
445 line = (yield line)
443
446
444 @CoroutineInputTransformer.wrap
447 @CoroutineInputTransformer.wrap
445 def classic_prompt():
448 def classic_prompt():
446 """Strip the >>>/... prompts of the Python interactive shell."""
449 """Strip the >>>/... prompts of the Python interactive shell."""
447 # FIXME: non-capturing version (?:...) usable?
450 # FIXME: non-capturing version (?:...) usable?
448 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
451 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
449 initial_re = re.compile(r'^(>>> ?)')
452 initial_re = re.compile(r'^(>>> ?)')
450 return _strip_prompts(prompt_re, initial_re)
453 return _strip_prompts(prompt_re, initial_re)
451
454
452 @CoroutineInputTransformer.wrap
455 @CoroutineInputTransformer.wrap
453 def ipy_prompt():
456 def ipy_prompt():
454 """Strip IPython's In [1]:/...: prompts."""
457 """Strip IPython's In [1]:/...: prompts."""
455 # FIXME: non-capturing version (?:...) usable?
458 # FIXME: non-capturing version (?:...) usable?
456 # FIXME: r'^(In \[\d+\]: | {3}\.{3,}: )' clearer?
459 # FIXME: r'^(In \[\d+\]: | {3}\.{3,}: )' clearer?
457 prompt_re = re.compile(r'^(In \[\d+\]: |\ \ \ \.\.\.+: )')
460 prompt_re = re.compile(r'^(In \[\d+\]: |\ \ \ \.\.\.+: )')
458 return _strip_prompts(prompt_re)
461 return _strip_prompts(prompt_re)
459
462
460
463
461 @CoroutineInputTransformer.wrap
464 @CoroutineInputTransformer.wrap
462 def leading_indent():
465 def leading_indent():
463 """Remove leading indentation.
466 """Remove leading indentation.
464
467
465 If the first line starts with a spaces or tabs, the same whitespace will be
468 If the first line starts with a spaces or tabs, the same whitespace will be
466 removed from each following line until it is reset.
469 removed from each following line until it is reset.
467 """
470 """
468 space_re = re.compile(r'^[ \t]+')
471 space_re = re.compile(r'^[ \t]+')
469 line = ''
472 line = ''
470 while True:
473 while True:
471 line = (yield line)
474 line = (yield line)
472
475
473 if line is None:
476 if line is None:
474 continue
477 continue
475
478
476 m = space_re.match(line)
479 m = space_re.match(line)
477 if m:
480 if m:
478 space = m.group(0)
481 space = m.group(0)
479 while line is not None:
482 while line is not None:
480 if line.startswith(space):
483 if line.startswith(space):
481 line = line[len(space):]
484 line = line[len(space):]
482 line = (yield line)
485 line = (yield line)
483 else:
486 else:
484 # No leading spaces - wait for reset
487 # No leading spaces - wait for reset
485 while line is not None:
488 while line is not None:
486 line = (yield line)
489 line = (yield line)
487
490
488
491
489 @CoroutineInputTransformer.wrap
492 @CoroutineInputTransformer.wrap
490 def strip_encoding_cookie():
493 def strip_encoding_cookie():
491 """Remove encoding comment if found in first two lines
494 """Remove encoding comment if found in first two lines
492
495
493 If the first or second line has the `# coding: utf-8` comment,
496 If the first or second line has the `# coding: utf-8` comment,
494 it will be removed.
497 it will be removed.
495 """
498 """
496 line = ''
499 line = ''
497 while True:
500 while True:
498 line = (yield line)
501 line = (yield line)
499 # check comment on first two lines
502 # check comment on first two lines
500 for i in range(2):
503 for i in range(2):
501 if line is None:
504 if line is None:
502 break
505 break
503 if cookie_comment_re.match(line):
506 if cookie_comment_re.match(line):
504 line = (yield "")
507 line = (yield "")
505 else:
508 else:
506 line = (yield line)
509 line = (yield line)
507
510
508 # no-op on the rest of the cell
511 # no-op on the rest of the cell
509 while line is not None:
512 while line is not None:
510 line = (yield line)
513 line = (yield line)
511
514
512
515
513 assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
516 assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
514 r'\s*=\s*!\s*(?P<cmd>.*)')
517 r'\s*=\s*!\s*(?P<cmd>.*)')
515 assign_system_template = '%s = get_ipython().getoutput(%r)'
518 assign_system_template = '%s = get_ipython().getoutput(%r)'
516 @StatelessInputTransformer.wrap
519 @StatelessInputTransformer.wrap
517 def assign_from_system(line):
520 def assign_from_system(line):
518 """Transform assignment from system commands (e.g. files = !ls)"""
521 """Transform assignment from system commands (e.g. files = !ls)"""
519 m = assign_system_re.match(line)
522 m = assign_system_re.match(line)
520 if m is None:
523 if m is None:
521 return line
524 return line
522
525
523 return assign_system_template % m.group('lhs', 'cmd')
526 return assign_system_template % m.group('lhs', 'cmd')
524
527
525 assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
528 assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
526 r'\s*=\s*%\s*(?P<cmd>.*)')
529 r'\s*=\s*%\s*(?P<cmd>.*)')
527 assign_magic_template = '%s = get_ipython().magic(%r)'
530 assign_magic_template = '%s = get_ipython().magic(%r)'
528 @StatelessInputTransformer.wrap
531 @StatelessInputTransformer.wrap
529 def assign_from_magic(line):
532 def assign_from_magic(line):
530 """Transform assignment from magic commands (e.g. a = %who_ls)"""
533 """Transform assignment from magic commands (e.g. a = %who_ls)"""
531 m = assign_magic_re.match(line)
534 m = assign_magic_re.match(line)
532 if m is None:
535 if m is None:
533 return line
536 return line
534
537
535 return assign_magic_template % m.group('lhs', 'cmd')
538 return assign_magic_template % m.group('lhs', 'cmd')
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,97 +1,50 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Tests for IPython.core.application"""
2 """Tests for IPython.core.application"""
3
3
4 import os
4 import os
5 import tempfile
5 import tempfile
6 import shutil
7
8 import nose.tools as nt
9
6
10 from IPython.core.application import BaseIPythonApplication
7 from IPython.core.application import BaseIPythonApplication
11 from IPython.testing import decorators as dec
8 from IPython.testing import decorators as dec
12 from IPython.testing.tools import make_tempfile, ipexec
13 from IPython.utils import py3compat
9 from IPython.utils import py3compat
14
10
15 @dec.onlyif_unicode_paths
11 @dec.onlyif_unicode_paths
16 def test_unicode_cwd():
12 def test_unicode_cwd():
17 """Check that IPython starts with non-ascii characters in the path."""
13 """Check that IPython starts with non-ascii characters in the path."""
18 wd = tempfile.mkdtemp(suffix=u"€")
14 wd = tempfile.mkdtemp(suffix=u"€")
19
15
20 old_wd = py3compat.getcwd()
16 old_wd = py3compat.getcwd()
21 os.chdir(wd)
17 os.chdir(wd)
22 #raise Exception(repr(py3compat.getcwd()))
18 #raise Exception(repr(py3compat.getcwd()))
23 try:
19 try:
24 app = BaseIPythonApplication()
20 app = BaseIPythonApplication()
25 # The lines below are copied from Application.initialize()
21 # The lines below are copied from Application.initialize()
26 app.init_profile_dir()
22 app.init_profile_dir()
27 app.init_config_files()
23 app.init_config_files()
28 app.load_config_file(suppress_errors=False)
24 app.load_config_file(suppress_errors=False)
29 finally:
25 finally:
30 os.chdir(old_wd)
26 os.chdir(old_wd)
31
27
32 @dec.onlyif_unicode_paths
28 @dec.onlyif_unicode_paths
33 def test_unicode_ipdir():
29 def test_unicode_ipdir():
34 """Check that IPython starts with non-ascii characters in the IP dir."""
30 """Check that IPython starts with non-ascii characters in the IP dir."""
35 ipdir = tempfile.mkdtemp(suffix=u"€")
31 ipdir = tempfile.mkdtemp(suffix=u"€")
36
32
37 # Create the config file, so it tries to load it.
33 # Create the config file, so it tries to load it.
38 with open(os.path.join(ipdir, 'ipython_config.py'), "w") as f:
34 with open(os.path.join(ipdir, 'ipython_config.py'), "w") as f:
39 pass
35 pass
40
36
41 old_ipdir1 = os.environ.pop("IPYTHONDIR", None)
37 old_ipdir1 = os.environ.pop("IPYTHONDIR", None)
42 old_ipdir2 = os.environ.pop("IPYTHON_DIR", None)
38 old_ipdir2 = os.environ.pop("IPYTHON_DIR", None)
43 os.environ["IPYTHONDIR"] = py3compat.unicode_to_str(ipdir, "utf-8")
39 os.environ["IPYTHONDIR"] = py3compat.unicode_to_str(ipdir, "utf-8")
44 try:
40 try:
45 app = BaseIPythonApplication()
41 app = BaseIPythonApplication()
46 # The lines below are copied from Application.initialize()
42 # The lines below are copied from Application.initialize()
47 app.init_profile_dir()
43 app.init_profile_dir()
48 app.init_config_files()
44 app.init_config_files()
49 app.load_config_file(suppress_errors=False)
45 app.load_config_file(suppress_errors=False)
50 finally:
46 finally:
51 if old_ipdir1:
47 if old_ipdir1:
52 os.environ["IPYTHONDIR"] = old_ipdir1
48 os.environ["IPYTHONDIR"] = old_ipdir1
53 if old_ipdir2:
49 if old_ipdir2:
54 os.environ["IPYTHONDIR"] = old_ipdir2
50 os.environ["IPYTHONDIR"] = old_ipdir2
55
56
57
58 TEST_SYNTAX_ERROR_CMDS = """
59 from IPython.core.inputtransformer import InputTransformer
60
61 %cpaste
62 class SyntaxErrorTransformer(InputTransformer):
63
64 def push(self, line):
65 if 'syntaxerror' in line:
66 raise SyntaxError('in input '+line)
67 return line
68
69 def reset(self):
70 pass
71 --
72
73 ip = get_ipython()
74 transformer = SyntaxErrorTransformer()
75 ip.input_splitter.python_line_transforms.append(transformer)
76 ip.input_transformer_manager.python_line_transforms.append(transformer)
77
78 # now the actual commands
79 1234
80 2345 # syntaxerror <- triggered here
81 3456
82 """
83
84 def test_syntax_error():
85 """Check that IPython does not abort if a SyntaxError is raised in an InputTransformer"""
86 try:
87 tmp = tempfile.mkdtemp()
88 filename = os.path.join(tmp, 'test_syntax_error.py')
89 with open(filename, 'w') as f:
90 f.write(TEST_SYNTAX_ERROR_CMDS)
91 out, err = ipexec(filename, pipe=True)
92 nt.assert_equal(err, '')
93 nt.assert_in('1234', out)
94 nt.assert_in('SyntaxError: in input 2345 # syntaxerror <- triggered here', out)
95 nt.assert_in('3456', out)
96 finally:
97 shutil.rmtree(tmp)
@@ -1,677 +1,715 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the key interactiveshell module.
2 """Tests for the key interactiveshell module.
3
3
4 Historically the main classes in interactiveshell have been under-tested. This
4 Historically the main classes in interactiveshell have been under-tested. This
5 module should grow as many single-method tests as possible to trap many of the
5 module should grow as many single-method tests as possible to trap many of the
6 recurring bugs we seem to encounter with high-level interaction.
6 recurring bugs we seem to encounter with high-level interaction.
7
7
8 Authors
8 Authors
9 -------
9 -------
10 * Fernando Perez
10 * Fernando Perez
11 """
11 """
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2011 The IPython Development Team
13 # Copyright (C) 2011 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # stdlib
22 # stdlib
23 import ast
23 import ast
24 import os
24 import os
25 import signal
25 import signal
26 import shutil
26 import shutil
27 import sys
27 import sys
28 import tempfile
28 import tempfile
29 import unittest
29 import unittest
30 from os.path import join
30 from os.path import join
31
31
32 # third-party
32 # third-party
33 import nose.tools as nt
33 import nose.tools as nt
34
34
35 # Our own
35 # Our own
36 from IPython.core.inputtransformer import InputTransformer
36 from IPython.testing.decorators import skipif, skip_win32, onlyif_unicode_paths
37 from IPython.testing.decorators import skipif, skip_win32, onlyif_unicode_paths
37 from IPython.testing import tools as tt
38 from IPython.testing import tools as tt
38 from IPython.utils import io
39 from IPython.utils import io
39 from IPython.utils import py3compat
40 from IPython.utils import py3compat
40 from IPython.utils.py3compat import unicode_type, PY3
41 from IPython.utils.py3compat import unicode_type, PY3
41
42
42 if PY3:
43 if PY3:
43 from io import StringIO
44 from io import StringIO
44 else:
45 else:
45 from StringIO import StringIO
46 from StringIO import StringIO
46
47
47 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
48 # Globals
49 # Globals
49 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
50 # This is used by every single test, no point repeating it ad nauseam
51 # This is used by every single test, no point repeating it ad nauseam
51 ip = get_ipython()
52 ip = get_ipython()
52
53
53 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
54 # Tests
55 # Tests
55 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
56
57
57 class InteractiveShellTestCase(unittest.TestCase):
58 class InteractiveShellTestCase(unittest.TestCase):
58 def test_naked_string_cells(self):
59 def test_naked_string_cells(self):
59 """Test that cells with only naked strings are fully executed"""
60 """Test that cells with only naked strings are fully executed"""
60 # First, single-line inputs
61 # First, single-line inputs
61 ip.run_cell('"a"\n')
62 ip.run_cell('"a"\n')
62 self.assertEqual(ip.user_ns['_'], 'a')
63 self.assertEqual(ip.user_ns['_'], 'a')
63 # And also multi-line cells
64 # And also multi-line cells
64 ip.run_cell('"""a\nb"""\n')
65 ip.run_cell('"""a\nb"""\n')
65 self.assertEqual(ip.user_ns['_'], 'a\nb')
66 self.assertEqual(ip.user_ns['_'], 'a\nb')
66
67
67 def test_run_empty_cell(self):
68 def test_run_empty_cell(self):
68 """Just make sure we don't get a horrible error with a blank
69 """Just make sure we don't get a horrible error with a blank
69 cell of input. Yes, I did overlook that."""
70 cell of input. Yes, I did overlook that."""
70 old_xc = ip.execution_count
71 old_xc = ip.execution_count
71 ip.run_cell('')
72 ip.run_cell('')
72 self.assertEqual(ip.execution_count, old_xc)
73 self.assertEqual(ip.execution_count, old_xc)
73
74
74 def test_run_cell_multiline(self):
75 def test_run_cell_multiline(self):
75 """Multi-block, multi-line cells must execute correctly.
76 """Multi-block, multi-line cells must execute correctly.
76 """
77 """
77 src = '\n'.join(["x=1",
78 src = '\n'.join(["x=1",
78 "y=2",
79 "y=2",
79 "if 1:",
80 "if 1:",
80 " x += 1",
81 " x += 1",
81 " y += 1",])
82 " y += 1",])
82 ip.run_cell(src)
83 ip.run_cell(src)
83 self.assertEqual(ip.user_ns['x'], 2)
84 self.assertEqual(ip.user_ns['x'], 2)
84 self.assertEqual(ip.user_ns['y'], 3)
85 self.assertEqual(ip.user_ns['y'], 3)
85
86
86 def test_multiline_string_cells(self):
87 def test_multiline_string_cells(self):
87 "Code sprinkled with multiline strings should execute (GH-306)"
88 "Code sprinkled with multiline strings should execute (GH-306)"
88 ip.run_cell('tmp=0')
89 ip.run_cell('tmp=0')
89 self.assertEqual(ip.user_ns['tmp'], 0)
90 self.assertEqual(ip.user_ns['tmp'], 0)
90 ip.run_cell('tmp=1;"""a\nb"""\n')
91 ip.run_cell('tmp=1;"""a\nb"""\n')
91 self.assertEqual(ip.user_ns['tmp'], 1)
92 self.assertEqual(ip.user_ns['tmp'], 1)
92
93
93 def test_dont_cache_with_semicolon(self):
94 def test_dont_cache_with_semicolon(self):
94 "Ending a line with semicolon should not cache the returned object (GH-307)"
95 "Ending a line with semicolon should not cache the returned object (GH-307)"
95 oldlen = len(ip.user_ns['Out'])
96 oldlen = len(ip.user_ns['Out'])
96 a = ip.run_cell('1;', store_history=True)
97 a = ip.run_cell('1;', store_history=True)
97 newlen = len(ip.user_ns['Out'])
98 newlen = len(ip.user_ns['Out'])
98 self.assertEqual(oldlen, newlen)
99 self.assertEqual(oldlen, newlen)
99 #also test the default caching behavior
100 #also test the default caching behavior
100 ip.run_cell('1', store_history=True)
101 ip.run_cell('1', store_history=True)
101 newlen = len(ip.user_ns['Out'])
102 newlen = len(ip.user_ns['Out'])
102 self.assertEqual(oldlen+1, newlen)
103 self.assertEqual(oldlen+1, newlen)
103
104
104 def test_In_variable(self):
105 def test_In_variable(self):
105 "Verify that In variable grows with user input (GH-284)"
106 "Verify that In variable grows with user input (GH-284)"
106 oldlen = len(ip.user_ns['In'])
107 oldlen = len(ip.user_ns['In'])
107 ip.run_cell('1;', store_history=True)
108 ip.run_cell('1;', store_history=True)
108 newlen = len(ip.user_ns['In'])
109 newlen = len(ip.user_ns['In'])
109 self.assertEqual(oldlen+1, newlen)
110 self.assertEqual(oldlen+1, newlen)
110 self.assertEqual(ip.user_ns['In'][-1],'1;')
111 self.assertEqual(ip.user_ns['In'][-1],'1;')
111
112
112 def test_magic_names_in_string(self):
113 def test_magic_names_in_string(self):
113 ip.run_cell('a = """\n%exit\n"""')
114 ip.run_cell('a = """\n%exit\n"""')
114 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
115 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
115
116
116 def test_trailing_newline(self):
117 def test_trailing_newline(self):
117 """test that running !(command) does not raise a SyntaxError"""
118 """test that running !(command) does not raise a SyntaxError"""
118 ip.run_cell('!(true)\n', False)
119 ip.run_cell('!(true)\n', False)
119 ip.run_cell('!(true)\n\n\n', False)
120 ip.run_cell('!(true)\n\n\n', False)
120
121
121 def test_gh_597(self):
122 def test_gh_597(self):
122 """Pretty-printing lists of objects with non-ascii reprs may cause
123 """Pretty-printing lists of objects with non-ascii reprs may cause
123 problems."""
124 problems."""
124 class Spam(object):
125 class Spam(object):
125 def __repr__(self):
126 def __repr__(self):
126 return "\xe9"*50
127 return "\xe9"*50
127 import IPython.core.formatters
128 import IPython.core.formatters
128 f = IPython.core.formatters.PlainTextFormatter()
129 f = IPython.core.formatters.PlainTextFormatter()
129 f([Spam(),Spam()])
130 f([Spam(),Spam()])
130
131
131
132
132 def test_future_flags(self):
133 def test_future_flags(self):
133 """Check that future flags are used for parsing code (gh-777)"""
134 """Check that future flags are used for parsing code (gh-777)"""
134 ip.run_cell('from __future__ import print_function')
135 ip.run_cell('from __future__ import print_function')
135 try:
136 try:
136 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
137 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
137 assert 'prfunc_return_val' in ip.user_ns
138 assert 'prfunc_return_val' in ip.user_ns
138 finally:
139 finally:
139 # Reset compiler flags so we don't mess up other tests.
140 # Reset compiler flags so we don't mess up other tests.
140 ip.compile.reset_compiler_flags()
141 ip.compile.reset_compiler_flags()
141
142
142 def test_future_unicode(self):
143 def test_future_unicode(self):
143 """Check that unicode_literals is imported from __future__ (gh #786)"""
144 """Check that unicode_literals is imported from __future__ (gh #786)"""
144 try:
145 try:
145 ip.run_cell(u'byte_str = "a"')
146 ip.run_cell(u'byte_str = "a"')
146 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
147 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
147 ip.run_cell('from __future__ import unicode_literals')
148 ip.run_cell('from __future__ import unicode_literals')
148 ip.run_cell(u'unicode_str = "a"')
149 ip.run_cell(u'unicode_str = "a"')
149 assert isinstance(ip.user_ns['unicode_str'], unicode_type) # strings literals are now unicode
150 assert isinstance(ip.user_ns['unicode_str'], unicode_type) # strings literals are now unicode
150 finally:
151 finally:
151 # Reset compiler flags so we don't mess up other tests.
152 # Reset compiler flags so we don't mess up other tests.
152 ip.compile.reset_compiler_flags()
153 ip.compile.reset_compiler_flags()
153
154
154 def test_can_pickle(self):
155 def test_can_pickle(self):
155 "Can we pickle objects defined interactively (GH-29)"
156 "Can we pickle objects defined interactively (GH-29)"
156 ip = get_ipython()
157 ip = get_ipython()
157 ip.reset()
158 ip.reset()
158 ip.run_cell(("class Mylist(list):\n"
159 ip.run_cell(("class Mylist(list):\n"
159 " def __init__(self,x=[]):\n"
160 " def __init__(self,x=[]):\n"
160 " list.__init__(self,x)"))
161 " list.__init__(self,x)"))
161 ip.run_cell("w=Mylist([1,2,3])")
162 ip.run_cell("w=Mylist([1,2,3])")
162
163
163 from pickle import dumps
164 from pickle import dumps
164
165
165 # We need to swap in our main module - this is only necessary
166 # We need to swap in our main module - this is only necessary
166 # inside the test framework, because IPython puts the interactive module
167 # inside the test framework, because IPython puts the interactive module
167 # in place (but the test framework undoes this).
168 # in place (but the test framework undoes this).
168 _main = sys.modules['__main__']
169 _main = sys.modules['__main__']
169 sys.modules['__main__'] = ip.user_module
170 sys.modules['__main__'] = ip.user_module
170 try:
171 try:
171 res = dumps(ip.user_ns["w"])
172 res = dumps(ip.user_ns["w"])
172 finally:
173 finally:
173 sys.modules['__main__'] = _main
174 sys.modules['__main__'] = _main
174 self.assertTrue(isinstance(res, bytes))
175 self.assertTrue(isinstance(res, bytes))
175
176
176 def test_global_ns(self):
177 def test_global_ns(self):
177 "Code in functions must be able to access variables outside them."
178 "Code in functions must be able to access variables outside them."
178 ip = get_ipython()
179 ip = get_ipython()
179 ip.run_cell("a = 10")
180 ip.run_cell("a = 10")
180 ip.run_cell(("def f(x):\n"
181 ip.run_cell(("def f(x):\n"
181 " return x + a"))
182 " return x + a"))
182 ip.run_cell("b = f(12)")
183 ip.run_cell("b = f(12)")
183 self.assertEqual(ip.user_ns["b"], 22)
184 self.assertEqual(ip.user_ns["b"], 22)
184
185
185 def test_bad_custom_tb(self):
186 def test_bad_custom_tb(self):
186 """Check that InteractiveShell is protected from bad custom exception handlers"""
187 """Check that InteractiveShell is protected from bad custom exception handlers"""
187 from IPython.utils import io
188 from IPython.utils import io
188 save_stderr = io.stderr
189 save_stderr = io.stderr
189 try:
190 try:
190 # capture stderr
191 # capture stderr
191 io.stderr = StringIO()
192 io.stderr = StringIO()
192 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
193 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
193 self.assertEqual(ip.custom_exceptions, (IOError,))
194 self.assertEqual(ip.custom_exceptions, (IOError,))
194 ip.run_cell(u'raise IOError("foo")')
195 ip.run_cell(u'raise IOError("foo")')
195 self.assertEqual(ip.custom_exceptions, ())
196 self.assertEqual(ip.custom_exceptions, ())
196 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
197 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
197 finally:
198 finally:
198 io.stderr = save_stderr
199 io.stderr = save_stderr
199
200
200 def test_bad_custom_tb_return(self):
201 def test_bad_custom_tb_return(self):
201 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
202 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
202 from IPython.utils import io
203 from IPython.utils import io
203 save_stderr = io.stderr
204 save_stderr = io.stderr
204 try:
205 try:
205 # capture stderr
206 # capture stderr
206 io.stderr = StringIO()
207 io.stderr = StringIO()
207 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
208 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
208 self.assertEqual(ip.custom_exceptions, (NameError,))
209 self.assertEqual(ip.custom_exceptions, (NameError,))
209 ip.run_cell(u'a=abracadabra')
210 ip.run_cell(u'a=abracadabra')
210 self.assertEqual(ip.custom_exceptions, ())
211 self.assertEqual(ip.custom_exceptions, ())
211 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
212 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
212 finally:
213 finally:
213 io.stderr = save_stderr
214 io.stderr = save_stderr
214
215
215 def test_drop_by_id(self):
216 def test_drop_by_id(self):
216 myvars = {"a":object(), "b":object(), "c": object()}
217 myvars = {"a":object(), "b":object(), "c": object()}
217 ip.push(myvars, interactive=False)
218 ip.push(myvars, interactive=False)
218 for name in myvars:
219 for name in myvars:
219 assert name in ip.user_ns, name
220 assert name in ip.user_ns, name
220 assert name in ip.user_ns_hidden, name
221 assert name in ip.user_ns_hidden, name
221 ip.user_ns['b'] = 12
222 ip.user_ns['b'] = 12
222 ip.drop_by_id(myvars)
223 ip.drop_by_id(myvars)
223 for name in ["a", "c"]:
224 for name in ["a", "c"]:
224 assert name not in ip.user_ns, name
225 assert name not in ip.user_ns, name
225 assert name not in ip.user_ns_hidden, name
226 assert name not in ip.user_ns_hidden, name
226 assert ip.user_ns['b'] == 12
227 assert ip.user_ns['b'] == 12
227 ip.reset()
228 ip.reset()
228
229
229 def test_var_expand(self):
230 def test_var_expand(self):
230 ip.user_ns['f'] = u'Ca\xf1o'
231 ip.user_ns['f'] = u'Ca\xf1o'
231 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
232 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
232 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
233 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
233 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
234 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
234 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
235 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
235
236
236 ip.user_ns['f'] = b'Ca\xc3\xb1o'
237 ip.user_ns['f'] = b'Ca\xc3\xb1o'
237 # This should not raise any exception:
238 # This should not raise any exception:
238 ip.var_expand(u'echo $f')
239 ip.var_expand(u'echo $f')
239
240
240 def test_var_expand_local(self):
241 def test_var_expand_local(self):
241 """Test local variable expansion in !system and %magic calls"""
242 """Test local variable expansion in !system and %magic calls"""
242 # !system
243 # !system
243 ip.run_cell('def test():\n'
244 ip.run_cell('def test():\n'
244 ' lvar = "ttt"\n'
245 ' lvar = "ttt"\n'
245 ' ret = !echo {lvar}\n'
246 ' ret = !echo {lvar}\n'
246 ' return ret[0]\n')
247 ' return ret[0]\n')
247 res = ip.user_ns['test']()
248 res = ip.user_ns['test']()
248 nt.assert_in('ttt', res)
249 nt.assert_in('ttt', res)
249
250
250 # %magic
251 # %magic
251 ip.run_cell('def makemacro():\n'
252 ip.run_cell('def makemacro():\n'
252 ' macroname = "macro_var_expand_locals"\n'
253 ' macroname = "macro_var_expand_locals"\n'
253 ' %macro {macroname} codestr\n')
254 ' %macro {macroname} codestr\n')
254 ip.user_ns['codestr'] = "str(12)"
255 ip.user_ns['codestr'] = "str(12)"
255 ip.run_cell('makemacro()')
256 ip.run_cell('makemacro()')
256 nt.assert_in('macro_var_expand_locals', ip.user_ns)
257 nt.assert_in('macro_var_expand_locals', ip.user_ns)
257
258
258 def test_var_expand_self(self):
259 def test_var_expand_self(self):
259 """Test variable expansion with the name 'self', which was failing.
260 """Test variable expansion with the name 'self', which was failing.
260
261
261 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
262 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
262 """
263 """
263 ip.run_cell('class cTest:\n'
264 ip.run_cell('class cTest:\n'
264 ' classvar="see me"\n'
265 ' classvar="see me"\n'
265 ' def test(self):\n'
266 ' def test(self):\n'
266 ' res = !echo Variable: {self.classvar}\n'
267 ' res = !echo Variable: {self.classvar}\n'
267 ' return res[0]\n')
268 ' return res[0]\n')
268 nt.assert_in('see me', ip.user_ns['cTest']().test())
269 nt.assert_in('see me', ip.user_ns['cTest']().test())
269
270
270 def test_bad_var_expand(self):
271 def test_bad_var_expand(self):
271 """var_expand on invalid formats shouldn't raise"""
272 """var_expand on invalid formats shouldn't raise"""
272 # SyntaxError
273 # SyntaxError
273 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
274 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
274 # NameError
275 # NameError
275 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
276 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
276 # ZeroDivisionError
277 # ZeroDivisionError
277 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
278 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
278
279
279 def test_silent_nopostexec(self):
280 def test_silent_nopostexec(self):
280 """run_cell(silent=True) doesn't invoke post-exec funcs"""
281 """run_cell(silent=True) doesn't invoke post-exec funcs"""
281 d = dict(called=False)
282 d = dict(called=False)
282 def set_called():
283 def set_called():
283 d['called'] = True
284 d['called'] = True
284
285
285 ip.register_post_execute(set_called)
286 ip.register_post_execute(set_called)
286 ip.run_cell("1", silent=True)
287 ip.run_cell("1", silent=True)
287 self.assertFalse(d['called'])
288 self.assertFalse(d['called'])
288 # double-check that non-silent exec did what we expected
289 # double-check that non-silent exec did what we expected
289 # silent to avoid
290 # silent to avoid
290 ip.run_cell("1")
291 ip.run_cell("1")
291 self.assertTrue(d['called'])
292 self.assertTrue(d['called'])
292 # remove post-exec
293 # remove post-exec
293 ip._post_execute.pop(set_called)
294 ip._post_execute.pop(set_called)
294
295
295 def test_silent_noadvance(self):
296 def test_silent_noadvance(self):
296 """run_cell(silent=True) doesn't advance execution_count"""
297 """run_cell(silent=True) doesn't advance execution_count"""
297 ec = ip.execution_count
298 ec = ip.execution_count
298 # silent should force store_history=False
299 # silent should force store_history=False
299 ip.run_cell("1", store_history=True, silent=True)
300 ip.run_cell("1", store_history=True, silent=True)
300
301
301 self.assertEqual(ec, ip.execution_count)
302 self.assertEqual(ec, ip.execution_count)
302 # double-check that non-silent exec did what we expected
303 # double-check that non-silent exec did what we expected
303 # silent to avoid
304 # silent to avoid
304 ip.run_cell("1", store_history=True)
305 ip.run_cell("1", store_history=True)
305 self.assertEqual(ec+1, ip.execution_count)
306 self.assertEqual(ec+1, ip.execution_count)
306
307
307 def test_silent_nodisplayhook(self):
308 def test_silent_nodisplayhook(self):
308 """run_cell(silent=True) doesn't trigger displayhook"""
309 """run_cell(silent=True) doesn't trigger displayhook"""
309 d = dict(called=False)
310 d = dict(called=False)
310
311
311 trap = ip.display_trap
312 trap = ip.display_trap
312 save_hook = trap.hook
313 save_hook = trap.hook
313
314
314 def failing_hook(*args, **kwargs):
315 def failing_hook(*args, **kwargs):
315 d['called'] = True
316 d['called'] = True
316
317
317 try:
318 try:
318 trap.hook = failing_hook
319 trap.hook = failing_hook
319 ip.run_cell("1", silent=True)
320 ip.run_cell("1", silent=True)
320 self.assertFalse(d['called'])
321 self.assertFalse(d['called'])
321 # double-check that non-silent exec did what we expected
322 # double-check that non-silent exec did what we expected
322 # silent to avoid
323 # silent to avoid
323 ip.run_cell("1")
324 ip.run_cell("1")
324 self.assertTrue(d['called'])
325 self.assertTrue(d['called'])
325 finally:
326 finally:
326 trap.hook = save_hook
327 trap.hook = save_hook
327
328
328 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
329 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
329 def test_print_softspace(self):
330 def test_print_softspace(self):
330 """Verify that softspace is handled correctly when executing multiple
331 """Verify that softspace is handled correctly when executing multiple
331 statements.
332 statements.
332
333
333 In [1]: print 1; print 2
334 In [1]: print 1; print 2
334 1
335 1
335 2
336 2
336
337
337 In [2]: print 1,; print 2
338 In [2]: print 1,; print 2
338 1 2
339 1 2
339 """
340 """
340
341
341 def test_ofind_line_magic(self):
342 def test_ofind_line_magic(self):
342 from IPython.core.magic import register_line_magic
343 from IPython.core.magic import register_line_magic
343
344
344 @register_line_magic
345 @register_line_magic
345 def lmagic(line):
346 def lmagic(line):
346 "A line magic"
347 "A line magic"
347
348
348 # Get info on line magic
349 # Get info on line magic
349 lfind = ip._ofind('lmagic')
350 lfind = ip._ofind('lmagic')
350 info = dict(found=True, isalias=False, ismagic=True,
351 info = dict(found=True, isalias=False, ismagic=True,
351 namespace = 'IPython internal', obj= lmagic.__wrapped__,
352 namespace = 'IPython internal', obj= lmagic.__wrapped__,
352 parent = None)
353 parent = None)
353 nt.assert_equal(lfind, info)
354 nt.assert_equal(lfind, info)
354
355
355 def test_ofind_cell_magic(self):
356 def test_ofind_cell_magic(self):
356 from IPython.core.magic import register_cell_magic
357 from IPython.core.magic import register_cell_magic
357
358
358 @register_cell_magic
359 @register_cell_magic
359 def cmagic(line, cell):
360 def cmagic(line, cell):
360 "A cell magic"
361 "A cell magic"
361
362
362 # Get info on cell magic
363 # Get info on cell magic
363 find = ip._ofind('cmagic')
364 find = ip._ofind('cmagic')
364 info = dict(found=True, isalias=False, ismagic=True,
365 info = dict(found=True, isalias=False, ismagic=True,
365 namespace = 'IPython internal', obj= cmagic.__wrapped__,
366 namespace = 'IPython internal', obj= cmagic.__wrapped__,
366 parent = None)
367 parent = None)
367 nt.assert_equal(find, info)
368 nt.assert_equal(find, info)
368
369
369 def test_custom_exception(self):
370 def test_custom_exception(self):
370 called = []
371 called = []
371 def my_handler(shell, etype, value, tb, tb_offset=None):
372 def my_handler(shell, etype, value, tb, tb_offset=None):
372 called.append(etype)
373 called.append(etype)
373 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
374 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
374
375
375 ip.set_custom_exc((ValueError,), my_handler)
376 ip.set_custom_exc((ValueError,), my_handler)
376 try:
377 try:
377 ip.run_cell("raise ValueError('test')")
378 ip.run_cell("raise ValueError('test')")
378 # Check that this was called, and only once.
379 # Check that this was called, and only once.
379 self.assertEqual(called, [ValueError])
380 self.assertEqual(called, [ValueError])
380 finally:
381 finally:
381 # Reset the custom exception hook
382 # Reset the custom exception hook
382 ip.set_custom_exc((), None)
383 ip.set_custom_exc((), None)
383
384
384 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
385 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
385 def test_future_environment(self):
386 def test_future_environment(self):
386 "Can we run code with & without the shell's __future__ imports?"
387 "Can we run code with & without the shell's __future__ imports?"
387 ip.run_cell("from __future__ import division")
388 ip.run_cell("from __future__ import division")
388 ip.run_cell("a = 1/2", shell_futures=True)
389 ip.run_cell("a = 1/2", shell_futures=True)
389 self.assertEqual(ip.user_ns['a'], 0.5)
390 self.assertEqual(ip.user_ns['a'], 0.5)
390 ip.run_cell("b = 1/2", shell_futures=False)
391 ip.run_cell("b = 1/2", shell_futures=False)
391 self.assertEqual(ip.user_ns['b'], 0)
392 self.assertEqual(ip.user_ns['b'], 0)
392
393
393 ip.compile.reset_compiler_flags()
394 ip.compile.reset_compiler_flags()
394 # This shouldn't leak to the shell's compiler
395 # This shouldn't leak to the shell's compiler
395 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
396 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
396 self.assertEqual(ip.user_ns['c'], 0.5)
397 self.assertEqual(ip.user_ns['c'], 0.5)
397 ip.run_cell("d = 1/2", shell_futures=True)
398 ip.run_cell("d = 1/2", shell_futures=True)
398 self.assertEqual(ip.user_ns['d'], 0)
399 self.assertEqual(ip.user_ns['d'], 0)
399
400
400
401
401 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
402 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
402
403
403 @onlyif_unicode_paths
404 @onlyif_unicode_paths
404 def setUp(self):
405 def setUp(self):
405 self.BASETESTDIR = tempfile.mkdtemp()
406 self.BASETESTDIR = tempfile.mkdtemp()
406 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
407 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
407 os.mkdir(self.TESTDIR)
408 os.mkdir(self.TESTDIR)
408 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
409 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
409 sfile.write("pass\n")
410 sfile.write("pass\n")
410 self.oldpath = py3compat.getcwd()
411 self.oldpath = py3compat.getcwd()
411 os.chdir(self.TESTDIR)
412 os.chdir(self.TESTDIR)
412 self.fname = u"Γ₯Àâtestscript.py"
413 self.fname = u"Γ₯Àâtestscript.py"
413
414
414 def tearDown(self):
415 def tearDown(self):
415 os.chdir(self.oldpath)
416 os.chdir(self.oldpath)
416 shutil.rmtree(self.BASETESTDIR)
417 shutil.rmtree(self.BASETESTDIR)
417
418
418 @onlyif_unicode_paths
419 @onlyif_unicode_paths
419 def test_1(self):
420 def test_1(self):
420 """Test safe_execfile with non-ascii path
421 """Test safe_execfile with non-ascii path
421 """
422 """
422 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
423 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
423
424
424 class ExitCodeChecks(tt.TempFileMixin):
425 class ExitCodeChecks(tt.TempFileMixin):
425 def test_exit_code_ok(self):
426 def test_exit_code_ok(self):
426 self.system('exit 0')
427 self.system('exit 0')
427 self.assertEqual(ip.user_ns['_exit_code'], 0)
428 self.assertEqual(ip.user_ns['_exit_code'], 0)
428
429
429 def test_exit_code_error(self):
430 def test_exit_code_error(self):
430 self.system('exit 1')
431 self.system('exit 1')
431 self.assertEqual(ip.user_ns['_exit_code'], 1)
432 self.assertEqual(ip.user_ns['_exit_code'], 1)
432
433
433 @skipif(not hasattr(signal, 'SIGALRM'))
434 @skipif(not hasattr(signal, 'SIGALRM'))
434 def test_exit_code_signal(self):
435 def test_exit_code_signal(self):
435 self.mktmp("import signal, time\n"
436 self.mktmp("import signal, time\n"
436 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
437 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
437 "time.sleep(1)\n")
438 "time.sleep(1)\n")
438 self.system("%s %s" % (sys.executable, self.fname))
439 self.system("%s %s" % (sys.executable, self.fname))
439 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
440 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
440
441
441 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
442 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
442 system = ip.system_raw
443 system = ip.system_raw
443
444
444 @onlyif_unicode_paths
445 @onlyif_unicode_paths
445 def test_1(self):
446 def test_1(self):
446 """Test system_raw with non-ascii cmd
447 """Test system_raw with non-ascii cmd
447 """
448 """
448 cmd = u'''python -c "'Γ₯Àâ'" '''
449 cmd = u'''python -c "'Γ₯Àâ'" '''
449 ip.system_raw(cmd)
450 ip.system_raw(cmd)
450
451
451 # TODO: Exit codes are currently ignored on Windows.
452 # TODO: Exit codes are currently ignored on Windows.
452 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
453 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
453 system = ip.system_piped
454 system = ip.system_piped
454
455
455 @skip_win32
456 @skip_win32
456 def test_exit_code_ok(self):
457 def test_exit_code_ok(self):
457 ExitCodeChecks.test_exit_code_ok(self)
458 ExitCodeChecks.test_exit_code_ok(self)
458
459
459 @skip_win32
460 @skip_win32
460 def test_exit_code_error(self):
461 def test_exit_code_error(self):
461 ExitCodeChecks.test_exit_code_error(self)
462 ExitCodeChecks.test_exit_code_error(self)
462
463
463 @skip_win32
464 @skip_win32
464 def test_exit_code_signal(self):
465 def test_exit_code_signal(self):
465 ExitCodeChecks.test_exit_code_signal(self)
466 ExitCodeChecks.test_exit_code_signal(self)
466
467
467 class TestModules(unittest.TestCase, tt.TempFileMixin):
468 class TestModules(unittest.TestCase, tt.TempFileMixin):
468 def test_extraneous_loads(self):
469 def test_extraneous_loads(self):
469 """Test we're not loading modules on startup that we shouldn't.
470 """Test we're not loading modules on startup that we shouldn't.
470 """
471 """
471 self.mktmp("import sys\n"
472 self.mktmp("import sys\n"
472 "print('numpy' in sys.modules)\n"
473 "print('numpy' in sys.modules)\n"
473 "print('IPython.parallel' in sys.modules)\n"
474 "print('IPython.parallel' in sys.modules)\n"
474 "print('IPython.kernel.zmq' in sys.modules)\n"
475 "print('IPython.kernel.zmq' in sys.modules)\n"
475 )
476 )
476 out = "False\nFalse\nFalse\n"
477 out = "False\nFalse\nFalse\n"
477 tt.ipexec_validate(self.fname, out)
478 tt.ipexec_validate(self.fname, out)
478
479
479 class Negator(ast.NodeTransformer):
480 class Negator(ast.NodeTransformer):
480 """Negates all number literals in an AST."""
481 """Negates all number literals in an AST."""
481 def visit_Num(self, node):
482 def visit_Num(self, node):
482 node.n = -node.n
483 node.n = -node.n
483 return node
484 return node
484
485
485 class TestAstTransform(unittest.TestCase):
486 class TestAstTransform(unittest.TestCase):
486 def setUp(self):
487 def setUp(self):
487 self.negator = Negator()
488 self.negator = Negator()
488 ip.ast_transformers.append(self.negator)
489 ip.ast_transformers.append(self.negator)
489
490
490 def tearDown(self):
491 def tearDown(self):
491 ip.ast_transformers.remove(self.negator)
492 ip.ast_transformers.remove(self.negator)
492
493
493 def test_run_cell(self):
494 def test_run_cell(self):
494 with tt.AssertPrints('-34'):
495 with tt.AssertPrints('-34'):
495 ip.run_cell('print (12 + 22)')
496 ip.run_cell('print (12 + 22)')
496
497
497 # A named reference to a number shouldn't be transformed.
498 # A named reference to a number shouldn't be transformed.
498 ip.user_ns['n'] = 55
499 ip.user_ns['n'] = 55
499 with tt.AssertNotPrints('-55'):
500 with tt.AssertNotPrints('-55'):
500 ip.run_cell('print (n)')
501 ip.run_cell('print (n)')
501
502
502 def test_timeit(self):
503 def test_timeit(self):
503 called = set()
504 called = set()
504 def f(x):
505 def f(x):
505 called.add(x)
506 called.add(x)
506 ip.push({'f':f})
507 ip.push({'f':f})
507
508
508 with tt.AssertPrints("best of "):
509 with tt.AssertPrints("best of "):
509 ip.run_line_magic("timeit", "-n1 f(1)")
510 ip.run_line_magic("timeit", "-n1 f(1)")
510 self.assertEqual(called, set([-1]))
511 self.assertEqual(called, set([-1]))
511 called.clear()
512 called.clear()
512
513
513 with tt.AssertPrints("best of "):
514 with tt.AssertPrints("best of "):
514 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
515 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
515 self.assertEqual(called, set([-2, -3]))
516 self.assertEqual(called, set([-2, -3]))
516
517
517 def test_time(self):
518 def test_time(self):
518 called = []
519 called = []
519 def f(x):
520 def f(x):
520 called.append(x)
521 called.append(x)
521 ip.push({'f':f})
522 ip.push({'f':f})
522
523
523 # Test with an expression
524 # Test with an expression
524 with tt.AssertPrints("Wall time: "):
525 with tt.AssertPrints("Wall time: "):
525 ip.run_line_magic("time", "f(5+9)")
526 ip.run_line_magic("time", "f(5+9)")
526 self.assertEqual(called, [-14])
527 self.assertEqual(called, [-14])
527 called[:] = []
528 called[:] = []
528
529
529 # Test with a statement (different code path)
530 # Test with a statement (different code path)
530 with tt.AssertPrints("Wall time: "):
531 with tt.AssertPrints("Wall time: "):
531 ip.run_line_magic("time", "a = f(-3 + -2)")
532 ip.run_line_magic("time", "a = f(-3 + -2)")
532 self.assertEqual(called, [5])
533 self.assertEqual(called, [5])
533
534
534 def test_macro(self):
535 def test_macro(self):
535 ip.push({'a':10})
536 ip.push({'a':10})
536 # The AST transformation makes this do a+=-1
537 # The AST transformation makes this do a+=-1
537 ip.define_macro("amacro", "a+=1\nprint(a)")
538 ip.define_macro("amacro", "a+=1\nprint(a)")
538
539
539 with tt.AssertPrints("9"):
540 with tt.AssertPrints("9"):
540 ip.run_cell("amacro")
541 ip.run_cell("amacro")
541 with tt.AssertPrints("8"):
542 with tt.AssertPrints("8"):
542 ip.run_cell("amacro")
543 ip.run_cell("amacro")
543
544
544 class IntegerWrapper(ast.NodeTransformer):
545 class IntegerWrapper(ast.NodeTransformer):
545 """Wraps all integers in a call to Integer()"""
546 """Wraps all integers in a call to Integer()"""
546 def visit_Num(self, node):
547 def visit_Num(self, node):
547 if isinstance(node.n, int):
548 if isinstance(node.n, int):
548 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
549 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
549 args=[node], keywords=[])
550 args=[node], keywords=[])
550 return node
551 return node
551
552
552 class TestAstTransform2(unittest.TestCase):
553 class TestAstTransform2(unittest.TestCase):
553 def setUp(self):
554 def setUp(self):
554 self.intwrapper = IntegerWrapper()
555 self.intwrapper = IntegerWrapper()
555 ip.ast_transformers.append(self.intwrapper)
556 ip.ast_transformers.append(self.intwrapper)
556
557
557 self.calls = []
558 self.calls = []
558 def Integer(*args):
559 def Integer(*args):
559 self.calls.append(args)
560 self.calls.append(args)
560 return args
561 return args
561 ip.push({"Integer": Integer})
562 ip.push({"Integer": Integer})
562
563
563 def tearDown(self):
564 def tearDown(self):
564 ip.ast_transformers.remove(self.intwrapper)
565 ip.ast_transformers.remove(self.intwrapper)
565 del ip.user_ns['Integer']
566 del ip.user_ns['Integer']
566
567
567 def test_run_cell(self):
568 def test_run_cell(self):
568 ip.run_cell("n = 2")
569 ip.run_cell("n = 2")
569 self.assertEqual(self.calls, [(2,)])
570 self.assertEqual(self.calls, [(2,)])
570
571
571 # This shouldn't throw an error
572 # This shouldn't throw an error
572 ip.run_cell("o = 2.0")
573 ip.run_cell("o = 2.0")
573 self.assertEqual(ip.user_ns['o'], 2.0)
574 self.assertEqual(ip.user_ns['o'], 2.0)
574
575
575 def test_timeit(self):
576 def test_timeit(self):
576 called = set()
577 called = set()
577 def f(x):
578 def f(x):
578 called.add(x)
579 called.add(x)
579 ip.push({'f':f})
580 ip.push({'f':f})
580
581
581 with tt.AssertPrints("best of "):
582 with tt.AssertPrints("best of "):
582 ip.run_line_magic("timeit", "-n1 f(1)")
583 ip.run_line_magic("timeit", "-n1 f(1)")
583 self.assertEqual(called, set([(1,)]))
584 self.assertEqual(called, set([(1,)]))
584 called.clear()
585 called.clear()
585
586
586 with tt.AssertPrints("best of "):
587 with tt.AssertPrints("best of "):
587 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
588 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
588 self.assertEqual(called, set([(2,), (3,)]))
589 self.assertEqual(called, set([(2,), (3,)]))
589
590
590 class ErrorTransformer(ast.NodeTransformer):
591 class ErrorTransformer(ast.NodeTransformer):
591 """Throws an error when it sees a number."""
592 """Throws an error when it sees a number."""
592 def visit_Num(self):
593 def visit_Num(self):
593 raise ValueError("test")
594 raise ValueError("test")
594
595
595 class TestAstTransformError(unittest.TestCase):
596 class TestAstTransformError(unittest.TestCase):
596 def test_unregistering(self):
597 def test_unregistering(self):
597 err_transformer = ErrorTransformer()
598 err_transformer = ErrorTransformer()
598 ip.ast_transformers.append(err_transformer)
599 ip.ast_transformers.append(err_transformer)
599
600
600 with tt.AssertPrints("unregister", channel='stderr'):
601 with tt.AssertPrints("unregister", channel='stderr'):
601 ip.run_cell("1 + 2")
602 ip.run_cell("1 + 2")
602
603
603 # This should have been removed.
604 # This should have been removed.
604 nt.assert_not_in(err_transformer, ip.ast_transformers)
605 nt.assert_not_in(err_transformer, ip.ast_transformers)
605
606
606 def test__IPYTHON__():
607 def test__IPYTHON__():
607 # This shouldn't raise a NameError, that's all
608 # This shouldn't raise a NameError, that's all
608 __IPYTHON__
609 __IPYTHON__
609
610
610
611
611 class DummyRepr(object):
612 class DummyRepr(object):
612 def __repr__(self):
613 def __repr__(self):
613 return "DummyRepr"
614 return "DummyRepr"
614
615
615 def _repr_html_(self):
616 def _repr_html_(self):
616 return "<b>dummy</b>"
617 return "<b>dummy</b>"
617
618
618 def _repr_javascript_(self):
619 def _repr_javascript_(self):
619 return "console.log('hi');", {'key': 'value'}
620 return "console.log('hi');", {'key': 'value'}
620
621
621
622
622 def test_user_variables():
623 def test_user_variables():
623 # enable all formatters
624 # enable all formatters
624 ip.display_formatter.active_types = ip.display_formatter.format_types
625 ip.display_formatter.active_types = ip.display_formatter.format_types
625
626
626 ip.user_ns['dummy'] = d = DummyRepr()
627 ip.user_ns['dummy'] = d = DummyRepr()
627 keys = set(['dummy', 'doesnotexist'])
628 keys = set(['dummy', 'doesnotexist'])
628 r = ip.user_variables(keys)
629 r = ip.user_variables(keys)
629
630
630 nt.assert_equal(keys, set(r.keys()))
631 nt.assert_equal(keys, set(r.keys()))
631 dummy = r['dummy']
632 dummy = r['dummy']
632 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
633 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
633 nt.assert_equal(dummy['status'], 'ok')
634 nt.assert_equal(dummy['status'], 'ok')
634 data = dummy['data']
635 data = dummy['data']
635 metadata = dummy['metadata']
636 metadata = dummy['metadata']
636 nt.assert_equal(data.get('text/html'), d._repr_html_())
637 nt.assert_equal(data.get('text/html'), d._repr_html_())
637 js, jsmd = d._repr_javascript_()
638 js, jsmd = d._repr_javascript_()
638 nt.assert_equal(data.get('application/javascript'), js)
639 nt.assert_equal(data.get('application/javascript'), js)
639 nt.assert_equal(metadata.get('application/javascript'), jsmd)
640 nt.assert_equal(metadata.get('application/javascript'), jsmd)
640
641
641 dne = r['doesnotexist']
642 dne = r['doesnotexist']
642 nt.assert_equal(dne['status'], 'error')
643 nt.assert_equal(dne['status'], 'error')
643 nt.assert_equal(dne['ename'], 'KeyError')
644 nt.assert_equal(dne['ename'], 'KeyError')
644
645
645 # back to text only
646 # back to text only
646 ip.display_formatter.active_types = ['text/plain']
647 ip.display_formatter.active_types = ['text/plain']
647
648
648 def test_user_expression():
649 def test_user_expression():
649 # enable all formatters
650 # enable all formatters
650 ip.display_formatter.active_types = ip.display_formatter.format_types
651 ip.display_formatter.active_types = ip.display_formatter.format_types
651 query = {
652 query = {
652 'a' : '1 + 2',
653 'a' : '1 + 2',
653 'b' : '1/0',
654 'b' : '1/0',
654 }
655 }
655 r = ip.user_expressions(query)
656 r = ip.user_expressions(query)
656 import pprint
657 import pprint
657 pprint.pprint(r)
658 pprint.pprint(r)
658 nt.assert_equal(r.keys(), query.keys())
659 nt.assert_equal(r.keys(), query.keys())
659 a = r['a']
660 a = r['a']
660 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
661 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
661 nt.assert_equal(a['status'], 'ok')
662 nt.assert_equal(a['status'], 'ok')
662 data = a['data']
663 data = a['data']
663 metadata = a['metadata']
664 metadata = a['metadata']
664 nt.assert_equal(data.get('text/plain'), '3')
665 nt.assert_equal(data.get('text/plain'), '3')
665
666
666 b = r['b']
667 b = r['b']
667 nt.assert_equal(b['status'], 'error')
668 nt.assert_equal(b['status'], 'error')
668 nt.assert_equal(b['ename'], 'ZeroDivisionError')
669 nt.assert_equal(b['ename'], 'ZeroDivisionError')
669
670
670 # back to text only
671 # back to text only
671 ip.display_formatter.active_types = ['text/plain']
672 ip.display_formatter.active_types = ['text/plain']
672
673
673
674
674
675
675
676
676
677
678 class TestSyntaxErrorTransformer(unittest.TestCase):
679 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
680
681 class SyntaxErrorTransformer(InputTransformer):
682
683 def push(self, line):
684 pos = line.find('syntaxerror')
685 if pos >= 0:
686 e = SyntaxError('input contains "syntaxerror"')
687 e.text = line
688 e.offset = pos + 1
689 raise e
690 return line
691
692 def reset(self):
693 pass
694
695 def setUp(self):
696 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
697 ip.input_splitter.python_line_transforms.append(self.transformer)
698 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
699
700 def tearDown(self):
701 ip.input_splitter.python_line_transforms.remove(self.transformer)
702 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
703
704 def test_syntaxerror_input_transformer(self):
705 with tt.AssertPrints('1234'):
706 ip.run_cell('1234')
707 with tt.AssertPrints('SyntaxError: invalid syntax'):
708 ip.run_cell('1 2 3') # plain python syntax error
709 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
710 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
711 with tt.AssertPrints('3456'):
712 ip.run_cell('3456')
713
714
677
715
General Comments 0
You need to be logged in to leave comments. Login now