##// END OF EJS Templates
Massive amount of work to improve the test suite, restores doctests....
Fernando Perez -
Show More

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

@@ -0,0 +1,185 b''
1 """Tests for code execution (%run and related), which is particularly tricky.
2
3 Because of how %run manages namespaces, and the fact that we are trying here to
4 verify subtle object deletion and reference counting issues, the %run tests
5 will be kept in this separate file. This makes it easier to aggregate in one
6 place the tricks needed to handle it; most other magics are much easier to test
7 and we do so in a common test_magic file.
8 """
9 from __future__ import absolute_import
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
15 # stdlib
16 import os
17 import sys
18 import tempfile
19
20 # third-party
21 import nose.tools as nt
22
23 # our own
24 from IPython.utils.platutils import find_cmd
25 from IPython.utils import genutils
26 from IPython.testing import decorators as dec
27 from IPython.testing import tools as tt
28
29 #-----------------------------------------------------------------------------
30 # Test functions begin
31 #-----------------------------------------------------------------------------
32
33 def doctest_refbug():
34 """Very nasty problem with references held by multiple runs of a script.
35 See: https://bugs.launchpad.net/ipython/+bug/269966
36
37 In [1]: _ip.clear_main_mod_cache()
38 # random
39
40 In [2]: %run refbug
41
42 In [3]: call_f()
43 lowercased: hello
44
45 In [4]: %run refbug
46
47 In [5]: call_f()
48 lowercased: hello
49 lowercased: hello
50 """
51
52
53 def doctest_run_builtins():
54 r"""Check that %run doesn't damage __builtins__.
55
56 In [1]: import tempfile
57
58 In [2]: bid1 = id(__builtins__)
59
60 In [3]: fname = tempfile.mkstemp('.py')[1]
61
62 In [3]: f = open(fname,'w')
63
64 In [4]: f.write('pass\n')
65
66 In [5]: f.flush()
67
68 In [6]: t1 = type(__builtins__)
69
70 In [7]: %run "$fname"
71
72 In [7]: f.close()
73
74 In [8]: bid2 = id(__builtins__)
75
76 In [9]: t2 = type(__builtins__)
77
78 In [10]: t1 == t2
79 Out[10]: True
80
81 In [10]: bid1 == bid2
82 Out[10]: True
83
84 In [12]: try:
85 ....: os.unlink(fname)
86 ....: except:
87 ....: pass
88 ....:
89 """
90
91 # For some tests, it will be handy to organize them in a class with a common
92 # setup that makes a temp file
93
94 class TempFileMixin(object):
95 def mktmp(self, src, ext='.py'):
96 """Make a valid python temp file."""
97 fname, f = tt.temp_pyfile(src, ext)
98 self.tmpfile = f
99 self.fname = fname
100
101 def teardown(self):
102 self.tmpfile.close()
103 try:
104 os.unlink(self.fname)
105 except:
106 # On Windows, even though we close the file, we still can't delete
107 # it. I have no clue why
108 pass
109
110
111 class TestMagicRunPass(TempFileMixin):
112
113 def setup(self):
114 """Make a valid python temp file."""
115 self.mktmp('pass\n')
116
117 def run_tmpfile(self):
118 _ip = get_ipython()
119 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
120 # See below and ticket https://bugs.launchpad.net/bugs/366353
121 _ip.magic('run "%s"' % self.fname)
122
123 def test_builtins_id(self):
124 """Check that %run doesn't damage __builtins__ """
125 _ip = get_ipython()
126 # Test that the id of __builtins__ is not modified by %run
127 bid1 = id(_ip.user_ns['__builtins__'])
128 self.run_tmpfile()
129 bid2 = id(_ip.user_ns['__builtins__'])
130 tt.assert_equals(bid1, bid2)
131
132 def test_builtins_type(self):
133 """Check that the type of __builtins__ doesn't change with %run.
134
135 However, the above could pass if __builtins__ was already modified to
136 be a dict (it should be a module) by a previous use of %run. So we
137 also check explicitly that it really is a module:
138 """
139 _ip = get_ipython()
140 self.run_tmpfile()
141 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
142
143 def test_prompts(self):
144 """Test that prompts correctly generate after %run"""
145 self.run_tmpfile()
146 _ip = get_ipython()
147 p2 = str(_ip.outputcache.prompt2).strip()
148 nt.assert_equals(p2[:3], '...')
149
150
151 class TestMagicRunSimple(TempFileMixin):
152
153 def test_simpledef(self):
154 """Test that simple class definitions work."""
155 src = ("class foo: pass\n"
156 "def f(): return foo()")
157 self.mktmp(src)
158 _ip.magic('run "%s"' % self.fname)
159 _ip.runlines('t = isinstance(f(), foo)')
160 nt.assert_true(_ip.user_ns['t'])
161
162 def test_obj_del(self):
163 """Test that object's __del__ methods are called on exit."""
164
165 # This test is known to fail on win32.
166 # See ticket https://bugs.launchpad.net/bugs/366334
167 src = ("class A(object):\n"
168 " def __del__(self):\n"
169 " print 'object A deleted'\n"
170 "a = A()\n")
171 self.mktmp(src)
172 tt.ipexec_validate(self.fname, 'object A deleted')
173
174 def test_tclass(self):
175 mydir = os.path.dirname(__file__)
176 tc = os.path.join(mydir, 'tclass')
177 src = ("%%run '%s' C-first\n"
178 "%%run '%s' C-second\n") % (tc, tc)
179 self.mktmp(src, '.ipy')
180 out = """\
181 ARGV 1-: ['C-first']
182 ARGV 1-: ['C-second']
183 tclass.py: deleting object: C-first
184 """
185 tt.ipexec_validate(self.fname, out)
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,994 +1,994 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Prefiltering components.
4 Prefiltering components.
5
5
6 Prefilters transform user input before it is exec'd by Python. These
6 Prefilters transform user input before it is exec'd by Python. These
7 transforms are used to implement additional syntax such as !ls and %magic.
7 transforms are used to implement additional syntax such as !ls and %magic.
8
8
9 Authors:
9 Authors:
10
10
11 * Brian Granger
11 * Brian Granger
12 * Fernando Perez
12 * Fernando Perez
13 * Dan Milstein
13 * Dan Milstein
14 * Ville Vainio
14 * Ville Vainio
15 """
15 """
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Copyright (C) 2008-2009 The IPython Development Team
18 # Copyright (C) 2008-2009 The IPython Development Team
19 #
19 #
20 # Distributed under the terms of the BSD License. The full license is in
20 # Distributed under the terms of the BSD License. The full license is in
21 # the file COPYING, distributed as part of this software.
21 # the file COPYING, distributed as part of this software.
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Imports
25 # Imports
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 import __builtin__
28 import __builtin__
29 import codeop
29 import codeop
30 import keyword
30 import keyword
31 import os
31 import os
32 import re
32 import re
33 import sys
33 import sys
34
34
35 from IPython.core.alias import AliasManager
35 from IPython.core.alias import AliasManager
36 from IPython.core.autocall import IPyAutocall
36 from IPython.core.autocall import IPyAutocall
37 from IPython.core.component import Component
37 from IPython.core.component import Component
38 from IPython.core.splitinput import split_user_input
38 from IPython.core.splitinput import split_user_input
39 from IPython.core.page import page
39 from IPython.core.page import page
40
40
41 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
41 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
42 from IPython.utils.genutils import make_quoted_expr, Term
42 from IPython.utils.genutils import make_quoted_expr, Term
43 from IPython.utils.autoattr import auto_attr
43 from IPython.utils.autoattr import auto_attr
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Global utilities, errors and constants
46 # Global utilities, errors and constants
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 # Warning, these cannot be changed unless various regular expressions
49 # Warning, these cannot be changed unless various regular expressions
50 # are updated in a number of places. Not great, but at least we told you.
50 # are updated in a number of places. Not great, but at least we told you.
51 ESC_SHELL = '!'
51 ESC_SHELL = '!'
52 ESC_SH_CAP = '!!'
52 ESC_SH_CAP = '!!'
53 ESC_HELP = '?'
53 ESC_HELP = '?'
54 ESC_MAGIC = '%'
54 ESC_MAGIC = '%'
55 ESC_QUOTE = ','
55 ESC_QUOTE = ','
56 ESC_QUOTE2 = ';'
56 ESC_QUOTE2 = ';'
57 ESC_PAREN = '/'
57 ESC_PAREN = '/'
58
58
59
59
60 class PrefilterError(Exception):
60 class PrefilterError(Exception):
61 pass
61 pass
62
62
63
63
64 # RegExp to identify potential function names
64 # RegExp to identify potential function names
65 re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
65 re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
66
66
67 # RegExp to exclude strings with this start from autocalling. In
67 # RegExp to exclude strings with this start from autocalling. In
68 # particular, all binary operators should be excluded, so that if foo is
68 # particular, all binary operators should be excluded, so that if foo is
69 # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
69 # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
70 # characters '!=()' don't need to be checked for, as the checkPythonChars
70 # characters '!=()' don't need to be checked for, as the checkPythonChars
71 # routine explicitely does so, to catch direct calls and rebindings of
71 # routine explicitely does so, to catch direct calls and rebindings of
72 # existing names.
72 # existing names.
73
73
74 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
74 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
75 # it affects the rest of the group in square brackets.
75 # it affects the rest of the group in square brackets.
76 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
76 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
77 r'|^is |^not |^in |^and |^or ')
77 r'|^is |^not |^in |^and |^or ')
78
78
79 # try to catch also methods for stuff in lists/tuples/dicts: off
79 # try to catch also methods for stuff in lists/tuples/dicts: off
80 # (experimental). For this to work, the line_split regexp would need
80 # (experimental). For this to work, the line_split regexp would need
81 # to be modified so it wouldn't break things at '['. That line is
81 # to be modified so it wouldn't break things at '['. That line is
82 # nasty enough that I shouldn't change it until I can test it _well_.
82 # nasty enough that I shouldn't change it until I can test it _well_.
83 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
83 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
84
84
85
85
86 # Handler Check Utilities
86 # Handler Check Utilities
87 def is_shadowed(identifier, ip):
87 def is_shadowed(identifier, ip):
88 """Is the given identifier defined in one of the namespaces which shadow
88 """Is the given identifier defined in one of the namespaces which shadow
89 the alias and magic namespaces? Note that an identifier is different
89 the alias and magic namespaces? Note that an identifier is different
90 than ifun, because it can not contain a '.' character."""
90 than ifun, because it can not contain a '.' character."""
91 # This is much safer than calling ofind, which can change state
91 # This is much safer than calling ofind, which can change state
92 return (identifier in ip.user_ns \
92 return (identifier in ip.user_ns \
93 or identifier in ip.internal_ns \
93 or identifier in ip.internal_ns \
94 or identifier in ip.ns_table['builtin'])
94 or identifier in ip.ns_table['builtin'])
95
95
96
96
97 #-----------------------------------------------------------------------------
97 #-----------------------------------------------------------------------------
98 # The LineInfo class used throughout
98 # The LineInfo class used throughout
99 #-----------------------------------------------------------------------------
99 #-----------------------------------------------------------------------------
100
100
101
101
102 class LineInfo(object):
102 class LineInfo(object):
103 """A single line of input and associated info.
103 """A single line of input and associated info.
104
104
105 Includes the following as properties:
105 Includes the following as properties:
106
106
107 line
107 line
108 The original, raw line
108 The original, raw line
109
109
110 continue_prompt
110 continue_prompt
111 Is this line a continuation in a sequence of multiline input?
111 Is this line a continuation in a sequence of multiline input?
112
112
113 pre
113 pre
114 The initial esc character or whitespace.
114 The initial esc character or whitespace.
115
115
116 pre_char
116 pre_char
117 The escape character(s) in pre or the empty string if there isn't one.
117 The escape character(s) in pre or the empty string if there isn't one.
118 Note that '!!' is a possible value for pre_char. Otherwise it will
118 Note that '!!' is a possible value for pre_char. Otherwise it will
119 always be a single character.
119 always be a single character.
120
120
121 pre_whitespace
121 pre_whitespace
122 The leading whitespace from pre if it exists. If there is a pre_char,
122 The leading whitespace from pre if it exists. If there is a pre_char,
123 this is just ''.
123 this is just ''.
124
124
125 ifun
125 ifun
126 The 'function part', which is basically the maximal initial sequence
126 The 'function part', which is basically the maximal initial sequence
127 of valid python identifiers and the '.' character. This is what is
127 of valid python identifiers and the '.' character. This is what is
128 checked for alias and magic transformations, used for auto-calling,
128 checked for alias and magic transformations, used for auto-calling,
129 etc.
129 etc.
130
130
131 the_rest
131 the_rest
132 Everything else on the line.
132 Everything else on the line.
133 """
133 """
134 def __init__(self, line, continue_prompt):
134 def __init__(self, line, continue_prompt):
135 self.line = line
135 self.line = line
136 self.continue_prompt = continue_prompt
136 self.continue_prompt = continue_prompt
137 self.pre, self.ifun, self.the_rest = split_user_input(line)
137 self.pre, self.ifun, self.the_rest = split_user_input(line)
138
138
139 self.pre_char = self.pre.strip()
139 self.pre_char = self.pre.strip()
140 if self.pre_char:
140 if self.pre_char:
141 self.pre_whitespace = '' # No whitespace allowd before esc chars
141 self.pre_whitespace = '' # No whitespace allowd before esc chars
142 else:
142 else:
143 self.pre_whitespace = self.pre
143 self.pre_whitespace = self.pre
144
144
145 self._oinfo = None
145 self._oinfo = None
146
146
147 def ofind(self, ip):
147 def ofind(self, ip):
148 """Do a full, attribute-walking lookup of the ifun in the various
148 """Do a full, attribute-walking lookup of the ifun in the various
149 namespaces for the given IPython InteractiveShell instance.
149 namespaces for the given IPython InteractiveShell instance.
150
150
151 Return a dict with keys: found,obj,ospace,ismagic
151 Return a dict with keys: found,obj,ospace,ismagic
152
152
153 Note: can cause state changes because of calling getattr, but should
153 Note: can cause state changes because of calling getattr, but should
154 only be run if autocall is on and if the line hasn't matched any
154 only be run if autocall is on and if the line hasn't matched any
155 other, less dangerous handlers.
155 other, less dangerous handlers.
156
156
157 Does cache the results of the call, so can be called multiple times
157 Does cache the results of the call, so can be called multiple times
158 without worrying about *further* damaging state.
158 without worrying about *further* damaging state.
159 """
159 """
160 if not self._oinfo:
160 if not self._oinfo:
161 self._oinfo = ip.shell._ofind(self.ifun)
161 self._oinfo = ip.shell._ofind(self.ifun)
162 return self._oinfo
162 return self._oinfo
163
163
164 def __str__(self):
164 def __str__(self):
165 return "Lineinfo [%s|%s|%s]" %(self.pre,self.ifun,self.the_rest)
165 return "Lineinfo [%s|%s|%s]" %(self.pre,self.ifun,self.the_rest)
166
166
167
167
168 #-----------------------------------------------------------------------------
168 #-----------------------------------------------------------------------------
169 # Main Prefilter manager
169 # Main Prefilter manager
170 #-----------------------------------------------------------------------------
170 #-----------------------------------------------------------------------------
171
171
172
172
173 class PrefilterManager(Component):
173 class PrefilterManager(Component):
174 """Main prefilter component.
174 """Main prefilter component.
175
175
176 The IPython prefilter is run on all user input before it is run. The
176 The IPython prefilter is run on all user input before it is run. The
177 prefilter consumes lines of input and produces transformed lines of
177 prefilter consumes lines of input and produces transformed lines of
178 input.
178 input.
179
179
180 The iplementation consists of two phases:
180 The iplementation consists of two phases:
181
181
182 1. Transformers
182 1. Transformers
183 2. Checkers and handlers
183 2. Checkers and handlers
184
184
185 Over time, we plan on deprecating the checkers and handlers and doing
185 Over time, we plan on deprecating the checkers and handlers and doing
186 everything in the transformers.
186 everything in the transformers.
187
187
188 The transformers are instances of :class:`PrefilterTransformer` and have
188 The transformers are instances of :class:`PrefilterTransformer` and have
189 a single method :meth:`transform` that takes a line and returns a
189 a single method :meth:`transform` that takes a line and returns a
190 transformed line. The transformation can be accomplished using any
190 transformed line. The transformation can be accomplished using any
191 tool, but our current ones use regular expressions for speed. We also
191 tool, but our current ones use regular expressions for speed. We also
192 ship :mod:`pyparsing` in :mod:`IPython.external` for use in transformers.
192 ship :mod:`pyparsing` in :mod:`IPython.external` for use in transformers.
193
193
194 After all the transformers have been run, the line is fed to the checkers,
194 After all the transformers have been run, the line is fed to the checkers,
195 which are instances of :class:`PrefilterChecker`. The line is passed to
195 which are instances of :class:`PrefilterChecker`. The line is passed to
196 the :meth:`check` method, which either returns `None` or a
196 the :meth:`check` method, which either returns `None` or a
197 :class:`PrefilterHandler` instance. If `None` is returned, the other
197 :class:`PrefilterHandler` instance. If `None` is returned, the other
198 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
198 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
199 the line is passed to the :meth:`handle` method of the returned
199 the line is passed to the :meth:`handle` method of the returned
200 handler and no further checkers are tried.
200 handler and no further checkers are tried.
201
201
202 Both transformers and checkers have a `priority` attribute, that determines
202 Both transformers and checkers have a `priority` attribute, that determines
203 the order in which they are called. Smaller priorities are tried first.
203 the order in which they are called. Smaller priorities are tried first.
204
204
205 Both transformers and checkers also have `enabled` attribute, which is
205 Both transformers and checkers also have `enabled` attribute, which is
206 a boolean that determines if the instance is used.
206 a boolean that determines if the instance is used.
207
207
208 Users or developers can change the priority or enabled attribute of
208 Users or developers can change the priority or enabled attribute of
209 transformers or checkers, but they must call the :meth:`sort_checkers`
209 transformers or checkers, but they must call the :meth:`sort_checkers`
210 or :meth:`sort_transformers` method after changing the priority.
210 or :meth:`sort_transformers` method after changing the priority.
211 """
211 """
212
212
213 multi_line_specials = CBool(True, config=True)
213 multi_line_specials = CBool(True, config=True)
214
214
215 def __init__(self, parent, config=None):
215 def __init__(self, parent, config=None):
216 super(PrefilterManager, self).__init__(parent, config=config)
216 super(PrefilterManager, self).__init__(parent, config=config)
217 self.init_transformers()
217 self.init_transformers()
218 self.init_handlers()
218 self.init_handlers()
219 self.init_checkers()
219 self.init_checkers()
220
220
221 @auto_attr
221 @auto_attr
222 def shell(self):
222 def shell(self):
223 return Component.get_instances(
223 return Component.get_instances(
224 root=self.root,
224 root=self.root,
225 klass='IPython.core.iplib.InteractiveShell')[0]
225 klass='IPython.core.iplib.InteractiveShell')[0]
226
226
227 #-------------------------------------------------------------------------
227 #-------------------------------------------------------------------------
228 # API for managing transformers
228 # API for managing transformers
229 #-------------------------------------------------------------------------
229 #-------------------------------------------------------------------------
230
230
231 def init_transformers(self):
231 def init_transformers(self):
232 """Create the default transformers."""
232 """Create the default transformers."""
233 self._transformers = []
233 self._transformers = []
234 for transformer_cls in _default_transformers:
234 for transformer_cls in _default_transformers:
235 transformer_cls(self, config=self.config)
235 transformer_cls(self, config=self.config)
236
236
237 def sort_transformers(self):
237 def sort_transformers(self):
238 """Sort the transformers by priority.
238 """Sort the transformers by priority.
239
239
240 This must be called after the priority of a transformer is changed.
240 This must be called after the priority of a transformer is changed.
241 The :meth:`register_transformer` method calls this automatically.
241 The :meth:`register_transformer` method calls this automatically.
242 """
242 """
243 self._transformers.sort(cmp=lambda x,y: x.priority-y.priority)
243 self._transformers.sort(cmp=lambda x,y: x.priority-y.priority)
244
244
245 @property
245 @property
246 def transformers(self):
246 def transformers(self):
247 """Return a list of checkers, sorted by priority."""
247 """Return a list of checkers, sorted by priority."""
248 return self._transformers
248 return self._transformers
249
249
250 def register_transformer(self, transformer):
250 def register_transformer(self, transformer):
251 """Register a transformer instance."""
251 """Register a transformer instance."""
252 if transformer not in self._transformers:
252 if transformer not in self._transformers:
253 self._transformers.append(transformer)
253 self._transformers.append(transformer)
254 self.sort_transformers()
254 self.sort_transformers()
255
255
256 def unregister_transformer(self, transformer):
256 def unregister_transformer(self, transformer):
257 """Unregister a transformer instance."""
257 """Unregister a transformer instance."""
258 if transformer in self._transformers:
258 if transformer in self._transformers:
259 self._transformers.remove(transformer)
259 self._transformers.remove(transformer)
260
260
261 #-------------------------------------------------------------------------
261 #-------------------------------------------------------------------------
262 # API for managing checkers
262 # API for managing checkers
263 #-------------------------------------------------------------------------
263 #-------------------------------------------------------------------------
264
264
265 def init_checkers(self):
265 def init_checkers(self):
266 """Create the default checkers."""
266 """Create the default checkers."""
267 self._checkers = []
267 self._checkers = []
268 for checker in _default_checkers:
268 for checker in _default_checkers:
269 checker(self, config=self.config)
269 checker(self, config=self.config)
270
270
271 def sort_checkers(self):
271 def sort_checkers(self):
272 """Sort the checkers by priority.
272 """Sort the checkers by priority.
273
273
274 This must be called after the priority of a checker is changed.
274 This must be called after the priority of a checker is changed.
275 The :meth:`register_checker` method calls this automatically.
275 The :meth:`register_checker` method calls this automatically.
276 """
276 """
277 self._checkers.sort(cmp=lambda x,y: x.priority-y.priority)
277 self._checkers.sort(cmp=lambda x,y: x.priority-y.priority)
278
278
279 @property
279 @property
280 def checkers(self):
280 def checkers(self):
281 """Return a list of checkers, sorted by priority."""
281 """Return a list of checkers, sorted by priority."""
282 return self._checkers
282 return self._checkers
283
283
284 def register_checker(self, checker):
284 def register_checker(self, checker):
285 """Register a checker instance."""
285 """Register a checker instance."""
286 if checker not in self._checkers:
286 if checker not in self._checkers:
287 self._checkers.append(checker)
287 self._checkers.append(checker)
288 self.sort_checkers()
288 self.sort_checkers()
289
289
290 def unregister_checker(self, checker):
290 def unregister_checker(self, checker):
291 """Unregister a checker instance."""
291 """Unregister a checker instance."""
292 if checker in self._checkers:
292 if checker in self._checkers:
293 self._checkers.remove(checker)
293 self._checkers.remove(checker)
294
294
295 #-------------------------------------------------------------------------
295 #-------------------------------------------------------------------------
296 # API for managing checkers
296 # API for managing checkers
297 #-------------------------------------------------------------------------
297 #-------------------------------------------------------------------------
298
298
299 def init_handlers(self):
299 def init_handlers(self):
300 """Create the default handlers."""
300 """Create the default handlers."""
301 self._handlers = {}
301 self._handlers = {}
302 self._esc_handlers = {}
302 self._esc_handlers = {}
303 for handler in _default_handlers:
303 for handler in _default_handlers:
304 handler(self, config=self.config)
304 handler(self, config=self.config)
305
305
306 @property
306 @property
307 def handlers(self):
307 def handlers(self):
308 """Return a dict of all the handlers."""
308 """Return a dict of all the handlers."""
309 return self._handlers
309 return self._handlers
310
310
311 def register_handler(self, name, handler, esc_strings):
311 def register_handler(self, name, handler, esc_strings):
312 """Register a handler instance by name with esc_strings."""
312 """Register a handler instance by name with esc_strings."""
313 self._handlers[name] = handler
313 self._handlers[name] = handler
314 for esc_str in esc_strings:
314 for esc_str in esc_strings:
315 self._esc_handlers[esc_str] = handler
315 self._esc_handlers[esc_str] = handler
316
316
317 def unregister_handler(self, name, handler, esc_strings):
317 def unregister_handler(self, name, handler, esc_strings):
318 """Unregister a handler instance by name with esc_strings."""
318 """Unregister a handler instance by name with esc_strings."""
319 try:
319 try:
320 del self._handlers[name]
320 del self._handlers[name]
321 except KeyError:
321 except KeyError:
322 pass
322 pass
323 for esc_str in esc_strings:
323 for esc_str in esc_strings:
324 h = self._esc_handlers.get(esc_str)
324 h = self._esc_handlers.get(esc_str)
325 if h is handler:
325 if h is handler:
326 del self._esc_handlers[esc_str]
326 del self._esc_handlers[esc_str]
327
327
328 def get_handler_by_name(self, name):
328 def get_handler_by_name(self, name):
329 """Get a handler by its name."""
329 """Get a handler by its name."""
330 return self._handlers.get(name)
330 return self._handlers.get(name)
331
331
332 def get_handler_by_esc(self, esc_str):
332 def get_handler_by_esc(self, esc_str):
333 """Get a handler by its escape string."""
333 """Get a handler by its escape string."""
334 return self._esc_handlers.get(esc_str)
334 return self._esc_handlers.get(esc_str)
335
335
336 #-------------------------------------------------------------------------
336 #-------------------------------------------------------------------------
337 # Main prefiltering API
337 # Main prefiltering API
338 #-------------------------------------------------------------------------
338 #-------------------------------------------------------------------------
339
339
340 def prefilter_line_info(self, line_info):
340 def prefilter_line_info(self, line_info):
341 """Prefilter a line that has been converted to a LineInfo object.
341 """Prefilter a line that has been converted to a LineInfo object.
342
342
343 This implements the checker/handler part of the prefilter pipe.
343 This implements the checker/handler part of the prefilter pipe.
344 """
344 """
345 # print "prefilter_line_info: ", line_info
345 # print "prefilter_line_info: ", line_info
346 handler = self.find_handler(line_info)
346 handler = self.find_handler(line_info)
347 return handler.handle(line_info)
347 return handler.handle(line_info)
348
348
349 def find_handler(self, line_info):
349 def find_handler(self, line_info):
350 """Find a handler for the line_info by trying checkers."""
350 """Find a handler for the line_info by trying checkers."""
351 for checker in self.checkers:
351 for checker in self.checkers:
352 if checker.enabled:
352 if checker.enabled:
353 handler = checker.check(line_info)
353 handler = checker.check(line_info)
354 if handler:
354 if handler:
355 return handler
355 return handler
356 return self.get_handler_by_name('normal')
356 return self.get_handler_by_name('normal')
357
357
358 def transform_line(self, line, continue_prompt):
358 def transform_line(self, line, continue_prompt):
359 """Calls the enabled transformers in order of increasing priority."""
359 """Calls the enabled transformers in order of increasing priority."""
360 for transformer in self.transformers:
360 for transformer in self.transformers:
361 if transformer.enabled:
361 if transformer.enabled:
362 line = transformer.transform(line, continue_prompt)
362 line = transformer.transform(line, continue_prompt)
363 return line
363 return line
364
364
365 def prefilter_line(self, line, continue_prompt):
365 def prefilter_line(self, line, continue_prompt):
366 """Prefilter a single input line as text.
366 """Prefilter a single input line as text.
367
367
368 This method prefilters a single line of text by calling the
368 This method prefilters a single line of text by calling the
369 transformers and then the checkers/handlers.
369 transformers and then the checkers/handlers.
370 """
370 """
371
371
372 # print "prefilter_line: ", line, continue_prompt
372 # print "prefilter_line: ", line, continue_prompt
373 # All handlers *must* return a value, even if it's blank ('').
373 # All handlers *must* return a value, even if it's blank ('').
374
374
375 # Lines are NOT logged here. Handlers should process the line as
375 # Lines are NOT logged here. Handlers should process the line as
376 # needed, update the cache AND log it (so that the input cache array
376 # needed, update the cache AND log it (so that the input cache array
377 # stays synced).
377 # stays synced).
378
378
379 # save the line away in case we crash, so the post-mortem handler can
379 # save the line away in case we crash, so the post-mortem handler can
380 # record it
380 # record it
381 self.shell._last_input_line = line
381 self.shell._last_input_line = line
382
382
383 if not line:
383 if not line:
384 # Return immediately on purely empty lines, so that if the user
384 # Return immediately on purely empty lines, so that if the user
385 # previously typed some whitespace that started a continuation
385 # previously typed some whitespace that started a continuation
386 # prompt, he can break out of that loop with just an empty line.
386 # prompt, he can break out of that loop with just an empty line.
387 # This is how the default python prompt works.
387 # This is how the default python prompt works.
388
388
389 # Only return if the accumulated input buffer was just whitespace!
389 # Only return if the accumulated input buffer was just whitespace!
390 if ''.join(self.shell.buffer).isspace():
390 if ''.join(self.shell.buffer).isspace():
391 self.shell.buffer[:] = []
391 self.shell.buffer[:] = []
392 return ''
392 return ''
393
393
394 # At this point, we invoke our transformers.
394 # At this point, we invoke our transformers.
395 if not continue_prompt or (continue_prompt and self.multi_line_specials):
395 if not continue_prompt or (continue_prompt and self.multi_line_specials):
396 line = self.transform_line(line, continue_prompt)
396 line = self.transform_line(line, continue_prompt)
397
397
398 # Now we compute line_info for the checkers and handlers
398 # Now we compute line_info for the checkers and handlers
399 line_info = LineInfo(line, continue_prompt)
399 line_info = LineInfo(line, continue_prompt)
400
400
401 # the input history needs to track even empty lines
401 # the input history needs to track even empty lines
402 stripped = line.strip()
402 stripped = line.strip()
403
403
404 normal_handler = self.get_handler_by_name('normal')
404 normal_handler = self.get_handler_by_name('normal')
405 if not stripped:
405 if not stripped:
406 if not continue_prompt:
406 if not continue_prompt:
407 self.shell.outputcache.prompt_count -= 1
407 self.shell.outputcache.prompt_count -= 1
408
408
409 return normal_handler.handle(line_info)
409 return normal_handler.handle(line_info)
410
410
411 # special handlers are only allowed for single line statements
411 # special handlers are only allowed for single line statements
412 if continue_prompt and not self.multi_line_specials:
412 if continue_prompt and not self.multi_line_specials:
413 return normal_handler.handle(line_info)
413 return normal_handler.handle(line_info)
414
414
415 prefiltered = self.prefilter_line_info(line_info)
415 prefiltered = self.prefilter_line_info(line_info)
416 # print "prefiltered line: %r" % prefiltered
416 # print "prefiltered line: %r" % prefiltered
417 return prefiltered
417 return prefiltered
418
418
419 def prefilter_lines(self, lines, continue_prompt):
419 def prefilter_lines(self, lines, continue_prompt=False):
420 """Prefilter multiple input lines of text.
420 """Prefilter multiple input lines of text.
421
421
422 This is the main entry point for prefiltering multiple lines of
422 This is the main entry point for prefiltering multiple lines of
423 input. This simply calls :meth:`prefilter_line` for each line of
423 input. This simply calls :meth:`prefilter_line` for each line of
424 input.
424 input.
425
425
426 This covers cases where there are multiple lines in the user entry,
426 This covers cases where there are multiple lines in the user entry,
427 which is the case when the user goes back to a multiline history
427 which is the case when the user goes back to a multiline history
428 entry and presses enter.
428 entry and presses enter.
429 """
429 """
430 out = []
430 out = []
431 for line in lines.rstrip('\n').split('\n'):
431 for line in lines.rstrip('\n').split('\n'):
432 out.append(self.prefilter_line(line, continue_prompt))
432 out.append(self.prefilter_line(line, continue_prompt))
433 return '\n'.join(out)
433 return '\n'.join(out)
434
434
435
435
436 #-----------------------------------------------------------------------------
436 #-----------------------------------------------------------------------------
437 # Prefilter transformers
437 # Prefilter transformers
438 #-----------------------------------------------------------------------------
438 #-----------------------------------------------------------------------------
439
439
440
440
441 class PrefilterTransformer(Component):
441 class PrefilterTransformer(Component):
442 """Transform a line of user input."""
442 """Transform a line of user input."""
443
443
444 priority = Int(100, config=True)
444 priority = Int(100, config=True)
445 shell = Any
445 shell = Any
446 prefilter_manager = Any
446 prefilter_manager = Any
447 enabled = Bool(True, config=True)
447 enabled = Bool(True, config=True)
448
448
449 def __init__(self, parent, config=None):
449 def __init__(self, parent, config=None):
450 super(PrefilterTransformer, self).__init__(parent, config=config)
450 super(PrefilterTransformer, self).__init__(parent, config=config)
451 self.prefilter_manager.register_transformer(self)
451 self.prefilter_manager.register_transformer(self)
452
452
453 @auto_attr
453 @auto_attr
454 def shell(self):
454 def shell(self):
455 return Component.get_instances(
455 return Component.get_instances(
456 root=self.root,
456 root=self.root,
457 klass='IPython.core.iplib.InteractiveShell')[0]
457 klass='IPython.core.iplib.InteractiveShell')[0]
458
458
459 @auto_attr
459 @auto_attr
460 def prefilter_manager(self):
460 def prefilter_manager(self):
461 return PrefilterManager.get_instances(root=self.root)[0]
461 return PrefilterManager.get_instances(root=self.root)[0]
462
462
463 def transform(self, line, continue_prompt):
463 def transform(self, line, continue_prompt):
464 """Transform a line, returning the new one."""
464 """Transform a line, returning the new one."""
465 return None
465 return None
466
466
467 def __repr__(self):
467 def __repr__(self):