##// END OF EJS Templates
Add support for "finally" event callbacks....
Fabio Niephaus -
Show More

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

@@ -0,0 +1,6 b''
1 Two new event callbacks have been added: ``finally_execute`` and ``finally_run_cell``.
2 They work similar to the corresponding *post* callbacks, but are guaranteed to be triggered (even when, for example, a ``SyntaxError`` was raised).
3 Also, the execution result is provided as an argument for further inspection.
4
5 * `GitHub issue <https://github.com/ipython/ipython/issues/10774>`__
6 * `Updated docs <http://ipython.readthedocs.io/en/stable/config/callbacks.html?highlight=finally>`__
@@ -1,130 +1,153 b''
1 1 """Infrastructure for registering and firing callbacks on application events.
2 2
3 3 Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
4 4 be called at specific times, or a collection of alternative methods to try,
5 5 callbacks are designed to be used by extension authors. A number of callbacks
6 6 can be registered for the same event without needing to be aware of one another.
7 7
8 8 The functions defined in this module are no-ops indicating the names of available
9 9 events and the arguments which will be passed to them.
10 10
11 11 .. note::
12 12
13 13 This API is experimental in IPython 2.0, and may be revised in future versions.
14 14 """
15 15
16 16 class EventManager(object):
17 17 """Manage a collection of events and a sequence of callbacks for each.
18 18
19 19 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
20 20 instances as an ``events`` attribute.
21 21
22 22 .. note::
23 23
24 24 This API is experimental in IPython 2.0, and may be revised in future versions.
25 25 """
26 26 def __init__(self, shell, available_events):
27 27 """Initialise the :class:`CallbackManager`.
28 28
29 29 Parameters
30 30 ----------
31 31 shell
32 32 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
33 33 available_callbacks
34 34 An iterable of names for callback events.
35 35 """
36 36 self.shell = shell
37 37 self.callbacks = {n:[] for n in available_events}
38 38
39 39 def register(self, event, function):
40 40 """Register a new event callback
41 41
42 42 Parameters
43 43 ----------
44 44 event : str
45 45 The event for which to register this callback.
46 46 function : callable
47 47 A function to be called on the given event. It should take the same
48 48 parameters as the appropriate callback prototype.
49 49
50 50 Raises
51 51 ------
52 52 TypeError
53 53 If ``function`` is not callable.
54 54 KeyError
55 55 If ``event`` is not one of the known events.
56 56 """
57 57 if not callable(function):
58 58 raise TypeError('Need a callable, got %r' % function)
59 59 self.callbacks[event].append(function)
60 60
61 61 def unregister(self, event, function):
62 62 """Remove a callback from the given event."""
63 63 self.callbacks[event].remove(function)
64 64
65 65 def trigger(self, event, *args, **kwargs):
66 66 """Call callbacks for ``event``.
67 67
68 68 Any additional arguments are passed to all callbacks registered for this
69 69 event. Exceptions raised by callbacks are caught, and a message printed.
70 70 """
71 71 for func in self.callbacks[event][:]:
72 72 try:
73 73 func(*args, **kwargs)
74 74 except Exception:
75 75 print("Error in callback {} (for {}):".format(func, event))
76 76 self.shell.showtraceback()
77 77
78 78 # event_name -> prototype mapping
79 79 available_events = {}
80 80
81 81 def _define_event(callback_proto):
82 82 available_events[callback_proto.__name__] = callback_proto
83 83 return callback_proto
84 84
85 85 # ------------------------------------------------------------------------------
86 86 # Callback prototypes
87 87 #
88 88 # No-op functions which describe the names of available events and the
89 89 # signatures of callbacks for those events.
90 90 # ------------------------------------------------------------------------------
91 91
92 92 @_define_event
93 93 def pre_execute():
94 94 """Fires before code is executed in response to user/frontend action.
95 95
96 96 This includes comm and widget messages and silent execution, as well as user
97 97 code cells."""
98 98 pass
99 99
100 100 @_define_event
101 101 def pre_run_cell():
102 102 """Fires before user-entered code runs."""
103 103 pass
104 104
105 105 @_define_event
106 106 def post_execute():
107 107 """Fires after code is executed in response to user/frontend action.
108 108
109 109 This includes comm and widget messages and silent execution, as well as user
110 110 code cells."""
111 111 pass
112 112
113 113 @_define_event
114 114 def post_run_cell():
115 115 """Fires after user-entered code runs."""
116 116 pass
117 117
118 118 @_define_event
119 def finally_execute(result):
120 """Always fires after code is executed in response to user/frontend action.
121
122 This includes comm and widget messages and silent execution, as well as user
123 code cells.
124
125 Parameters
126 ----------
127 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
128 """
129 pass
130
131 @_define_event
132 def finally_run_cell(result):
133 """Always fires after user-entered code runs.
134
135 Parameters
136 ----------
137 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
138 """
139 pass
140
141 @_define_event
119 142 def shell_initialized(ip):
120 143 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
121 144
122 145 This is before extensions and startup scripts are loaded, so it can only be
123 146 set by subclassing.
124 147
125 148 Parameters
126 149 ----------
127 150 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
128 151 The newly initialised shell.
129 152 """
130 153 pass
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,912 +1,932 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the key interactiveshell module.
3 3
4 4 Historically the main classes in interactiveshell have been under-tested. This
5 5 module should grow as many single-method tests as possible to trap many of the
6 6 recurring bugs we seem to encounter with high-level interaction.
7 7 """
8 8
9 9 # Copyright (c) IPython Development Team.
10 10 # Distributed under the terms of the Modified BSD License.
11 11
12 12 import ast
13 13 import os
14 14 import signal
15 15 import shutil
16 16 import sys
17 17 import tempfile
18 18 import unittest
19 19 from unittest import mock
20 20 from io import StringIO
21 21
22 22 from os.path import join
23 23
24 24 import nose.tools as nt
25 25
26 26 from IPython.core.error import InputRejected
27 27 from IPython.core.inputtransformer import InputTransformer
28 28 from IPython.testing.decorators import (
29 29 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
30 30 )
31 31 from IPython.testing import tools as tt
32 32 from IPython.utils.process import find_cmd
33 33 from IPython.utils import py3compat
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Globals
37 37 #-----------------------------------------------------------------------------
38 38 # This is used by every single test, no point repeating it ad nauseam
39 39 ip = get_ipython()
40 40
41 41 #-----------------------------------------------------------------------------
42 42 # Tests
43 43 #-----------------------------------------------------------------------------
44 44
45 45 class DerivedInterrupt(KeyboardInterrupt):
46 46 pass
47 47
48 48 class InteractiveShellTestCase(unittest.TestCase):
49 49 def test_naked_string_cells(self):
50 50 """Test that cells with only naked strings are fully executed"""
51 51 # First, single-line inputs
52 52 ip.run_cell('"a"\n')
53 53 self.assertEqual(ip.user_ns['_'], 'a')
54 54 # And also multi-line cells
55 55 ip.run_cell('"""a\nb"""\n')
56 56 self.assertEqual(ip.user_ns['_'], 'a\nb')
57 57
58 58 def test_run_empty_cell(self):
59 59 """Just make sure we don't get a horrible error with a blank
60 60 cell of input. Yes, I did overlook that."""
61 61 old_xc = ip.execution_count
62 62 res = ip.run_cell('')
63 63 self.assertEqual(ip.execution_count, old_xc)
64 64 self.assertEqual(res.execution_count, None)
65 65
66 66 def test_run_cell_multiline(self):
67 67 """Multi-block, multi-line cells must execute correctly.
68 68 """
69 69 src = '\n'.join(["x=1",
70 70 "y=2",
71 71 "if 1:",
72 72 " x += 1",
73 73 " y += 1",])
74 74 res = ip.run_cell(src)
75 75 self.assertEqual(ip.user_ns['x'], 2)
76 76 self.assertEqual(ip.user_ns['y'], 3)
77 77 self.assertEqual(res.success, True)
78 78 self.assertEqual(res.result, None)
79 79
80 80 def test_multiline_string_cells(self):
81 81 "Code sprinkled with multiline strings should execute (GH-306)"
82 82 ip.run_cell('tmp=0')
83 83 self.assertEqual(ip.user_ns['tmp'], 0)
84 84 res = ip.run_cell('tmp=1;"""a\nb"""\n')
85 85 self.assertEqual(ip.user_ns['tmp'], 1)
86 86 self.assertEqual(res.success, True)
87 87 self.assertEqual(res.result, "a\nb")
88 88
89 89 def test_dont_cache_with_semicolon(self):
90 90 "Ending a line with semicolon should not cache the returned object (GH-307)"
91 91 oldlen = len(ip.user_ns['Out'])
92 92 for cell in ['1;', '1;1;']:
93 93 res = ip.run_cell(cell, store_history=True)
94 94 newlen = len(ip.user_ns['Out'])
95 95 self.assertEqual(oldlen, newlen)
96 96 self.assertIsNone(res.result)
97 97 i = 0
98 98 #also test the default caching behavior
99 99 for cell in ['1', '1;1']:
100 100 ip.run_cell(cell, store_history=True)
101 101 newlen = len(ip.user_ns['Out'])
102 102 i += 1
103 103 self.assertEqual(oldlen+i, newlen)
104 104
105 105 def test_syntax_error(self):
106 106 res = ip.run_cell("raise = 3")
107 107 self.assertIsInstance(res.error_before_exec, SyntaxError)
108 108
109 109 def test_In_variable(self):
110 110 "Verify that In variable grows with user input (GH-284)"
111 111 oldlen = len(ip.user_ns['In'])
112 112 ip.run_cell('1;', store_history=True)
113 113 newlen = len(ip.user_ns['In'])
114 114 self.assertEqual(oldlen+1, newlen)
115 115 self.assertEqual(ip.user_ns['In'][-1],'1;')
116 116
117 117 def test_magic_names_in_string(self):
118 118 ip.run_cell('a = """\n%exit\n"""')
119 119 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
120 120
121 121 def test_trailing_newline(self):
122 122 """test that running !(command) does not raise a SyntaxError"""
123 123 ip.run_cell('!(true)\n', False)
124 124 ip.run_cell('!(true)\n\n\n', False)
125 125
126 126 def test_gh_597(self):
127 127 """Pretty-printing lists of objects with non-ascii reprs may cause
128 128 problems."""
129 129 class Spam(object):
130 130 def __repr__(self):
131 131 return "\xe9"*50
132 132 import IPython.core.formatters
133 133 f = IPython.core.formatters.PlainTextFormatter()
134 134 f([Spam(),Spam()])
135 135
136 136
137 137 def test_future_flags(self):
138 138 """Check that future flags are used for parsing code (gh-777)"""
139 139 ip.run_cell('from __future__ import barry_as_FLUFL')
140 140 try:
141 141 ip.run_cell('prfunc_return_val = 1 <> 2')
142 142 assert 'prfunc_return_val' in ip.user_ns
143 143 finally:
144 144 # Reset compiler flags so we don't mess up other tests.
145 145 ip.compile.reset_compiler_flags()
146 146
147 147 def test_can_pickle(self):
148 148 "Can we pickle objects defined interactively (GH-29)"
149 149 ip = get_ipython()
150 150 ip.reset()
151 151 ip.run_cell(("class Mylist(list):\n"
152 152 " def __init__(self,x=[]):\n"
153 153 " list.__init__(self,x)"))
154 154 ip.run_cell("w=Mylist([1,2,3])")
155 155
156 156 from pickle import dumps
157 157
158 158 # We need to swap in our main module - this is only necessary
159 159 # inside the test framework, because IPython puts the interactive module
160 160 # in place (but the test framework undoes this).
161 161 _main = sys.modules['__main__']
162 162 sys.modules['__main__'] = ip.user_module
163 163 try:
164 164 res = dumps(ip.user_ns["w"])
165 165 finally:
166 166 sys.modules['__main__'] = _main
167 167 self.assertTrue(isinstance(res, bytes))
168 168
169 169 def test_global_ns(self):
170 170 "Code in functions must be able to access variables outside them."
171 171 ip = get_ipython()
172 172 ip.run_cell("a = 10")
173 173 ip.run_cell(("def f(x):\n"
174 174 " return x + a"))
175 175 ip.run_cell("b = f(12)")
176 176 self.assertEqual(ip.user_ns["b"], 22)
177 177
178 178 def test_bad_custom_tb(self):
179 179 """Check that InteractiveShell is protected from bad custom exception handlers"""
180 180 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
181 181 self.assertEqual(ip.custom_exceptions, (IOError,))
182 182 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
183 183 ip.run_cell(u'raise IOError("foo")')
184 184 self.assertEqual(ip.custom_exceptions, ())
185 185
186 186 def test_bad_custom_tb_return(self):
187 187 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
188 188 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
189 189 self.assertEqual(ip.custom_exceptions, (NameError,))
190 190 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
191 191 ip.run_cell(u'a=abracadabra')
192 192 self.assertEqual(ip.custom_exceptions, ())
193 193
194 194 def test_drop_by_id(self):
195 195 myvars = {"a":object(), "b":object(), "c": object()}
196 196 ip.push(myvars, interactive=False)
197 197 for name in myvars:
198 198 assert name in ip.user_ns, name
199 199 assert name in ip.user_ns_hidden, name
200 200 ip.user_ns['b'] = 12
201 201 ip.drop_by_id(myvars)
202 202 for name in ["a", "c"]:
203 203 assert name not in ip.user_ns, name
204 204 assert name not in ip.user_ns_hidden, name
205 205 assert ip.user_ns['b'] == 12
206 206 ip.reset()
207 207
208 208 def test_var_expand(self):
209 209 ip.user_ns['f'] = u'Ca\xf1o'
210 210 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
211 211 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
212 212 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
213 213 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
214 214
215 215 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
216 216
217 217 ip.user_ns['f'] = b'Ca\xc3\xb1o'
218 218 # This should not raise any exception:
219 219 ip.var_expand(u'echo $f')
220 220
221 221 def test_var_expand_local(self):
222 222 """Test local variable expansion in !system and %magic calls"""
223 223 # !system
224 224 ip.run_cell('def test():\n'
225 225 ' lvar = "ttt"\n'
226 226 ' ret = !echo {lvar}\n'
227 227 ' return ret[0]\n')
228 228 res = ip.user_ns['test']()
229 229 nt.assert_in('ttt', res)
230 230
231 231 # %magic
232 232 ip.run_cell('def makemacro():\n'
233 233 ' macroname = "macro_var_expand_locals"\n'
234 234 ' %macro {macroname} codestr\n')
235 235 ip.user_ns['codestr'] = "str(12)"
236 236 ip.run_cell('makemacro()')
237 237 nt.assert_in('macro_var_expand_locals', ip.user_ns)
238 238
239 239 def test_var_expand_self(self):
240 240 """Test variable expansion with the name 'self', which was failing.
241 241
242 242 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
243 243 """
244 244 ip.run_cell('class cTest:\n'
245 245 ' classvar="see me"\n'
246 246 ' def test(self):\n'
247 247 ' res = !echo Variable: {self.classvar}\n'
248 248 ' return res[0]\n')
249 249 nt.assert_in('see me', ip.user_ns['cTest']().test())
250 250
251 251 def test_bad_var_expand(self):
252 252 """var_expand on invalid formats shouldn't raise"""
253 253 # SyntaxError
254 254 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
255 255 # NameError
256 256 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
257 257 # ZeroDivisionError
258 258 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
259 259
260 260 def test_silent_postexec(self):
261 261 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
262 262 pre_explicit = mock.Mock()
263 263 pre_always = mock.Mock()
264 264 post_explicit = mock.Mock()
265 265 post_always = mock.Mock()
266 finally_explicit = mock.Mock()
267 finally_always = mock.Mock()
268 all_mocks = [pre_explicit, pre_always, post_explicit, post_always,
269 finally_explicit,finally_always]
266 270
267 271 ip.events.register('pre_run_cell', pre_explicit)
268 272 ip.events.register('pre_execute', pre_always)
269 273 ip.events.register('post_run_cell', post_explicit)
270 274 ip.events.register('post_execute', post_always)
275 ip.events.register('finally_run_cell', finally_explicit)
276 ip.events.register('finally_execute', finally_always)
271 277
272 278 try:
273 279 ip.run_cell("1", silent=True)
274 280 assert pre_always.called
275 281 assert not pre_explicit.called
276 282 assert post_always.called
277 283 assert not post_explicit.called
284 assert finally_always.called
285 assert not finally_explicit.called
278 286 # double-check that non-silent exec did what we expected
279 287 # silent to avoid
280 288 ip.run_cell("1")
281 289 assert pre_explicit.called
282 290 assert post_explicit.called
291 assert finally_explicit.called
292 # check that finally hooks are always called
293 [m.reset_mock() for m in all_mocks]
294 ip.run_cell("syntax error")
295 assert pre_always.called
296 assert pre_explicit.called
297 assert not post_always.called # because of `SyntaxError`
298 assert not post_explicit.called
299 assert finally_explicit.called
300 assert finally_always.called
283 301 finally:
284 302 # remove post-exec
285 303 ip.events.unregister('pre_run_cell', pre_explicit)
286 304 ip.events.unregister('pre_execute', pre_always)
287 305 ip.events.unregister('post_run_cell', post_explicit)
288 306 ip.events.unregister('post_execute', post_always)
307 ip.events.unregister('finally_run_cell', finally_explicit)
308 ip.events.unregister('finally_execute', finally_always)
289 309
290 310 def test_silent_noadvance(self):
291 311 """run_cell(silent=True) doesn't advance execution_count"""
292 312 ec = ip.execution_count
293 313 # silent should force store_history=False
294 314 ip.run_cell("1", store_history=True, silent=True)
295 315
296 316 self.assertEqual(ec, ip.execution_count)
297 317 # double-check that non-silent exec did what we expected
298 318 # silent to avoid
299 319 ip.run_cell("1", store_history=True)
300 320 self.assertEqual(ec+1, ip.execution_count)
301 321
302 322 def test_silent_nodisplayhook(self):
303 323 """run_cell(silent=True) doesn't trigger displayhook"""
304 324 d = dict(called=False)
305 325
306 326 trap = ip.display_trap
307 327 save_hook = trap.hook
308 328
309 329 def failing_hook(*args, **kwargs):
310 330 d['called'] = True
311 331
312 332 try:
313 333 trap.hook = failing_hook
314 334 res = ip.run_cell("1", silent=True)
315 335 self.assertFalse(d['called'])
316 336 self.assertIsNone(res.result)
317 337 # double-check that non-silent exec did what we expected
318 338 # silent to avoid
319 339 ip.run_cell("1")
320 340 self.assertTrue(d['called'])
321 341 finally:
322 342 trap.hook = save_hook
323 343
324 344 def test_ofind_line_magic(self):
325 345 from IPython.core.magic import register_line_magic
326 346
327 347 @register_line_magic
328 348 def lmagic(line):
329 349 "A line magic"
330 350
331 351 # Get info on line magic
332 352 lfind = ip._ofind('lmagic')
333 353 info = dict(found=True, isalias=False, ismagic=True,
334 354 namespace = 'IPython internal', obj= lmagic.__wrapped__,
335 355 parent = None)
336 356 nt.assert_equal(lfind, info)
337 357
338 358 def test_ofind_cell_magic(self):
339 359 from IPython.core.magic import register_cell_magic
340 360
341 361 @register_cell_magic
342 362 def cmagic(line, cell):
343 363 "A cell magic"
344 364
345 365 # Get info on cell magic
346 366 find = ip._ofind('cmagic')
347 367 info = dict(found=True, isalias=False, ismagic=True,
348 368 namespace = 'IPython internal', obj= cmagic.__wrapped__,
349 369 parent = None)
350 370 nt.assert_equal(find, info)
351 371
352 372 def test_ofind_property_with_error(self):
353 373 class A(object):
354 374 @property
355 375 def foo(self):
356 376 raise NotImplementedError()
357 377 a = A()
358 378
359 379 found = ip._ofind('a.foo', [('locals', locals())])
360 380 info = dict(found=True, isalias=False, ismagic=False,
361 381 namespace='locals', obj=A.foo, parent=a)
362 382 nt.assert_equal(found, info)
363 383
364 384 def test_ofind_multiple_attribute_lookups(self):
365 385 class A(object):
366 386 @property
367 387 def foo(self):
368 388 raise NotImplementedError()
369 389
370 390 a = A()
371 391 a.a = A()
372 392 a.a.a = A()
373 393
374 394 found = ip._ofind('a.a.a.foo', [('locals', locals())])
375 395 info = dict(found=True, isalias=False, ismagic=False,
376 396 namespace='locals', obj=A.foo, parent=a.a.a)
377 397 nt.assert_equal(found, info)
378 398
379 399 def test_ofind_slotted_attributes(self):
380 400 class A(object):
381 401 __slots__ = ['foo']
382 402 def __init__(self):
383 403 self.foo = 'bar'
384 404
385 405 a = A()
386 406 found = ip._ofind('a.foo', [('locals', locals())])
387 407 info = dict(found=True, isalias=False, ismagic=False,
388 408 namespace='locals', obj=a.foo, parent=a)
389 409 nt.assert_equal(found, info)
390 410
391 411 found = ip._ofind('a.bar', [('locals', locals())])
392 412 info = dict(found=False, isalias=False, ismagic=False,
393 413 namespace=None, obj=None, parent=a)
394 414 nt.assert_equal(found, info)
395 415
396 416 def test_ofind_prefers_property_to_instance_level_attribute(self):
397 417 class A(object):
398 418 @property
399 419 def foo(self):
400 420 return 'bar'
401 421 a = A()
402 422 a.__dict__['foo'] = 'baz'
403 423 nt.assert_equal(a.foo, 'bar')
404 424 found = ip._ofind('a.foo', [('locals', locals())])
405 425 nt.assert_is(found['obj'], A.foo)
406 426
407 427 def test_custom_syntaxerror_exception(self):
408 428 called = []
409 429 def my_handler(shell, etype, value, tb, tb_offset=None):
410 430 called.append(etype)
411 431 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
412 432
413 433 ip.set_custom_exc((SyntaxError,), my_handler)
414 434 try:
415 435 ip.run_cell("1f")
416 436 # Check that this was called, and only once.
417 437 self.assertEqual(called, [SyntaxError])
418 438 finally:
419 439 # Reset the custom exception hook
420 440 ip.set_custom_exc((), None)
421 441
422 442 def test_custom_exception(self):
423 443 called = []
424 444 def my_handler(shell, etype, value, tb, tb_offset=None):
425 445 called.append(etype)
426 446 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
427 447
428 448 ip.set_custom_exc((ValueError,), my_handler)
429 449 try:
430 450 res = ip.run_cell("raise ValueError('test')")
431 451 # Check that this was called, and only once.
432 452 self.assertEqual(called, [ValueError])
433 453 # Check that the error is on the result object
434 454 self.assertIsInstance(res.error_in_exec, ValueError)
435 455 finally:
436 456 # Reset the custom exception hook
437 457 ip.set_custom_exc((), None)
438 458
439 459 def test_mktempfile(self):
440 460 filename = ip.mktempfile()
441 461 # Check that we can open the file again on Windows
442 462 with open(filename, 'w') as f:
443 463 f.write('abc')
444 464
445 465 filename = ip.mktempfile(data='blah')
446 466 with open(filename, 'r') as f:
447 467 self.assertEqual(f.read(), 'blah')
448 468
449 469 def test_new_main_mod(self):
450 470 # Smoketest to check that this accepts a unicode module name
451 471 name = u'jiefmw'
452 472 mod = ip.new_main_mod(u'%s.py' % name, name)
453 473 self.assertEqual(mod.__name__, name)
454 474
455 475 def test_get_exception_only(self):
456 476 try:
457 477 raise KeyboardInterrupt
458 478 except KeyboardInterrupt:
459 479 msg = ip.get_exception_only()
460 480 self.assertEqual(msg, 'KeyboardInterrupt\n')
461 481
462 482 try:
463 483 raise DerivedInterrupt("foo")
464 484 except KeyboardInterrupt:
465 485 msg = ip.get_exception_only()
466 486 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
467 487
468 488 def test_inspect_text(self):
469 489 ip.run_cell('a = 5')
470 490 text = ip.object_inspect_text('a')
471 491 self.assertIsInstance(text, str)
472 492
473 493 def test_last_execution_result(self):
474 494 """ Check that last execution result gets set correctly (GH-10702) """
475 495 result = ip.run_cell('a = 5; a')
476 496 self.assertTrue(ip.last_execution_succeeded)
477 497 self.assertEqual(ip.last_execution_result.result, 5)
478 498
479 499 result = ip.run_cell('a = x_invalid_id_x')
480 500 self.assertFalse(ip.last_execution_succeeded)
481 501 self.assertFalse(ip.last_execution_result.success)
482 502 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
483 503
484 504
485 505 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
486 506
487 507 @onlyif_unicode_paths
488 508 def setUp(self):
489 509 self.BASETESTDIR = tempfile.mkdtemp()
490 510 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
491 511 os.mkdir(self.TESTDIR)
492 512 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
493 513 sfile.write("pass\n")
494 514 self.oldpath = os.getcwd()
495 515 os.chdir(self.TESTDIR)
496 516 self.fname = u"Γ₯Àâtestscript.py"
497 517
498 518 def tearDown(self):
499 519 os.chdir(self.oldpath)
500 520 shutil.rmtree(self.BASETESTDIR)
501 521
502 522 @onlyif_unicode_paths
503 523 def test_1(self):
504 524 """Test safe_execfile with non-ascii path
505 525 """
506 526 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
507 527
508 528 class ExitCodeChecks(tt.TempFileMixin):
509 529 def test_exit_code_ok(self):
510 530 self.system('exit 0')
511 531 self.assertEqual(ip.user_ns['_exit_code'], 0)
512 532
513 533 def test_exit_code_error(self):
514 534 self.system('exit 1')
515 535 self.assertEqual(ip.user_ns['_exit_code'], 1)
516 536
517 537 @skipif(not hasattr(signal, 'SIGALRM'))
518 538 def test_exit_code_signal(self):
519 539 self.mktmp("import signal, time\n"
520 540 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
521 541 "time.sleep(1)\n")
522 542 self.system("%s %s" % (sys.executable, self.fname))
523 543 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
524 544
525 545 @onlyif_cmds_exist("csh")
526 546 def test_exit_code_signal_csh(self):
527 547 SHELL = os.environ.get('SHELL', None)
528 548 os.environ['SHELL'] = find_cmd("csh")
529 549 try:
530 550 self.test_exit_code_signal()
531 551 finally:
532 552 if SHELL is not None:
533 553 os.environ['SHELL'] = SHELL
534 554 else:
535 555 del os.environ['SHELL']
536 556
537 557 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
538 558 system = ip.system_raw
539 559
540 560 @onlyif_unicode_paths
541 561 def test_1(self):
542 562 """Test system_raw with non-ascii cmd
543 563 """
544 564 cmd = u'''python -c "'Γ₯Àâ'" '''
545 565 ip.system_raw(cmd)
546 566
547 567 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
548 568 @mock.patch('os.system', side_effect=KeyboardInterrupt)
549 569 def test_control_c(self, *mocks):
550 570 try:
551 571 self.system("sleep 1 # wont happen")
552 572 except KeyboardInterrupt:
553 573 self.fail("system call should intercept "
554 574 "keyboard interrupt from subprocess.call")
555 575 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
556 576
557 577 # TODO: Exit codes are currently ignored on Windows.
558 578 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
559 579 system = ip.system_piped
560 580
561 581 @skip_win32
562 582 def test_exit_code_ok(self):
563 583 ExitCodeChecks.test_exit_code_ok(self)
564 584
565 585 @skip_win32
566 586 def test_exit_code_error(self):
567 587 ExitCodeChecks.test_exit_code_error(self)
568 588
569 589 @skip_win32
570 590 def test_exit_code_signal(self):
571 591 ExitCodeChecks.test_exit_code_signal(self)
572 592
573 593 class TestModules(unittest.TestCase, tt.TempFileMixin):
574 594 def test_extraneous_loads(self):
575 595 """Test we're not loading modules on startup that we shouldn't.
576 596 """
577 597 self.mktmp("import sys\n"
578 598 "print('numpy' in sys.modules)\n"
579 599 "print('ipyparallel' in sys.modules)\n"
580 600 "print('ipykernel' in sys.modules)\n"
581 601 )
582 602 out = "False\nFalse\nFalse\n"
583 603 tt.ipexec_validate(self.fname, out)
584 604
585 605 class Negator(ast.NodeTransformer):
586 606 """Negates all number literals in an AST."""
587 607 def visit_Num(self, node):
588 608 node.n = -node.n
589 609 return node
590 610
591 611 class TestAstTransform(unittest.TestCase):
592 612 def setUp(self):
593 613 self.negator = Negator()
594 614 ip.ast_transformers.append(self.negator)
595 615
596 616 def tearDown(self):
597 617 ip.ast_transformers.remove(self.negator)
598 618
599 619 def test_run_cell(self):
600 620 with tt.AssertPrints('-34'):
601 621 ip.run_cell('print (12 + 22)')
602 622
603 623 # A named reference to a number shouldn't be transformed.
604 624 ip.user_ns['n'] = 55
605 625 with tt.AssertNotPrints('-55'):
606 626 ip.run_cell('print (n)')
607 627
608 628 def test_timeit(self):
609 629 called = set()
610 630 def f(x):
611 631 called.add(x)
612 632 ip.push({'f':f})
613 633
614 634 with tt.AssertPrints("std. dev. of"):
615 635 ip.run_line_magic("timeit", "-n1 f(1)")
616 636 self.assertEqual(called, {-1})
617 637 called.clear()
618 638
619 639 with tt.AssertPrints("std. dev. of"):
620 640 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
621 641 self.assertEqual(called, {-2, -3})
622 642
623 643 def test_time(self):
624 644 called = []
625 645 def f(x):
626 646 called.append(x)
627 647 ip.push({'f':f})
628 648
629 649 # Test with an expression
630 650 with tt.AssertPrints("Wall time: "):
631 651 ip.run_line_magic("time", "f(5+9)")
632 652 self.assertEqual(called, [-14])
633 653 called[:] = []
634 654
635 655 # Test with a statement (different code path)
636 656 with tt.AssertPrints("Wall time: "):
637 657 ip.run_line_magic("time", "a = f(-3 + -2)")
638 658 self.assertEqual(called, [5])
639 659
640 660 def test_macro(self):
641 661 ip.push({'a':10})
642 662 # The AST transformation makes this do a+=-1
643 663 ip.define_macro("amacro", "a+=1\nprint(a)")
644 664
645 665 with tt.AssertPrints("9"):
646 666 ip.run_cell("amacro")
647 667 with tt.AssertPrints("8"):
648 668 ip.run_cell("amacro")
649 669
650 670 class IntegerWrapper(ast.NodeTransformer):
651 671 """Wraps all integers in a call to Integer()"""
652 672 def visit_Num(self, node):
653 673 if isinstance(node.n, int):
654 674 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
655 675 args=[node], keywords=[])
656 676 return node
657 677
658 678 class TestAstTransform2(unittest.TestCase):
659 679 def setUp(self):
660 680 self.intwrapper = IntegerWrapper()
661 681 ip.ast_transformers.append(self.intwrapper)
662 682
663 683 self.calls = []
664 684 def Integer(*args):
665 685 self.calls.append(args)
666 686 return args
667 687 ip.push({"Integer": Integer})
668 688
669 689 def tearDown(self):
670 690 ip.ast_transformers.remove(self.intwrapper)
671 691 del ip.user_ns['Integer']
672 692
673 693 def test_run_cell(self):
674 694 ip.run_cell("n = 2")
675 695 self.assertEqual(self.calls, [(2,)])
676 696
677 697 # This shouldn't throw an error
678 698 ip.run_cell("o = 2.0")
679 699 self.assertEqual(ip.user_ns['o'], 2.0)
680 700
681 701 def test_timeit(self):
682 702 called = set()
683 703 def f(x):
684 704 called.add(x)
685 705 ip.push({'f':f})
686 706
687 707 with tt.AssertPrints("std. dev. of"):
688 708 ip.run_line_magic("timeit", "-n1 f(1)")
689 709 self.assertEqual(called, {(1,)})
690 710 called.clear()
691 711
692 712 with tt.AssertPrints("std. dev. of"):
693 713 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
694 714 self.assertEqual(called, {(2,), (3,)})
695 715
696 716 class ErrorTransformer(ast.NodeTransformer):
697 717 """Throws an error when it sees a number."""
698 718 def visit_Num(self, node):
699 719 raise ValueError("test")
700 720
701 721 class TestAstTransformError(unittest.TestCase):
702 722 def test_unregistering(self):
703 723 err_transformer = ErrorTransformer()
704 724 ip.ast_transformers.append(err_transformer)
705 725
706 726 with tt.AssertPrints("unregister", channel='stderr'):
707 727 ip.run_cell("1 + 2")
708 728
709 729 # This should have been removed.
710 730 nt.assert_not_in(err_transformer, ip.ast_transformers)
711 731
712 732
713 733 class StringRejector(ast.NodeTransformer):
714 734 """Throws an InputRejected when it sees a string literal.
715 735
716 736 Used to verify that NodeTransformers can signal that a piece of code should
717 737 not be executed by throwing an InputRejected.
718 738 """
719 739
720 740 def visit_Str(self, node):
721 741 raise InputRejected("test")
722 742
723 743
724 744 class TestAstTransformInputRejection(unittest.TestCase):
725 745
726 746 def setUp(self):
727 747 self.transformer = StringRejector()
728 748 ip.ast_transformers.append(self.transformer)
729 749
730 750 def tearDown(self):
731 751 ip.ast_transformers.remove(self.transformer)
732 752
733 753 def test_input_rejection(self):
734 754 """Check that NodeTransformers can reject input."""
735 755
736 756 expect_exception_tb = tt.AssertPrints("InputRejected: test")
737 757 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
738 758
739 759 # Run the same check twice to verify that the transformer is not
740 760 # disabled after raising.
741 761 with expect_exception_tb, expect_no_cell_output:
742 762 ip.run_cell("'unsafe'")
743 763
744 764 with expect_exception_tb, expect_no_cell_output:
745 765 res = ip.run_cell("'unsafe'")
746 766
747 767 self.assertIsInstance(res.error_before_exec, InputRejected)
748 768
749 769 def test__IPYTHON__():
750 770 # This shouldn't raise a NameError, that's all
751 771 __IPYTHON__
752 772
753 773
754 774 class DummyRepr(object):
755 775 def __repr__(self):
756 776 return "DummyRepr"
757 777
758 778 def _repr_html_(self):
759 779 return "<b>dummy</b>"
760 780
761 781 def _repr_javascript_(self):
762 782 return "console.log('hi');", {'key': 'value'}
763 783
764 784
765 785 def test_user_variables():
766 786 # enable all formatters
767 787 ip.display_formatter.active_types = ip.display_formatter.format_types
768 788
769 789 ip.user_ns['dummy'] = d = DummyRepr()
770 790 keys = {'dummy', 'doesnotexist'}
771 791 r = ip.user_expressions({ key:key for key in keys})
772 792
773 793 nt.assert_equal(keys, set(r.keys()))
774 794 dummy = r['dummy']
775 795 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
776 796 nt.assert_equal(dummy['status'], 'ok')
777 797 data = dummy['data']
778 798 metadata = dummy['metadata']
779 799 nt.assert_equal(data.get('text/html'), d._repr_html_())
780 800 js, jsmd = d._repr_javascript_()
781 801 nt.assert_equal(data.get('application/javascript'), js)
782 802 nt.assert_equal(metadata.get('application/javascript'), jsmd)
783 803
784 804 dne = r['doesnotexist']
785 805 nt.assert_equal(dne['status'], 'error')
786 806 nt.assert_equal(dne['ename'], 'NameError')
787 807
788 808 # back to text only
789 809 ip.display_formatter.active_types = ['text/plain']
790 810
791 811 def test_user_expression():
792 812 # enable all formatters
793 813 ip.display_formatter.active_types = ip.display_formatter.format_types
794 814 query = {
795 815 'a' : '1 + 2',
796 816 'b' : '1/0',
797 817 }
798 818 r = ip.user_expressions(query)
799 819 import pprint
800 820 pprint.pprint(r)
801 821 nt.assert_equal(set(r.keys()), set(query.keys()))
802 822 a = r['a']
803 823 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
804 824 nt.assert_equal(a['status'], 'ok')
805 825 data = a['data']
806 826 metadata = a['metadata']
807 827 nt.assert_equal(data.get('text/plain'), '3')
808 828
809 829 b = r['b']
810 830 nt.assert_equal(b['status'], 'error')
811 831 nt.assert_equal(b['ename'], 'ZeroDivisionError')
812 832
813 833 # back to text only
814 834 ip.display_formatter.active_types = ['text/plain']
815 835
816 836
817 837
818 838
819 839
820 840 class TestSyntaxErrorTransformer(unittest.TestCase):
821 841 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
822 842
823 843 class SyntaxErrorTransformer(InputTransformer):
824 844
825 845 def push(self, line):
826 846 pos = line.find('syntaxerror')
827 847 if pos >= 0:
828 848 e = SyntaxError('input contains "syntaxerror"')
829 849 e.text = line
830 850 e.offset = pos + 1
831 851 raise e
832 852 return line
833 853
834 854 def reset(self):
835 855 pass
836 856
837 857 def setUp(self):
838 858 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
839 859 ip.input_splitter.python_line_transforms.append(self.transformer)
840 860 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
841 861
842 862 def tearDown(self):
843 863 ip.input_splitter.python_line_transforms.remove(self.transformer)
844 864 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
845 865
846 866 def test_syntaxerror_input_transformer(self):
847 867 with tt.AssertPrints('1234'):
848 868 ip.run_cell('1234')
849 869 with tt.AssertPrints('SyntaxError: invalid syntax'):
850 870 ip.run_cell('1 2 3') # plain python syntax error
851 871 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
852 872 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
853 873 with tt.AssertPrints('3456'):
854 874 ip.run_cell('3456')
855 875
856 876
857 877
858 878 def test_warning_suppression():
859 879 ip.run_cell("import warnings")
860 880 try:
861 881 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
862 882 ip.run_cell("warnings.warn('asdf')")
863 883 # Here's the real test -- if we run that again, we should get the
864 884 # warning again. Traditionally, each warning was only issued once per
865 885 # IPython session (approximately), even if the user typed in new and
866 886 # different code that should have also triggered the warning, leading
867 887 # to much confusion.
868 888 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
869 889 ip.run_cell("warnings.warn('asdf')")
870 890 finally:
871 891 ip.run_cell("del warnings")
872 892
873 893
874 894 def test_deprecation_warning():
875 895 ip.run_cell("""
876 896 import warnings
877 897 def wrn():
878 898 warnings.warn(
879 899 "I AM A WARNING",
880 900 DeprecationWarning
881 901 )
882 902 """)
883 903 try:
884 904 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
885 905 ip.run_cell("wrn()")
886 906 finally:
887 907 ip.run_cell("del warnings")
888 908 ip.run_cell("del wrn")
889 909
890 910
891 911 class TestImportNoDeprecate(tt.TempFileMixin):
892 912
893 913 def setup(self):
894 914 """Make a valid python temp file."""
895 915 self.mktmp("""
896 916 import warnings
897 917 def wrn():
898 918 warnings.warn(
899 919 "I AM A WARNING",
900 920 DeprecationWarning
901 921 )
902 922 """)
903 923
904 924 def test_no_dep(self):
905 925 """
906 926 No deprecation warning should be raised from imported functions
907 927 """
908 928 ip.run_cell("from {} import wrn".format(self.fname))
909 929
910 930 with tt.AssertNotPrints("I AM A WARNING"):
911 931 ip.run_cell("wrn()")
912 932 ip.run_cell("del wrn")
@@ -1,87 +1,110 b''
1 1 .. _events:
2 2 .. _callbacks:
3 3
4 4 ==============
5 5 IPython Events
6 6 ==============
7 7
8 8 Extension code can register callbacks functions which will be called on specific
9 9 events within the IPython code. You can see the current list of available
10 10 callbacks, and the parameters that will be passed with each, in the callback
11 11 prototype functions defined in :mod:`IPython.core.callbacks`.
12 12
13 13 To register callbacks, use :meth:`IPython.core.events.EventManager.register`.
14 14 For example::
15 15
16 16 class VarWatcher(object):
17 17 def __init__(self, ip):
18 18 self.shell = ip
19 19 self.last_x = None
20 20
21 21 def pre_execute(self):
22 22 self.last_x = self.shell.user_ns.get('x', None)
23 23
24 24 def post_execute(self):
25 25 if self.shell.user_ns.get('x', None) != self.last_x:
26 26 print("x changed!")
27
28 def finally_execute(self, result):
29 if result.error_before_exec:
30 print('Error before execution: %s' % result.error_before_exec)
31 else:
32 print('Execution result: %s', result.result)
27 33
28 34 def load_ipython_extension(ip):
29 35 vw = VarWatcher(ip)
30 36 ip.events.register('pre_execute', vw.pre_execute)
31 37 ip.events.register('post_execute', vw.post_execute)
38 ip.events.register('finally_execute', vw.finally_execute)
32 39
33 40
34 41 Events
35 42 ======
36 43
37 44 These are the events IPython will emit. Callbacks will be passed no arguments, unless otherwise specified.
38 45
39 46 shell_initialized
40 47 -----------------
41 48
42 49 .. code-block:: python
43 50
44 51 def shell_initialized(ipython):
45 52 ...
46 53
47 54 This event is triggered only once, at the end of setting up IPython.
48 55 Extensions registered to load by default as part of configuration can use this to execute code to finalize setup.
49 56 Callbacks will be passed the InteractiveShell instance.
50 57
51 58 pre_run_cell
52 59 ------------
53 60
54 61 ``pre_run_cell`` fires prior to interactive execution (e.g. a cell in a notebook).
55 62 It can be used to note the state prior to execution, and keep track of changes.
56 63
57 64 pre_execute
58 65 -----------
59 66
60 67 ``pre_execute`` is like ``pre_run_cell``, but is triggered prior to *any* execution.
61 68 Sometimes code can be executed by libraries, etc. which
62 69 skipping the history/display mechanisms, in which cases ``pre_run_cell`` will not fire.
63 70
64 71 post_run_cell
65 72 -------------
66 73
67 ``post_run_cell`` runs after interactive execution (e.g. a cell in a notebook).
68 It can be used to cleanup or notify or perform operations on any side effects produced during execution.
69 For instance, the inline matplotlib backend uses this event to display any figures created but not explicitly displayed during the course of the cell.
70
74 ``post_run_cell`` runs after successful interactive execution (e.g. a cell in a
75 notebook, but, for example, not when a ``SyntaxError`` was raised).
76 It can be used to cleanup or notify or perform operations on any side effects
77 produced during execution.
78 For instance, the inline matplotlib backend uses this event to display any
79 figures created but not explicitly displayed during the course of the cell.
71 80
72 81 post_execute
73 82 ------------
74 83
75 84 The same as ``pre_execute``, ``post_execute`` is like ``post_run_cell``,
76 but fires for *all* executions, not just interactive ones.
85 but fires for *all* successful executions, not just interactive ones.
86
87 finally_run_cell
88 -------------
89
90 ``finally_run_cell`` is like ``post_run_cell``, but fires after *all* executions
91 (even when, for example, a ``SyntaxError`` was raised).
92 Additionally, the execution result is provided as an argument.
93
94 finally_execute
95 ------------
96
97 ``finally_execute`` is like ``post_execute``, but fires after *all* executions
98 (even when, for example, a ``SyntaxError`` was raised).
99 Additionally, the execution result is provided as an argument.
77 100
78 101
79 102 .. seealso::
80 103
81 104 Module :mod:`IPython.core.hooks`
82 105 The older 'hooks' system allows end users to customise some parts of
83 106 IPython's behaviour.
84 107
85 108 :doc:`inputtransforms`
86 109 By registering input transformers that don't change code, you can monitor
87 110 what is being executed.
@@ -1,64 +1,67 b''
1 1 .. _execution_semantics:
2 2
3 3 Execution semantics in the IPython kernel
4 4 =========================================
5 5
6 6 The execution of user code consists of the following phases:
7 7
8 8 1. Fire the ``pre_execute`` event.
9 2. Fire the ``pre_run_cell`` event unless silent is True.
9 2. Fire the ``pre_run_cell`` event unless silent is ``True``.
10 10 3. Execute the ``code`` field, see below for details.
11 11 4. If execution succeeds, expressions in ``user_expressions`` are computed.
12 12 This ensures that any error in the expressions don't affect the main code execution.
13 5. Fire the post_execute event.
13 5. Fire the ``post_execute`` event unless the execution failed.
14 6. Fire the ``post_run_cell`` event unless the execution failed or silent is ``True``.
15 7. Fire the ``finally_execute`` event.
16 8. Fire the ``finally_run_cell`` event unless silent is ``True``.
14 17
15 18 .. seealso::
16 19
17 20 :doc:`/config/callbacks`
18 21
19 22
20 23 To understand how the ``code`` field is executed, one must know that Python
21 24 code can be compiled in one of three modes (controlled by the ``mode`` argument
22 25 to the :func:`compile` builtin):
23 26
24 27 *single*
25 28 Valid for a single interactive statement (though the source can contain
26 29 multiple lines, such as a for loop). When compiled in this mode, the
27 30 generated bytecode contains special instructions that trigger the calling of
28 31 :func:`sys.displayhook` for any expression in the block that returns a value.
29 32 This means that a single statement can actually produce multiple calls to
30 33 :func:`sys.displayhook`, if for example it contains a loop where each
31 34 iteration computes an unassigned expression would generate 10 calls::
32 35
33 36 for i in range(10):
34 37 i**2
35 38
36 39 *exec*
37 40 An arbitrary amount of source code, this is how modules are compiled.
38 41 :func:`sys.displayhook` is *never* implicitly called.
39 42
40 43 *eval*
41 44 A single expression that returns a value. :func:`sys.displayhook` is *never*
42 45 implicitly called.
43 46
44 47
45 48 The ``code`` field is split into individual blocks each of which is valid for
46 49 execution in 'single' mode, and then:
47 50
48 51 - If there is only a single block: it is executed in 'single' mode.
49 52
50 53 - If there is more than one block:
51 54
52 55 * if the last one is a single line long, run all but the last in 'exec' mode
53 56 and the very last one in 'single' mode. This makes it easy to type simple
54 57 expressions at the end to see computed values.
55 58
56 59 * if the last one is no more than two lines long, run all but the last in
57 60 'exec' mode and the very last one in 'single' mode. This makes it easy to
58 61 type simple expressions at the end to see computed values. - otherwise
59 62 (last one is also multiline), run all in 'exec' mode
60 63
61 64 * otherwise (last one is also multiline), run all in 'exec' mode as a single
62 65 unit.
63 66
64 67
General Comments 0
You need to be logged in to leave comments. Login now