##// END OF EJS Templates
Use `backcall` and introduce `ExecutionRequest`
Fabio Niephaus -
Show More

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

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