##// END OF EJS Templates
Completed first pass of inputsplitter with IPython syntax....
Fernando Perez -
Show More
@@ -1,521 +1,798 b''
1 """Analysis of text input into executable blocks.
1 """Analysis of text input into executable blocks.
2
2
3 The main class in this module, :class:`InputSplitter`, is designed to break
3 The main class in this module, :class:`InputSplitter`, is designed to break
4 input from either interactive, line-by-line environments or block-based ones,
4 input from either interactive, line-by-line environments or block-based ones,
5 into standalone blocks that can be executed by Python as 'single' statements
5 into standalone blocks that can be executed by Python as 'single' statements
6 (thus triggering sys.displayhook).
6 (thus triggering sys.displayhook).
7
7
8 For more details, see the class docstring below.
8 For more details, see the class docstring below.
9
10 Authors
11
12 * Fernando Perez
13 * Brian Granger
9 """
14 """
10 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
11 # Copyright (C) 2010 The IPython Development Team
16 # Copyright (C) 2010 The IPython Development Team
12 #
17 #
13 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
16
21
17 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
18 # Imports
23 # Imports
19 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
20 # stdlib
25 # stdlib
21 import codeop
26 import codeop
22 import re
27 import re
23 import sys
28 import sys
24
29
25 # IPython modules
30 # IPython modules
26 from IPython.utils.text import make_quoted_expr
31 from IPython.utils.text import make_quoted_expr
27
32
28 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Globals
35 #-----------------------------------------------------------------------------
36
37 # The escape sequences that define the syntax transformations IPython will
38 # apply to user input. These can NOT be just changed here: many regular
39 # expressions and other parts of the code may use their hardcoded values, and
40 # for all intents and purposes they constitute the 'IPython syntax', so they
41 # should be considered fixed.
42
43 ESC_SHELL = '!'
44 ESC_SH_CAP = '!!'
45 ESC_HELP = '?'
46 ESC_HELP2 = '??'
47 ESC_MAGIC = '%'
48 ESC_QUOTE = ','
49 ESC_QUOTE2 = ';'
50 ESC_PAREN = '/'
51
52 #-----------------------------------------------------------------------------
29 # Utilities
53 # Utilities
30 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
31
55
32 # FIXME: move these utilities to the general ward...
56 # FIXME: These are general-purpose utilities that later can be moved to the
57 # general ward. Kept here for now because we're being very strict about test
58 # coverage with this code, and this lets us ensure that we keep 100% coverage
59 # while developing.
33
60
34 # compiled regexps for autoindent management
61 # compiled regexps for autoindent management
35 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
62 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
36 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
63 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
37
64
38
65
39 def num_ini_spaces(s):
66 def num_ini_spaces(s):
40 """Return the number of initial spaces in a string.
67 """Return the number of initial spaces in a string.
41
68
42 Note that tabs are counted as a single space. For now, we do *not* support
69 Note that tabs are counted as a single space. For now, we do *not* support
43 mixing of tabs and spaces in the user's input.
70 mixing of tabs and spaces in the user's input.
44
71
45 Parameters
72 Parameters
46 ----------
73 ----------
47 s : string
74 s : string
48
75
49 Returns
76 Returns
50 -------
77 -------
51 n : int
78 n : int
52 """
79 """
53
80
54 ini_spaces = ini_spaces_re.match(s)
81 ini_spaces = ini_spaces_re.match(s)
55 if ini_spaces:
82 if ini_spaces:
56 return ini_spaces.end()
83 return ini_spaces.end()
57 else:
84 else:
58 return 0
85 return 0
59
86
60
87
61 def remove_comments(src):
88 def remove_comments(src):
62 """Remove all comments from input source.
89 """Remove all comments from input source.
63
90
64 Note: comments are NOT recognized inside of strings!
91 Note: comments are NOT recognized inside of strings!
65
92
66 Parameters
93 Parameters
67 ----------
94 ----------
68 src : string
95 src : string
69 A single or multiline input string.
96 A single or multiline input string.
70
97
71 Returns
98 Returns
72 -------
99 -------
73 String with all Python comments removed.
100 String with all Python comments removed.
74 """
101 """
75
102
76 return re.sub('#.*', '', src)
103 return re.sub('#.*', '', src)
77
104
78
105
79 def get_input_encoding():
106 def get_input_encoding():
80 """Return the default standard input encoding.
107 """Return the default standard input encoding.
81
108
82 If sys.stdin has no encoding, 'ascii' is returned."""
109 If sys.stdin has no encoding, 'ascii' is returned."""
83 # There are strange environments for which sys.stdin.encoding is None. We
110 # There are strange environments for which sys.stdin.encoding is None. We
84 # ensure that a valid encoding is returned.
111 # ensure that a valid encoding is returned.
85 encoding = getattr(sys.stdin, 'encoding', None)
112 encoding = getattr(sys.stdin, 'encoding', None)
86 if encoding is None:
113 if encoding is None:
87 encoding = 'ascii'
114 encoding = 'ascii'
88 return encoding
115 return encoding
89
116
90 #-----------------------------------------------------------------------------
117 #-----------------------------------------------------------------------------
91 # Classes and functions
118 # Classes and functions for normal Python syntax handling
92 #-----------------------------------------------------------------------------
119 #-----------------------------------------------------------------------------
93
120
94 class InputSplitter(object):
121 class InputSplitter(object):
95 """An object that can split Python source input in executable blocks.
122 """An object that can split Python source input in executable blocks.
96
123
97 This object is designed to be used in one of two basic modes:
124 This object is designed to be used in one of two basic modes:
98
125
99 1. By feeding it python source line-by-line, using :meth:`push`. In this
126 1. By feeding it python source line-by-line, using :meth:`push`. In this
100 mode, it will return on each push whether the currently pushed code
127 mode, it will return on each push whether the currently pushed code
101 could be executed already. In addition, it provides a method called
128 could be executed already. In addition, it provides a method called
102 :meth:`push_accepts_more` that can be used to query whether more input
129 :meth:`push_accepts_more` that can be used to query whether more input
103 can be pushed into a single interactive block.
130 can be pushed into a single interactive block.
104
131
105 2. By calling :meth:`split_blocks` with a single, multiline Python string,
132 2. By calling :meth:`split_blocks` with a single, multiline Python string,
106 that is then split into blocks each of which can be executed
133 that is then split into blocks each of which can be executed
107 interactively as a single statement.
134 interactively as a single statement.
108
135
109 This is a simple example of how an interactive terminal-based client can use
136 This is a simple example of how an interactive terminal-based client can use
110 this tool::
137 this tool::
111
138
112 isp = InputSplitter()
139 isp = InputSplitter()
113 while isp.push_accepts_more():
140 while isp.push_accepts_more():
114 indent = ' '*isp.indent_spaces
141 indent = ' '*isp.indent_spaces
115 prompt = '>>> ' + indent
142 prompt = '>>> ' + indent
116 line = indent + raw_input(prompt)
143 line = indent + raw_input(prompt)
117 isp.push(line)
144 isp.push(line)
118 print 'Input source was:\n', isp.source_reset(),
145 print 'Input source was:\n', isp.source_reset(),
119 """
146 """
120 # Number of spaces of indentation computed from input that has been pushed
147 # Number of spaces of indentation computed from input that has been pushed
121 # so far. This is the attributes callers should query to get the current
148 # so far. This is the attributes callers should query to get the current
122 # indentation level, in order to provide auto-indent facilities.
149 # indentation level, in order to provide auto-indent facilities.
123 indent_spaces = 0
150 indent_spaces = 0
124 # String, indicating the default input encoding. It is computed by default
151 # String, indicating the default input encoding. It is computed by default
125 # at initialization time via get_input_encoding(), but it can be reset by a
152 # at initialization time via get_input_encoding(), but it can be reset by a
126 # client with specific knowledge of the encoding.
153 # client with specific knowledge of the encoding.
127 encoding = ''
154 encoding = ''
128 # String where the current full source input is stored, properly encoded.
155 # String where the current full source input is stored, properly encoded.
129 # Reading this attribute is the normal way of querying the currently pushed
156 # Reading this attribute is the normal way of querying the currently pushed
130 # source code, that has been properly encoded.
157 # source code, that has been properly encoded.
131 source = ''
158 source = ''
132 # Code object corresponding to the current source. It is automatically
159 # Code object corresponding to the current source. It is automatically
133 # synced to the source, so it can be queried at any time to obtain the code
160 # synced to the source, so it can be queried at any time to obtain the code
134 # object; it will be None if the source doesn't compile to valid Python.
161 # object; it will be None if the source doesn't compile to valid Python.
135 code = None
162 code = None
136 # Input mode
163 # Input mode
137 input_mode = 'append'
164 input_mode = 'append'
138
165
139 # Private attributes
166 # Private attributes
140
167
141 # List with lines of input accumulated so far
168 # List with lines of input accumulated so far
142 _buffer = None
169 _buffer = None
143 # Command compiler
170 # Command compiler
144 _compile = None
171 _compile = None
145 # Mark when input has changed indentation all the way back to flush-left
172 # Mark when input has changed indentation all the way back to flush-left
146 _full_dedent = False
173 _full_dedent = False
147 # Boolean indicating whether the current block is complete
174 # Boolean indicating whether the current block is complete
148 _is_complete = None
175 _is_complete = None
149
176
150 def __init__(self, input_mode=None):
177 def __init__(self, input_mode=None):
151 """Create a new InputSplitter instance.
178 """Create a new InputSplitter instance.
152
179
153 Parameters
180 Parameters
154 ----------
181 ----------
155 input_mode : str
182 input_mode : str
156
183
157 One of 'append', 'replace', default is 'append'. This controls how
184 One of 'append', 'replace', default is 'append'. This controls how
158 new inputs are used: in 'append' mode, they are appended to the
185 new inputs are used: in 'append' mode, they are appended to the
159 existing buffer and the whole buffer is compiled; in 'replace' mode,
186 existing buffer and the whole buffer is compiled; in 'replace' mode,
160 each new input completely replaces all prior inputs. Replace mode is
187 each new input completely replaces all prior inputs. Replace mode is
161 thus equivalent to prepending a full reset() to every push() call.
188 thus equivalent to prepending a full reset() to every push() call.
162
189
163 In practice, line-oriented clients likely want to use 'append' mode
190 In practice, line-oriented clients likely want to use 'append' mode
164 while block-oriented ones will want to use 'replace'.
191 while block-oriented ones will want to use 'replace'.
165 """
192 """
166 self._buffer = []
193 self._buffer = []
167 self._compile = codeop.CommandCompiler()
194 self._compile = codeop.CommandCompiler()
168 self.encoding = get_input_encoding()
195 self.encoding = get_input_encoding()
169 self.input_mode = InputSplitter.input_mode if input_mode is None \
196 self.input_mode = InputSplitter.input_mode if input_mode is None \
170 else input_mode
197 else input_mode
171
198
172 def reset(self):
199 def reset(self):
173 """Reset the input buffer and associated state."""
200 """Reset the input buffer and associated state."""
174 self.indent_spaces = 0
201 self.indent_spaces = 0
175 self._buffer[:] = []
202 self._buffer[:] = []
176 self.source = ''
203 self.source = ''
177 self.code = None
204 self.code = None
178 self._is_complete = False
205 self._is_complete = False
179 self._full_dedent = False
206 self._full_dedent = False
180
207
181 def source_reset(self):
208 def source_reset(self):
182 """Return the input source and perform a full reset.
209 """Return the input source and perform a full reset.
183 """
210 """
184 out = self.source
211 out = self.source
185 self.reset()
212 self.reset()
186 return out
213 return out
187
214
188 def push(self, lines):
215 def push(self, lines):
189 """Push one ore more lines of input.
216 """Push one ore more lines of input.
190
217
191 This stores the given lines and returns a status code indicating
218 This stores the given lines and returns a status code indicating
192 whether the code forms a complete Python block or not.
219 whether the code forms a complete Python block or not.
193
220
194 Any exceptions generated in compilation are swallowed, but if an
221 Any exceptions generated in compilation are swallowed, but if an
195 exception was produced, the method returns True.
222 exception was produced, the method returns True.
196
223
197 Parameters
224 Parameters
198 ----------
225 ----------
199 lines : string
226 lines : string
200 One or more lines of Python input.
227 One or more lines of Python input.
201
228
202 Returns
229 Returns
203 -------
230 -------
204 is_complete : boolean
231 is_complete : boolean
205 True if the current input source (the result of the current input
232 True if the current input source (the result of the current input
206 plus prior inputs) forms a complete Python execution block. Note that
233 plus prior inputs) forms a complete Python execution block. Note that
207 this value is also stored as a private attribute (_is_complete), so it
234 this value is also stored as a private attribute (_is_complete), so it
208 can be queried at any time.
235 can be queried at any time.
209 """
236 """
210 if self.input_mode == 'replace':
237 if self.input_mode == 'replace':
211 self.reset()
238 self.reset()
212
239
213 # If the source code has leading blanks, add 'if 1:\n' to it
240 # If the source code has leading blanks, add 'if 1:\n' to it
214 # this allows execution of indented pasted code. It is tempting
241 # this allows execution of indented pasted code. It is tempting
215 # to add '\n' at the end of source to run commands like ' a=1'
242 # to add '\n' at the end of source to run commands like ' a=1'
216 # directly, but this fails for more complicated scenarios
243 # directly, but this fails for more complicated scenarios
217 if not self._buffer and lines[:1] in [' ', '\t']:
244 if not self._buffer and lines[:1] in [' ', '\t']:
218 lines = 'if 1:\n%s' % lines
245 lines = 'if 1:\n%s' % lines
219
246
220 self._store(lines)
247 self._store(lines)
221 source = self.source
248 source = self.source
222
249
223 # Before calling _compile(), reset the code object to None so that if an
250 # Before calling _compile(), reset the code object to None so that if an
224 # exception is raised in compilation, we don't mislead by having
251 # exception is raised in compilation, we don't mislead by having
225 # inconsistent code/source attributes.
252 # inconsistent code/source attributes.
226 self.code, self._is_complete = None, None
253 self.code, self._is_complete = None, None
227
254
228 self._update_indent(lines)
255 self._update_indent(lines)
229 try:
256 try:
230 self.code = self._compile(source)
257 self.code = self._compile(source)
231 # Invalid syntax can produce any of a number of different errors from
258 # Invalid syntax can produce any of a number of different errors from
232 # inside the compiler, so we have to catch them all. Syntax errors
259 # inside the compiler, so we have to catch them all. Syntax errors
233 # immediately produce a 'ready' block, so the invalid Python can be
260 # immediately produce a 'ready' block, so the invalid Python can be
234 # sent to the kernel for evaluation with possible ipython
261 # sent to the kernel for evaluation with possible ipython
235 # special-syntax conversion.
262 # special-syntax conversion.
236 except (SyntaxError, OverflowError, ValueError, TypeError,
263 except (SyntaxError, OverflowError, ValueError, TypeError,
237 MemoryError):
264 MemoryError):
238 self._is_complete = True
265 self._is_complete = True
239 else:
266 else:
240 # Compilation didn't produce any exceptions (though it may not have
267 # Compilation didn't produce any exceptions (though it may not have
241 # given a complete code object)
268 # given a complete code object)
242 self._is_complete = self.code is not None
269 self._is_complete = self.code is not None
243
270
244 return self._is_complete
271 return self._is_complete
245
272
246 def push_accepts_more(self):
273 def push_accepts_more(self):
247 """Return whether a block of interactive input can accept more input.
274 """Return whether a block of interactive input can accept more input.
248
275
249 This method is meant to be used by line-oriented frontends, who need to
276 This method is meant to be used by line-oriented frontends, who need to
250 guess whether a block is complete or not based solely on prior and
277 guess whether a block is complete or not based solely on prior and
251 current input lines. The InputSplitter considers it has a complete
278 current input lines. The InputSplitter considers it has a complete
252 interactive block and will not accept more input only when either a
279 interactive block and will not accept more input only when either a
253 SyntaxError is raised, or *all* of the following are true:
280 SyntaxError is raised, or *all* of the following are true:
254
281
255 1. The input compiles to a complete statement.
282 1. The input compiles to a complete statement.
256
283
257 2. The indentation level is flush-left (because if we are indented,
284 2. The indentation level is flush-left (because if we are indented,
258 like inside a function definition or for loop, we need to keep
285 like inside a function definition or for loop, we need to keep
259 reading new input).
286 reading new input).
260
287
261 3. There is one extra line consisting only of whitespace.
288 3. There is one extra line consisting only of whitespace.
262
289
263 Because of condition #3, this method should be used only by
290 Because of condition #3, this method should be used only by
264 *line-oriented* frontends, since it means that intermediate blank lines
291 *line-oriented* frontends, since it means that intermediate blank lines
265 are not allowed in function definitions (or any other indented block).
292 are not allowed in function definitions (or any other indented block).
266
293
267 Block-oriented frontends that have a separate keyboard event to
294 Block-oriented frontends that have a separate keyboard event to
268 indicate execution should use the :meth:`split_blocks` method instead.
295 indicate execution should use the :meth:`split_blocks` method instead.
269
296
270 If the current input produces a syntax error, this method immediately
297 If the current input produces a syntax error, this method immediately
271 returns False but does *not* raise the syntax error exception, as
298 returns False but does *not* raise the syntax error exception, as
272 typically clients will want to send invalid syntax to an execution
299 typically clients will want to send invalid syntax to an execution
273 backend which might convert the invalid syntax into valid Python via
300 backend which might convert the invalid syntax into valid Python via
274 one of the dynamic IPython mechanisms.
301 one of the dynamic IPython mechanisms.
275 """
302 """
276
303
277 if not self._is_complete:
304 if not self._is_complete:
278 return True
305 return True
279
306
280 if self.indent_spaces==0:
307 if self.indent_spaces==0:
281 return False
308 return False
282
309
283 last_line = self.source.splitlines()[-1]
310 last_line = self.source.splitlines()[-1]
284 return bool(last_line and not last_line.isspace())
311 return bool(last_line and not last_line.isspace())
285
312
286 def split_blocks(self, lines):
313 def split_blocks(self, lines):
287 """Split a multiline string into multiple input blocks.
314 """Split a multiline string into multiple input blocks.
288
315
289 Note: this method starts by performing a full reset().
316 Note: this method starts by performing a full reset().
290
317
291 Parameters
318 Parameters
292 ----------
319 ----------
293 lines : str
320 lines : str
294 A possibly multiline string.
321 A possibly multiline string.
295
322
296 Returns
323 Returns
297 -------
324 -------
298 blocks : list
325 blocks : list
299 A list of strings, each possibly multiline. Each string corresponds
326 A list of strings, each possibly multiline. Each string corresponds
300 to a single block that can be compiled in 'single' mode (unless it
327 to a single block that can be compiled in 'single' mode (unless it
301 has a syntax error)."""
328 has a syntax error)."""
302
329
303 # This code is fairly delicate. If you make any changes here, make
330 # This code is fairly delicate. If you make any changes here, make
304 # absolutely sure that you do run the full test suite and ALL tests
331 # absolutely sure that you do run the full test suite and ALL tests
305 # pass.
332 # pass.
306
333
307 self.reset()
334 self.reset()
308 blocks = []
335 blocks = []
309
336
310 # Reversed copy so we can use pop() efficiently and consume the input
337 # Reversed copy so we can use pop() efficiently and consume the input
311 # as a stack
338 # as a stack
312 lines = lines.splitlines()[::-1]
339 lines = lines.splitlines()[::-1]
313 # Outer loop over all input
340 # Outer loop over all input
314 while lines:
341 while lines:
315 # Inner loop to build each block
342 # Inner loop to build each block
316 while True:
343 while True:
317 # Safety exit from inner loop
344 # Safety exit from inner loop
318 if not lines:
345 if not lines:
319 break
346 break
320 # Grab next line but don't push it yet
347 # Grab next line but don't push it yet
321 next_line = lines.pop()
348 next_line = lines.pop()
322 # Blank/empty lines are pushed as-is
349 # Blank/empty lines are pushed as-is
323 if not next_line or next_line.isspace():
350 if not next_line or next_line.isspace():
324 self.push(next_line)
351 self.push(next_line)
325 continue
352 continue
326
353
327 # Check indentation changes caused by the *next* line
354 # Check indentation changes caused by the *next* line
328 indent_spaces, _full_dedent = self._find_indent(next_line)
355 indent_spaces, _full_dedent = self._find_indent(next_line)
329
356
330 # If the next line causes a dedent, it can be for two differnt
357 # If the next line causes a dedent, it can be for two differnt
331 # reasons: either an explicit de-dent by the user or a
358 # reasons: either an explicit de-dent by the user or a
332 # return/raise/pass statement. These MUST be handled
359 # return/raise/pass statement. These MUST be handled
333 # separately:
360 # separately:
334 #
361 #
335 # 1. the first case is only detected when the actual explicit
362 # 1. the first case is only detected when the actual explicit
336 # dedent happens, and that would be the *first* line of a *new*
363 # dedent happens, and that would be the *first* line of a *new*
337 # block. Thus, we must put the line back into the input buffer
364 # block. Thus, we must put the line back into the input buffer
338 # so that it starts a new block on the next pass.
365 # so that it starts a new block on the next pass.
339 #
366 #
340 # 2. the second case is detected in the line before the actual
367 # 2. the second case is detected in the line before the actual
341 # dedent happens, so , we consume the line and we can break out
368 # dedent happens, so , we consume the line and we can break out
342 # to start a new block.
369 # to start a new block.
343
370
344 # Case 1, explicit dedent causes a break
371 # Case 1, explicit dedent causes a break
345 if _full_dedent and not next_line.startswith(' '):
372 if _full_dedent and not next_line.startswith(' '):
346 lines.append(next_line)
373 lines.append(next_line)
347 break
374 break
348
375
349 # Otherwise any line is pushed
376 # Otherwise any line is pushed
350 self.push(next_line)
377 self.push(next_line)
351
378
352 # Case 2, full dedent with full block ready:
379 # Case 2, full dedent with full block ready:
353 if _full_dedent or \
380 if _full_dedent or \
354 self.indent_spaces==0 and not self.push_accepts_more():
381 self.indent_spaces==0 and not self.push_accepts_more():
355 break
382 break
356 # Form the new block with the current source input
383 # Form the new block with the current source input
357 blocks.append(self.source_reset())
384 blocks.append(self.source_reset())
358
385
359 return blocks
386 return blocks
360
387
361 #------------------------------------------------------------------------
388 #------------------------------------------------------------------------
362 # Private interface
389 # Private interface
363 #------------------------------------------------------------------------
390 #------------------------------------------------------------------------
364
391
365 def _find_indent(self, line):
392 def _find_indent(self, line):
366 """Compute the new indentation level for a single line.
393 """Compute the new indentation level for a single line.
367
394
368 Parameters
395 Parameters
369 ----------
396 ----------
370 line : str
397 line : str
371 A single new line of non-whitespace, non-comment Python input.
398 A single new line of non-whitespace, non-comment Python input.
372
399
373 Returns
400 Returns
374 -------
401 -------
375 indent_spaces : int
402 indent_spaces : int
376 New value for the indent level (it may be equal to self.indent_spaces
403 New value for the indent level (it may be equal to self.indent_spaces
377 if indentation doesn't change.
404 if indentation doesn't change.
378
405
379 full_dedent : boolean
406 full_dedent : boolean
380 Whether the new line causes a full flush-left dedent.
407 Whether the new line causes a full flush-left dedent.
381 """
408 """
382 indent_spaces = self.indent_spaces
409 indent_spaces = self.indent_spaces
383 full_dedent = self._full_dedent
410 full_dedent = self._full_dedent
384
411
385 inisp = num_ini_spaces(line)
412 inisp = num_ini_spaces(line)
386 if inisp < indent_spaces:
413 if inisp < indent_spaces:
387 indent_spaces = inisp
414 indent_spaces = inisp
388 if indent_spaces <= 0:
415 if indent_spaces <= 0:
389 #print 'Full dedent in text',self.source # dbg
416 #print 'Full dedent in text',self.source # dbg
390 full_dedent = True
417 full_dedent = True
391
418
392 if line[-1] == ':':
419 if line[-1] == ':':
393 indent_spaces += 4
420 indent_spaces += 4
394 elif dedent_re.match(line):
421 elif dedent_re.match(line):
395 indent_spaces -= 4
422 indent_spaces -= 4
396 if indent_spaces <= 0:
423 if indent_spaces <= 0:
397 full_dedent = True
424 full_dedent = True
398
425
399 # Safety
426 # Safety
400 if indent_spaces < 0:
427 if indent_spaces < 0:
401 indent_spaces = 0
428 indent_spaces = 0
402 #print 'safety' # dbg
429 #print 'safety' # dbg
403
430
404 return indent_spaces, full_dedent
431 return indent_spaces, full_dedent
405
432
406 def _update_indent(self, lines):
433 def _update_indent(self, lines):
407 for line in remove_comments(lines).splitlines():
434 for line in remove_comments(lines).splitlines():
408 if line and not line.isspace():
435 if line and not line.isspace():
409 self.indent_spaces, self._full_dedent = self._find_indent(line)
436 self.indent_spaces, self._full_dedent = self._find_indent(line)
410
437
411 def _store(self, lines):
438 def _store(self, lines):
412 """Store one or more lines of input.
439 """Store one or more lines of input.
413
440
414 If input lines are not newline-terminated, a newline is automatically
441 If input lines are not newline-terminated, a newline is automatically
415 appended."""
442 appended."""
416
443
417 if lines.endswith('\n'):
444 if lines.endswith('\n'):
418 self._buffer.append(lines)
445 self._buffer.append(lines)
419 else:
446 else:
420 self._buffer.append(lines+'\n')
447 self._buffer.append(lines+'\n')
421 self._set_source()
448 self._set_source()
422
449
423 def _set_source(self):
450 def _set_source(self):
424 self.source = ''.join(self._buffer).encode(self.encoding)
451 self.source = ''.join(self._buffer).encode(self.encoding)
425
452
426
453
427 #-----------------------------------------------------------------------------
454 #-----------------------------------------------------------------------------
428 # IPython-specific syntactic support
455 # Functions and classes for IPython-specific syntactic support
429 #-----------------------------------------------------------------------------
456 #-----------------------------------------------------------------------------
430
457
431 # We implement things, as much as possible, as standalone functions that can be
458 # RegExp for splitting line contents into pre-char//first word-method//rest.
432 # tested and validated in isolation.
459 # For clarity, each group in on one line.
460
461 line_split = re.compile("""
462 ^(\s*) # any leading space
463 ([,;/%]|!!?|\?\??) # escape character or characters
464 \s*([\w\.]*) # function/method part (mix of \w and '.')
465 (\s+.*$|$) # rest of line
466 """, re.VERBOSE)
467
468
469 def split_user_input(line):
470 """Split user input into early whitespace, esc-char, function part and rest.
471
472 This is currently handles lines with '=' in them in a very inconsistent
473 manner.
474
475 Examples
476 ========
477 >>> split_user_input('x=1')
478 ('', '', 'x=1', '')
479 >>> split_user_input('?')
480 ('', '?', '', '')
481 >>> split_user_input('??')
482 ('', '??', '', '')
483 >>> split_user_input(' ?')
484 (' ', '?', '', '')
485 >>> split_user_input(' ??')
486 (' ', '??', '', '')
487 >>> split_user_input('??x')
488 ('', '??', 'x', '')
489 >>> split_user_input('?x=1')
490 ('', '', '?x=1', '')
491 >>> split_user_input('!ls')
492 ('', '!', 'ls', '')
493 >>> split_user_input(' !ls')
494 (' ', '!', 'ls', '')
495 >>> split_user_input('!!ls')
496 ('', '!!', 'ls', '')
497 >>> split_user_input(' !!ls')
498 (' ', '!!', 'ls', '')
499 >>> split_user_input(',ls')
500 ('', ',', 'ls', '')
501 >>> split_user_input(';ls')
502 ('', ';', 'ls', '')
503 >>> split_user_input(' ;ls')
504 (' ', ';', 'ls', '')
505 >>> split_user_input('f.g(x)')
506 ('', '', 'f.g(x)', '')
507 >>> split_user_input('f.g (x)')
508 ('', '', 'f.g', '(x)')
509 """
510 match = line_split.match(line)
511 if match:
512 lspace, esc, fpart, rest = match.groups()
513 else:
514 # print "match failed for line '%s'" % line
515 try:
516 fpart, rest = line.split(None,1)
517 except ValueError:
518 # print "split failed for line '%s'" % line
519 fpart, rest = line,''
520 lspace = re.match('^(\s*)(.*)',line).groups()[0]
521 esc = ''
522
523 # fpart has to be a valid python identifier, so it better be only pure
524 # ascii, no unicode:
525 try:
526 fpart = fpart.encode('ascii')
527 except UnicodeEncodeError:
528 lspace = unicode(lspace)
529 rest = fpart + u' ' + rest
530 fpart = u''
531
532 #print 'line:<%s>' % line # dbg
533 #print 'esc <%s> fpart <%s> rest <%s>' % (esc,fpart.strip(),rest) # dbg
534 return lspace, esc, fpart.strip(), rest.lstrip()
535
536
537 # The escaped translators ALL receive a line where their own escape has been
538 # stripped. Only '?' is valid at the end of the line, all others can only be
539 # placed at the start.
540
541 class LineInfo(object):
542 """A single line of input and associated info.
543
544 This is a utility class that mostly wraps the output of
545 :func:`split_user_input` into a convenient object to be passed around
546 during input transformations.
547
548 Includes the following as properties:
549
550 line
551 The original, raw line
552
553 lspace
554 Any early whitespace before actual text starts.
555
556 esc
557 The initial esc character (or characters, for double-char escapes like
558 '??' or '!!').
559
560 pre_char
561 The escape character(s) in esc or the empty string if there isn't one.
562
563 fpart
564 The 'function part', which is basically the maximal initial sequence
565 of valid python identifiers and the '.' character. This is what is
566 checked for alias and magic transformations, used for auto-calling,
567 etc.
568
569 rest
570 Everything else on the line.
571 """
572 def __init__(self, line):
573 self.line = line
574 self.lspace, self.esc, self.fpart, self.rest = \
575 split_user_input(line)
576
577 def __str__(self):
578 return "LineInfo [%s|%s|%s|%s]" % (self.lspace, self.esc,
579 self.fpart, self.rest)
580
581
582 # Transformations of the special syntaxes that don't rely on an explicit escape
583 # character but instead on patterns on the input line
584
585 # The core transformations are implemented as standalone functions that can be
586 # tested and validated in isolation. Each of these uses a regexp, we
587 # pre-compile these and keep them close to each function definition for clarity
433
588
434 # Each of these uses a regexp, we pre-compile these and keep them close to each
435 # function definition for clarity
436 _assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
589 _assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
437 r'\s*=\s*!\s*(?P<cmd>.*)')
590 r'\s*=\s*!\s*(?P<cmd>.*)')
438
591
439 def transform_assign_system(line):
592 def transform_assign_system(line):
440 """Handle the `files = !ls` syntax."""
593 """Handle the `files = !ls` syntax."""
441 # FIXME: This transforms the line to use %sc, but we've listed that magic
594 # FIXME: This transforms the line to use %sc, but we've listed that magic
442 # as deprecated. We should then implement this functionality in a
595 # as deprecated. We should then implement this functionality in a
443 # standalone api that we can transform to, without going through a
596 # standalone api that we can transform to, without going through a
444 # deprecated magic.
597 # deprecated magic.
445 m = _assign_system_re.match(line)
598 m = _assign_system_re.match(line)
446 if m is not None:
599 if m is not None:
447 cmd = m.group('cmd')
600 cmd = m.group('cmd')
448 lhs = m.group('lhs')
601 lhs = m.group('lhs')
449 expr = make_quoted_expr("sc -l = %s" % cmd)
602 expr = make_quoted_expr("sc -l = %s" % cmd)
450 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
603 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
451 return new_line
604 return new_line
452 return line
605 return line
453
606
454
607
455 _assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
608 _assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
456 r'\s*=\s*%\s*(?P<cmd>.*)')
609 r'\s*=\s*%\s*(?P<cmd>.*)')
457
610
458 def transform_assign_magic(line):
611 def transform_assign_magic(line):
459 """Handle the `a = %who` syntax."""
612 """Handle the `a = %who` syntax."""
460 m = _assign_magic_re.match(line)
613 m = _assign_magic_re.match(line)
461 if m is not None:
614 if m is not None:
462 cmd = m.group('cmd')
615 cmd = m.group('cmd')
463 lhs = m.group('lhs')
616 lhs = m.group('lhs')
464 expr = make_quoted_expr(cmd)
617 expr = make_quoted_expr(cmd)
465 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
618 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
466 return new_line
619 return new_line
467 return line
620 return line
468
621
469
622
470 _classic_prompt_re = re.compile(r'(^[ \t]*>>> |^[ \t]*\.\.\. )')
623 _classic_prompt_re = re.compile(r'^([ \t]*>>> |^[ \t]*\.\.\. )')
471
624
472 def transform_classic_prompt(line):
625 def transform_classic_prompt(line):
473 """Handle inputs that start with '>>> ' syntax."""
626 """Handle inputs that start with '>>> ' syntax."""
474
627
475 if not line or line.isspace() or line.strip() == '...':
628 if not line or line.isspace():
476 # This allows us to recognize multiple input prompts separated by
629 return line
477 # blank lines and pasted in a single chunk, very common when
478 # pasting doctests or long tutorial passages.
479 return ''
480 m = _classic_prompt_re.match(line)
630 m = _classic_prompt_re.match(line)
481 if m:
631 if m:
482 return line[len(m.group(0)):]
632 return line[len(m.group(0)):]
483 else:
633 else:
484 return line
634 return line
485
635
486
636
487 _ipy_prompt_re = re.compile(r'(^[ \t]*In \[\d+\]: |^[ \t]*\ \ \ \.\.\.+: )')
637 _ipy_prompt_re = re.compile(r'^([ \t]*In \[\d+\]: |^[ \t]*\ \ \ \.\.\.+: )')
488
638
489 def transform_ipy_prompt(line):
639 def transform_ipy_prompt(line):
490 """Handle inputs that start classic IPython prompt syntax."""
640 """Handle inputs that start classic IPython prompt syntax."""
491
641
492 if not line or line.isspace() or line.strip() == '...':
642 if not line or line.isspace():
493 # This allows us to recognize multiple input prompts separated by
643 return line
494 # blank lines and pasted in a single chunk, very common when
495 # pasting doctests or long tutorial passages.
496 return ''
497 m = _ipy_prompt_re.match(line)
644 m = _ipy_prompt_re.match(line)
498 if m:
645 if m:
499 return line[len(m.group(0)):]
646 return line[len(m.group(0)):]
500 else:
647 else:
501 return line
648 return line
502
649
503
650
504 # Warning, these cannot be changed unless various regular expressions
651 def transform_unescaped(line):
505 # are updated in a number of places. Not great, but at least we told you.
652 """Transform lines that are explicitly escaped out.
506 ESC_SHELL = '!'
653
507 ESC_SH_CAP = '!!'
654 This calls to the above transform_* functions for the actual line
508 ESC_HELP = '?'
655 translations.
509 ESC_MAGIC = '%'
656
510 ESC_QUOTE = ','
657 Parameters
511 ESC_QUOTE2 = ';'
658 ----------
512 ESC_PAREN = '/'
659 line : str
660 A single line of input to be transformed.
661
662 Returns
663 -------
664 new_line : str
665 Transformed line, which may be identical to the original."""
666
667 if not line or line.isspace():
668 return line
669
670 new_line = line
671 for f in [transform_assign_system, transform_assign_magic,
672 transform_classic_prompt, transform_ipy_prompt ] :
673 new_line = f(new_line)
674 return new_line
675
676 # Support for syntax transformations that use explicit escapes typed by the
677 # user at the beginning of a line
678
679 def tr_system(line_info):
680 "Translate lines escaped with: !"
681 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
682 return '%sget_ipython().system(%s)' % (line_info.lspace,
683 make_quoted_expr(cmd))
684
685
686 def tr_system2(line_info):
687 "Translate lines escaped with: !!"
688 cmd = line_info.line.lstrip()[2:]
689 return '%sget_ipython().getoutput(%s)' % (line_info.lspace,
690 make_quoted_expr(cmd))
691
692
693 def tr_help(line_info):
694 "Translate lines escaped with: ?/??"
695 # A naked help line should just fire the intro help screen
696 if not line_info.line[1:]:
697 return 'get_ipython().show_usage()'
698
699 # There may be one or two '?' at the end, move them to the front so that
700 # the rest of the logic can assume escapes are at the start
701 line = line_info.line
702 if line.endswith('?'):
703 line = line[-1] + line[:-1]
704 if line.endswith('?'):
705 line = line[-1] + line[:-1]
706 line_info = LineInfo(line)
707
708 # From here on, simply choose which level of detail to get.
709 if line_info.esc == '?':
710 pinfo = 'pinfo'
711 elif line_info.esc == '??':
712 pinfo = 'pinfo2'
713
714 tpl = '%sget_ipython().magic("%s %s")'
715 return tpl % (line_info.lspace, pinfo,
716 ' '.join([line_info.fpart, line_info.rest]).strip())
717
718
719 def tr_magic(line_info):
720 "Translate lines escaped with: %"
721 tpl = '%sget_ipython().magic(%s)'
722 cmd = make_quoted_expr(' '.join([line_info.fpart,
723 line_info.rest])).strip()
724 return tpl % (line_info.lspace, cmd)
725
726
727 def tr_quote(line_info):
728 "Translate lines escaped with: ,"
729 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
730 '", "'.join(line_info.rest.split()) )
731
732
733 def tr_quote2(line_info):
734 "Translate lines escaped with: ;"
735 return '%s%s("%s")' % (line_info.lspace, line_info.fpart,
736 line_info.rest)
737
738
739 def tr_paren(line_info):
740 "Translate lines escaped with: /"
741 return '%s%s(%s)' % (line_info.lspace, line_info.fpart,
742 ", ".join(line_info.rest.split()))
743
744
745 def transform_escaped(line):
746 """Transform lines that are explicitly escaped out.
747
748 This calls to the above tr_* functions for the actual line translations."""
749
750 tr = { ESC_SHELL : tr_system,
751 ESC_SH_CAP : tr_system2,
752 ESC_HELP : tr_help,
753 ESC_HELP2 : tr_help,
754 ESC_MAGIC : tr_magic,
755 ESC_QUOTE : tr_quote,
756 ESC_QUOTE2 : tr_quote2,
757 ESC_PAREN : tr_paren }
758
759 # Empty lines just get returned unmodified
760 if not line or line.isspace():
761 return line
762
763 # Get line endpoints, where the escapes can be
764 line_info = LineInfo(line)
765
766 # If the escape is not at the start, only '?' needs to be special-cased.
767 # All other escapes are only valid at the start
768 if not line_info.esc in tr:
769 if line.endswith(ESC_HELP):
770 return tr_help(line_info)
771 else:
772 # If we don't recognize the escape, don't modify the line
773 return line
774
775 return tr[line_info.esc](line_info)
776
513
777
514 class IPythonInputSplitter(InputSplitter):
778 class IPythonInputSplitter(InputSplitter):
515 """An input splitter that recognizes all of IPython's special syntax."""
779 """An input splitter that recognizes all of IPython's special syntax."""
516
780
517
518 def push(self, lines):
781 def push(self, lines):
519 """Push one or more lines of IPython input.
782 """Push one or more lines of IPython input.
520 """
783 """
521 return super(IPythonInputSplitter, self).push(lines)
784 # We only apply the line transformers to the input if we have either no
785 # input yet, or complete input. This prevents the accidental
786 # transformation of escapes inside multiline expressions like
787 # triple-quoted strings or parenthesized expressions.
788 lines_list = lines.splitlines()
789 if self._is_complete or not self._buffer:
790
791 new_list = map(transform_escaped, lines_list)
792 else:
793 new_list = lines_list
794
795 # Now apply the unescaped transformations to each input line
796 new_list = map(transform_unescaped, new_list)
797 newlines = '\n'.join(new_list)
798 return super(IPythonInputSplitter, self).push(newlines)
@@ -1,411 +1,623 b''
1 # -*- coding: utf-8 -*-
1 """Tests for the inputsplitter module.
2 """Tests for the inputsplitter module.
2 """
3 """
3 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
4 # Copyright (C) 2010 The IPython Development Team
5 # Copyright (C) 2010 The IPython Development Team
5 #
6 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
9
10
10 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
11 # Imports
12 # Imports
12 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
13 # stdlib
14 # stdlib
14 import unittest
15 import unittest
15 import sys
16 import sys
16
17
17 # Third party
18 # Third party
18 import nose.tools as nt
19 import nose.tools as nt
19
20
20 # Our own
21 # Our own
21 from IPython.core import inputsplitter as isp
22 from IPython.core import inputsplitter as isp
22
23
23 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
24 # Semi-complete examples (also used as tests)
25 # Semi-complete examples (also used as tests)
25 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
28 # Note: at the bottom, there's a slightly more complete version of this that
29 # can be useful during development of code here.
30
26 def mini_interactive_loop(raw_input):
31 def mini_interactive_loop(raw_input):
27 """Minimal example of the logic of an interactive interpreter loop.
32 """Minimal example of the logic of an interactive interpreter loop.
28
33
29 This serves as an example, and it is used by the test system with a fake
34 This serves as an example, and it is used by the test system with a fake
30 raw_input that simulates interactive input."""
35 raw_input that simulates interactive input."""
31
36
32 from IPython.core.inputsplitter import InputSplitter
37 from IPython.core.inputsplitter import InputSplitter
33
38
34 isp = InputSplitter()
39 isp = InputSplitter()
35 # In practice, this input loop would be wrapped in an outside loop to read
40 # In practice, this input loop would be wrapped in an outside loop to read
36 # input indefinitely, until some exit/quit command was issued. Here we
41 # input indefinitely, until some exit/quit command was issued. Here we
37 # only illustrate the basic inner loop.
42 # only illustrate the basic inner loop.
38 while isp.push_accepts_more():
43 while isp.push_accepts_more():
39 indent = ' '*isp.indent_spaces
44 indent = ' '*isp.indent_spaces
40 prompt = '>>> ' + indent
45 prompt = '>>> ' + indent
41 line = indent + raw_input(prompt)
46 line = indent + raw_input(prompt)
42 isp.push(line)
47 isp.push(line)
43
48
44 # Here we just return input so we can use it in a test suite, but a real
49 # Here we just return input so we can use it in a test suite, but a real
45 # interpreter would instead send it for execution somewhere.
50 # interpreter would instead send it for execution somewhere.
46 src = isp.source_reset()
51 src = isp.source_reset()
47 print 'Input source was:\n', src
52 #print 'Input source was:\n', src # dbg
48 return src
53 return src
49
54
50 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
51 # Test utilities, just for local use
56 # Test utilities, just for local use
52 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
53
58
54 def assemble(block):
59 def assemble(block):
55 """Assemble a block into multi-line sub-blocks."""
60 """Assemble a block into multi-line sub-blocks."""
56 return ['\n'.join(sub_block)+'\n' for sub_block in block]
61 return ['\n'.join(sub_block)+'\n' for sub_block in block]
57
62
58
63
59 def pseudo_input(lines):
64 def pseudo_input(lines):
60 """Return a function that acts like raw_input but feeds the input list."""
65 """Return a function that acts like raw_input but feeds the input list."""
61 ilines = iter(lines)
66 ilines = iter(lines)
62 def raw_in(prompt):
67 def raw_in(prompt):
63 try:
68 try:
64 return next(ilines)
69 return next(ilines)
65 except StopIteration:
70 except StopIteration:
66 return ''
71 return ''
67 return raw_in
72 return raw_in
68
73
69 #-----------------------------------------------------------------------------
74 #-----------------------------------------------------------------------------
70 # Tests
75 # Tests
71 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
72 def test_spaces():
77 def test_spaces():
73 tests = [('', 0),
78 tests = [('', 0),
74 (' ', 1),
79 (' ', 1),
75 ('\n', 0),
80 ('\n', 0),
76 (' \n', 1),
81 (' \n', 1),
77 ('x', 0),
82 ('x', 0),
78 (' x', 1),
83 (' x', 1),
79 (' x',2),
84 (' x',2),
80 (' x',4),
85 (' x',4),
81 # Note: tabs are counted as a single whitespace!
86 # Note: tabs are counted as a single whitespace!
82 ('\tx', 1),
87 ('\tx', 1),
83 ('\t x', 2),
88 ('\t x', 2),
84 ]
89 ]
85
90
86 for s, nsp in tests:
91 for s, nsp in tests:
87 nt.assert_equal(isp.num_ini_spaces(s), nsp)
92 nt.assert_equal(isp.num_ini_spaces(s), nsp)
88
93
89
94
90 def test_remove_comments():
95 def test_remove_comments():
91 tests = [('text', 'text'),
96 tests = [('text', 'text'),
92 ('text # comment', 'text '),
97 ('text # comment', 'text '),
93 ('text # comment\n', 'text \n'),
98 ('text # comment\n', 'text \n'),
94 ('text # comment \n', 'text \n'),
99 ('text # comment \n', 'text \n'),
95 ('line # c \nline\n','line \nline\n'),
100 ('line # c \nline\n','line \nline\n'),
96 ('line # c \nline#c2 \nline\nline #c\n\n',
101 ('line # c \nline#c2 \nline\nline #c\n\n',
97 'line \nline\nline\nline \n\n'),
102 'line \nline\nline\nline \n\n'),
98 ]
103 ]
99
104
100 for inp, out in tests:
105 for inp, out in tests:
101 nt.assert_equal(isp.remove_comments(inp), out)
106 nt.assert_equal(isp.remove_comments(inp), out)
102
107
103
108
104 def test_get_input_encoding():
109 def test_get_input_encoding():
105 encoding = isp.get_input_encoding()
110 encoding = isp.get_input_encoding()
106 nt.assert_true(isinstance(encoding, basestring))
111 nt.assert_true(isinstance(encoding, basestring))
107 # simple-minded check that at least encoding a simple string works with the
112 # simple-minded check that at least encoding a simple string works with the
108 # encoding we got.
113 # encoding we got.
109 nt.assert_equal('test'.encode(encoding), 'test')
114 nt.assert_equal('test'.encode(encoding), 'test')
110
115
111
116
112 class NoInputEncodingTestCase(unittest.TestCase):
117 class NoInputEncodingTestCase(unittest.TestCase):
113 def setUp(self):
118 def setUp(self):
114 self.old_stdin = sys.stdin
119 self.old_stdin = sys.stdin
115 class X: pass
120 class X: pass
116 fake_stdin = X()
121 fake_stdin = X()
117 sys.stdin = fake_stdin
122 sys.stdin = fake_stdin
118
123
119 def test(self):
124 def test(self):
120 # Verify that if sys.stdin has no 'encoding' attribute we do the right
125 # Verify that if sys.stdin has no 'encoding' attribute we do the right
121 # thing
126 # thing
122 enc = isp.get_input_encoding()
127 enc = isp.get_input_encoding()
123 self.assertEqual(enc, 'ascii')
128 self.assertEqual(enc, 'ascii')
124
129
125 def tearDown(self):
130 def tearDown(self):
126 sys.stdin = self.old_stdin
131 sys.stdin = self.old_stdin
127
132
128
133
129 class InputSplitterTestCase(unittest.TestCase):
134 class InputSplitterTestCase(unittest.TestCase):
130 def setUp(self):
135 def setUp(self):
131 self.isp = isp.InputSplitter()
136 self.isp = isp.InputSplitter()
132
137
133 def test_reset(self):
138 def test_reset(self):
134 isp = self.isp
139 isp = self.isp
135 isp.push('x=1')
140 isp.push('x=1')
136 isp.reset()
141 isp.reset()
137 self.assertEqual(isp._buffer, [])
142 self.assertEqual(isp._buffer, [])
138 self.assertEqual(isp.indent_spaces, 0)
143 self.assertEqual(isp.indent_spaces, 0)
139 self.assertEqual(isp.source, '')
144 self.assertEqual(isp.source, '')
140 self.assertEqual(isp.code, None)
145 self.assertEqual(isp.code, None)
141 self.assertEqual(isp._is_complete, False)
146 self.assertEqual(isp._is_complete, False)
142
147
143 def test_source(self):
148 def test_source(self):
144 self.isp._store('1')
149 self.isp._store('1')
145 self.isp._store('2')
150 self.isp._store('2')
146 self.assertEqual(self.isp.source, '1\n2\n')
151 self.assertEqual(self.isp.source, '1\n2\n')
147 self.assertTrue(len(self.isp._buffer)>0)
152 self.assertTrue(len(self.isp._buffer)>0)
148 self.assertEqual(self.isp.source_reset(), '1\n2\n')
153 self.assertEqual(self.isp.source_reset(), '1\n2\n')
149 self.assertEqual(self.isp._buffer, [])
154 self.assertEqual(self.isp._buffer, [])
150 self.assertEqual(self.isp.source, '')
155 self.assertEqual(self.isp.source, '')
151
156
152 def test_indent(self):
157 def test_indent(self):
153 isp = self.isp # shorthand
158 isp = self.isp # shorthand
154 isp.push('x=1')
159 isp.push('x=1')
155 self.assertEqual(isp.indent_spaces, 0)
160 self.assertEqual(isp.indent_spaces, 0)
156 isp.push('if 1:\n x=1')
161 isp.push('if 1:\n x=1')
157 self.assertEqual(isp.indent_spaces, 4)
162 self.assertEqual(isp.indent_spaces, 4)
158 isp.push('y=2\n')
163 isp.push('y=2\n')
159 self.assertEqual(isp.indent_spaces, 0)
164 self.assertEqual(isp.indent_spaces, 0)
160 isp.push('if 1:')
165 isp.push('if 1:')
161 self.assertEqual(isp.indent_spaces, 4)
166 self.assertEqual(isp.indent_spaces, 4)
162 isp.push(' x=1')
167 isp.push(' x=1')
163 self.assertEqual(isp.indent_spaces, 4)
168 self.assertEqual(isp.indent_spaces, 4)
164 # Blank lines shouldn't change the indent level
169 # Blank lines shouldn't change the indent level
165 isp.push(' '*2)
170 isp.push(' '*2)
166 self.assertEqual(isp.indent_spaces, 4)
171 self.assertEqual(isp.indent_spaces, 4)
167
172
168 def test_indent2(self):
173 def test_indent2(self):
169 isp = self.isp
174 isp = self.isp
170 # When a multiline statement contains parens or multiline strings, we
175 # When a multiline statement contains parens or multiline strings, we
171 # shouldn't get confused.
176 # shouldn't get confused.
172 isp.push("if 1:")
177 isp.push("if 1:")
173 isp.push(" x = (1+\n 2)")
178 isp.push(" x = (1+\n 2)")
174 self.assertEqual(isp.indent_spaces, 4)
179 self.assertEqual(isp.indent_spaces, 4)
175
180
176 def test_dedent(self):
181 def test_dedent(self):
177 isp = self.isp # shorthand
182 isp = self.isp # shorthand
178 isp.push('if 1:')
183 isp.push('if 1:')
179 self.assertEqual(isp.indent_spaces, 4)
184 self.assertEqual(isp.indent_spaces, 4)
180 isp.push(' pass')
185 isp.push(' pass')
181 self.assertEqual(isp.indent_spaces, 0)
186 self.assertEqual(isp.indent_spaces, 0)
182
187
183 def test_push(self):
188 def test_push(self):
184 isp = self.isp
189 isp = self.isp
185 self.assertTrue(isp.push('x=1'))
190 self.assertTrue(isp.push('x=1'))
186
191
187 def test_push2(self):
192 def test_push2(self):
188 isp = self.isp
193 isp = self.isp
189 self.assertFalse(isp.push('if 1:'))
194 self.assertFalse(isp.push('if 1:'))
190 for line in [' x=1', '# a comment', ' y=2']:
195 for line in [' x=1', '# a comment', ' y=2']:
191 self.assertTrue(isp.push(line))
196 self.assertTrue(isp.push(line))
192
197
193 def test_push3(self):
198 def test_push3(self):
194 """Test input with leading whitespace"""
199 """Test input with leading whitespace"""
195 isp = self.isp
200 isp = self.isp
196 isp.push(' x=1')
201 isp.push(' x=1')
197 isp.push(' y=2')
202 isp.push(' y=2')
198 self.assertEqual(isp.source, 'if 1:\n x=1\n y=2\n')
203 self.assertEqual(isp.source, 'if 1:\n x=1\n y=2\n')
199
204
200 def test_replace_mode(self):
205 def test_replace_mode(self):
201 isp = self.isp
206 isp = self.isp
202 isp.input_mode = 'replace'
207 isp.input_mode = 'replace'
203 isp.push('x=1')
208 isp.push('x=1')
204 self.assertEqual(isp.source, 'x=1\n')
209 self.assertEqual(isp.source, 'x=1\n')
205 isp.push('x=2')
210 isp.push('x=2')
206 self.assertEqual(isp.source, 'x=2\n')
211 self.assertEqual(isp.source, 'x=2\n')
207
212
208 def test_push_accepts_more(self):
213 def test_push_accepts_more(self):
209 isp = self.isp
214 isp = self.isp
210 isp.push('x=1')
215 isp.push('x=1')
211 self.assertFalse(isp.push_accepts_more())
216 self.assertFalse(isp.push_accepts_more())
212
217
213 def test_push_accepts_more2(self):
218 def test_push_accepts_more2(self):
214 isp = self.isp
219 isp = self.isp
215 isp.push('if 1:')
220 isp.push('if 1:')
216 self.assertTrue(isp.push_accepts_more())
221 self.assertTrue(isp.push_accepts_more())
217 isp.push(' x=1')
222 isp.push(' x=1')
218 self.assertTrue(isp.push_accepts_more())
223 self.assertTrue(isp.push_accepts_more())
219 isp.push('')
224 isp.push('')
220 self.assertFalse(isp.push_accepts_more())
225 self.assertFalse(isp.push_accepts_more())
221
226
222 def test_push_accepts_more3(self):
227 def test_push_accepts_more3(self):
223 isp = self.isp
228 isp = self.isp
224 isp.push("x = (2+\n3)")
229 isp.push("x = (2+\n3)")
225 self.assertFalse(isp.push_accepts_more())
230 self.assertFalse(isp.push_accepts_more())
226
231
227 def test_push_accepts_more4(self):
232 def test_push_accepts_more4(self):
228 isp = self.isp
233 isp = self.isp
229 # When a multiline statement contains parens or multiline strings, we
234 # When a multiline statement contains parens or multiline strings, we
230 # shouldn't get confused.
235 # shouldn't get confused.
231 # FIXME: we should be able to better handle de-dents in statements like
236 # FIXME: we should be able to better handle de-dents in statements like
232 # multiline strings and multiline expressions (continued with \ or
237 # multiline strings and multiline expressions (continued with \ or
233 # parens). Right now we aren't handling the indentation tracking quite
238 # parens). Right now we aren't handling the indentation tracking quite
234 # correctly with this, though in practice it may not be too much of a
239 # correctly with this, though in practice it may not be too much of a
235 # problem. We'll need to see.
240 # problem. We'll need to see.
236 isp.push("if 1:")
241 isp.push("if 1:")
237 isp.push(" x = (2+")
242 isp.push(" x = (2+")
238 isp.push(" 3)")
243 isp.push(" 3)")
239 self.assertTrue(isp.push_accepts_more())
244 self.assertTrue(isp.push_accepts_more())
240 isp.push(" y = 3")
245 isp.push(" y = 3")
241 self.assertTrue(isp.push_accepts_more())
246 self.assertTrue(isp.push_accepts_more())
242 isp.push('')
247 isp.push('')
243 self.assertFalse(isp.push_accepts_more())
248 self.assertFalse(isp.push_accepts_more())
244
249
245 def test_syntax_error(self):
250 def test_syntax_error(self):
246 isp = self.isp
251 isp = self.isp
247 # Syntax errors immediately produce a 'ready' block, so the invalid
252 # Syntax errors immediately produce a 'ready' block, so the invalid
248 # Python can be sent to the kernel for evaluation with possible ipython
253 # Python can be sent to the kernel for evaluation with possible ipython
249 # special-syntax conversion.
254 # special-syntax conversion.
250 isp.push('run foo')
255 isp.push('run foo')
251 self.assertFalse(isp.push_accepts_more())
256 self.assertFalse(isp.push_accepts_more())
252
257
253 def check_split(self, block_lines, compile=True):
258 def check_split(self, block_lines, compile=True):
254 blocks = assemble(block_lines)
259 blocks = assemble(block_lines)
255 lines = ''.join(blocks)
260 lines = ''.join(blocks)
256 oblock = self.isp.split_blocks(lines)
261 oblock = self.isp.split_blocks(lines)
257 self.assertEqual(oblock, blocks)
262 self.assertEqual(oblock, blocks)
258 if compile:
263 if compile:
259 for block in blocks:
264 for block in blocks:
260 self.isp._compile(block)
265 self.isp._compile(block)
261
266
262 def test_split(self):
267 def test_split(self):
263 # All blocks of input we want to test in a list. The format for each
268 # All blocks of input we want to test in a list. The format for each
264 # block is a list of lists, with each inner lists consisting of all the
269 # block is a list of lists, with each inner lists consisting of all the
265 # lines (as single-lines) that should make up a sub-block.
270 # lines (as single-lines) that should make up a sub-block.
266
271
267 # Note: do NOT put here sub-blocks that don't compile, as the
272 # Note: do NOT put here sub-blocks that don't compile, as the
268 # check_split() routine makes a final verification pass to check that
273 # check_split() routine makes a final verification pass to check that
269 # each sub_block, as returned by split_blocks(), does compile
274 # each sub_block, as returned by split_blocks(), does compile
270 # correctly.
275 # correctly.
271 all_blocks = [ [['x=1']],
276 all_blocks = [ [['x=1']],
272
277
273 [['x=1'],
278 [['x=1'],
274 ['y=2']],
279 ['y=2']],
275
280
276 [['x=1'],
281 [['x=1'],
277 ['# a comment'],
282 ['# a comment'],
278 ['y=11']],
283 ['y=11']],
279
284
280 [['if 1:',
285 [['if 1:',
281 ' x=1'],
286 ' x=1'],
282 ['y=3']],
287 ['y=3']],
283
288
284 [['def f(x):',
289 [['def f(x):',
285 ' return x'],
290 ' return x'],
286 ['x=1']],
291 ['x=1']],
287
292
288 [['def f(x):',
293 [['def f(x):',
289 ' x+=1',
294 ' x+=1',
290 ' ',
295 ' ',
291 ' return x'],
296 ' return x'],
292 ['x=1']],
297 ['x=1']],
293
298
294 [['def f(x):',
299 [['def f(x):',
295 ' if x>0:',
300 ' if x>0:',
296 ' y=1',
301 ' y=1',
297 ' # a comment',
302 ' # a comment',
298 ' else:',
303 ' else:',
299 ' y=4',
304 ' y=4',
300 ' ',
305 ' ',
301 ' return y'],
306 ' return y'],
302 ['x=1'],
307 ['x=1'],
303 ['if 1:',
308 ['if 1:',
304 ' y=11'] ],
309 ' y=11'] ],
305
310
306 [['for i in range(10):'
311 [['for i in range(10):'
307 ' x=i**2']],
312 ' x=i**2']],
308
313
309 [['for i in range(10):'
314 [['for i in range(10):'
310 ' x=i**2'],
315 ' x=i**2'],
311 ['z = 1']],
316 ['z = 1']],
312 ]
317 ]
313 for block_lines in all_blocks:
318 for block_lines in all_blocks:
314 self.check_split(block_lines)
319 self.check_split(block_lines)
315
320
316 def test_split_syntax_errors(self):
321 def test_split_syntax_errors(self):
317 # Block splitting with invalid syntax
322 # Block splitting with invalid syntax
318 all_blocks = [ [['a syntax error']],
323 all_blocks = [ [['a syntax error']],
319
324
320 [['x=1'],
325 [['x=1'],
321 ['a syntax error']],
326 ['a syntax error']],
322
327
323 [['for i in range(10):'
328 [['for i in range(10):'
324 ' an error']],
329 ' an error']],
325
330
326 ]
331 ]
327 for block_lines in all_blocks:
332 for block_lines in all_blocks:
328 self.check_split(block_lines, compile=False)
333 self.check_split(block_lines, compile=False)
329
334
330
335
331 class InteractiveLoopTestCase(unittest.TestCase):
336 class InteractiveLoopTestCase(unittest.TestCase):
332 """Tests for an interactive loop like a python shell.
337 """Tests for an interactive loop like a python shell.
333 """
338 """
334 def check_ns(self, lines, ns):
339 def check_ns(self, lines, ns):
335 """Validate that the given input lines produce the resulting namespace.
340 """Validate that the given input lines produce the resulting namespace.
336
341
337 Note: the input lines are given exactly as they would be typed in an
342 Note: the input lines are given exactly as they would be typed in an
338 auto-indenting environment, as mini_interactive_loop above already does
343 auto-indenting environment, as mini_interactive_loop above already does
339 auto-indenting and prepends spaces to the input.
344 auto-indenting and prepends spaces to the input.
340 """
345 """
341 src = mini_interactive_loop(pseudo_input(lines))
346 src = mini_interactive_loop(pseudo_input(lines))
342 test_ns = {}
347 test_ns = {}
343 exec src in test_ns
348 exec src in test_ns
344 # We can't check that the provided ns is identical to the test_ns,
349 # We can't check that the provided ns is identical to the test_ns,
345 # because Python fills test_ns with extra keys (copyright, etc). But
350 # because Python fills test_ns with extra keys (copyright, etc). But
346 # we can check that the given dict is *contained* in test_ns
351 # we can check that the given dict is *contained* in test_ns
347 for k,v in ns.items():
352 for k,v in ns.items():
348 self.assertEqual(test_ns[k], v)
353 self.assertEqual(test_ns[k], v)
349
354
350 def test_simple(self):
355 def test_simple(self):
351 self.check_ns(['x=1'], dict(x=1))
356 self.check_ns(['x=1'], dict(x=1))
352
357
353 def test_simple2(self):
358 def test_simple2(self):
354 self.check_ns(['if 1:', 'x=2'], dict(x=2))
359 self.check_ns(['if 1:', 'x=2'], dict(x=2))
355
360
356 def test_xy(self):
361 def test_xy(self):
357 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
362 self.check_ns(['x=1; y=2'], dict(x=1, y=2))
358
363
359 def test_abc(self):
364 def test_abc(self):
360 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
365 self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3))
361
366
362 def test_multi(self):
367 def test_multi(self):
363 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
368 self.check_ns(['x =(1+','1+','2)'], dict(x=4))
364
369
365
370
366 class IPythonInputTestCase(InputSplitterTestCase):
371 def test_LineInfo():
367 def setUp(self):
372 """Simple test for LineInfo construction and str()"""
368 self.isp = isp.IPythonInputSplitter()
373 linfo = isp.LineInfo(' %cd /home')
374 nt.assert_equals(str(linfo), 'LineInfo [ |%|cd|/home]')
375
376
377 def test_split_user_input():
378 """Unicode test - split_user_input already has good doctests"""
379 line = u"PΓ©rez Fernando"
380 parts = isp.split_user_input(line)
381 parts_expected = (u'', u'', u'', line)
382 nt.assert_equal(parts, parts_expected)
369
383
370
384
371 # Transformer tests
385 # Transformer tests
372 def transform_checker(tests, func):
386 def transform_checker(tests, func):
373 """Utility to loop over test inputs"""
387 """Utility to loop over test inputs"""
374 for inp, tr in tests:
388 for inp, tr in tests:
375 nt.assert_equals(func(inp), tr)
389 nt.assert_equals(func(inp), tr)
376
390
391 # Data for all the syntax tests in the form of lists of pairs of
392 # raw/transformed input. We store it here as a global dict so that we can use
393 # it both within single-function tests and also to validate the behavior of the
394 # larger objects
395
396 syntax = \
397 dict(assign_system =
398 [('a =! ls', 'a = get_ipython().magic("sc -l = ls")'),
399 ('b = !ls', 'b = get_ipython().magic("sc -l = ls")'),
400 ('x=1', 'x=1'), # normal input is unmodified
401 (' ',' '), # blank lines are kept intact
402 ],
403
404 assign_magic =
405 [('a =% who', 'a = get_ipython().magic("who")'),
406 ('b = %who', 'b = get_ipython().magic("who")'),
407 ('x=1', 'x=1'), # normal input is unmodified
408 (' ',' '), # blank lines are kept intact
409 ],
410
411 classic_prompt =
412 [('>>> x=1', 'x=1'),
413 ('x=1', 'x=1'), # normal input is unmodified
414 (' ',' '), # blank lines are kept intact
415 ],
416
417 ipy_prompt =
418 [('In [1]: x=1', 'x=1'),
419 ('x=1', 'x=1'), # normal input is unmodified
420 (' ',' '), # blank lines are kept intact
421 ],
422
423 # Tests for the escape transformer to leave normal code alone
424 escaped_noesc =
425 [ (' ', ' '),
426 ('x=1', 'x=1'),
427 ],
428
429 # System calls
430 escaped_shell =
431 [ ('!ls', 'get_ipython().system("ls")'),
432 # Double-escape shell, this means to capture the output of the
433 # subprocess and return it
434 ('!!ls', 'get_ipython().getoutput("ls")'),
435 ],
436
437 # Help/object info
438 escaped_help =
439 [ ('?', 'get_ipython().show_usage()'),
440 ('?x1', 'get_ipython().magic("pinfo x1")'),
441 ('??x2', 'get_ipython().magic("pinfo2 x2")'),
442 ('x3?', 'get_ipython().magic("pinfo x3")'),
443 ('x4??', 'get_ipython().magic("pinfo2 x4")'),
444 ],
445
446 # Explicit magic calls
447 escaped_magic =
448 [ ('%cd', 'get_ipython().magic("cd")'),
449 ('%cd /home', 'get_ipython().magic("cd /home")'),
450 (' %magic', ' get_ipython().magic("magic")'),
451 ],
452
453 # Quoting with separate arguments
454 escaped_quote =
455 [ (',f', 'f("")'),
456 (',f x', 'f("x")'),
457 (' ,f y', ' f("y")'),
458 (',f a b', 'f("a", "b")'),
459 ],
460
461 # Quoting with single argument
462 escaped_quote2 =
463 [ (';f', 'f("")'),
464 (';f x', 'f("x")'),
465 (' ;f y', ' f("y")'),
466 (';f a b', 'f("a b")'),
467 ],
468
469 # Simply apply parens
470 escaped_paren =
471 [ ('/f', 'f()'),
472 ('/f x', 'f(x)'),
473 (' /f y', ' f(y)'),
474 ('/f a b', 'f(a, b)'),
475 ],
476
477 # More complex multiline tests
478 ## escaped_multiline =
479 ## [()],
480 )
481
482 # multiline syntax examples. Each of these should be a list of lists, with
483 # each entry itself having pairs of raw/transformed input. The union (with
484 # '\n'.join() of the transformed inputs is what the splitter should produce
485 # when fed the raw lines one at a time via push.
486 syntax_ml = \
487 dict(classic_prompt =
488 [ [('>>> for i in range(10):','for i in range(10):'),
489 ('... print i',' print i'),
490 ('... ', ''),
491 ],
492 ],
493
494 ipy_prompt =
495 [ [('In [24]: for i in range(10):','for i in range(10):'),
496 (' ....: print i',' print i'),
497 (' ....: ', ''),
498 ],
499 ],
500 )
501
377
502
378 def test_assign_system():
503 def test_assign_system():
379 tests = [('a =! ls', 'a = get_ipython().magic("sc -l = ls")'),
504 transform_checker(syntax['assign_system'], isp.transform_assign_system)
380 ('b = !ls', 'b = get_ipython().magic("sc -l = ls")'),
381 ('x=1','x=1')]
382 transform_checker(tests, isp.transform_assign_system)
383
505
384
506
385 def test_assign_magic():
507 def test_assign_magic():
386 tests = [('a =% who', 'a = get_ipython().magic("who")'),
508 transform_checker(syntax['assign_magic'], isp.transform_assign_magic)
387 ('b = %who', 'b = get_ipython().magic("who")'),
388 ('x=1','x=1')]
389 transform_checker(tests, isp.transform_assign_magic)
390
509
391
510
392 def test_classic_prompt():
511 def test_classic_prompt():
393 tests = [('>>> x=1', 'x=1'),
512 transform_checker(syntax['classic_prompt'], isp.transform_classic_prompt)
394 ('>>> for i in range(10):','for i in range(10):'),
513 for example in syntax_ml['classic_prompt']:
395 ('... print i',' print i'),
514 transform_checker(example, isp.transform_classic_prompt)
396 ('...', ''),
397 ('x=1','x=1')
398 ]
399 transform_checker(tests, isp.transform_classic_prompt)
400
515
401
516
402 def test_ipy_prompt():
517 def test_ipy_prompt():
403 tests = [('In [1]: x=1', 'x=1'),
518 transform_checker(syntax['ipy_prompt'], isp.transform_ipy_prompt)
404 ('In [24]: for i in range(10):','for i in range(10):'),
519 for example in syntax_ml['ipy_prompt']:
405 (' ....: print i',' print i'),
520 transform_checker(example, isp.transform_ipy_prompt)
406 (' ....: ', ''),
521
407 ('x=1', 'x=1'), # normal input is unmodified
522
408 (' ','') # blank lines are just collapsed
523 def test_escaped_noesc():
409 ]
524 transform_checker(syntax['escaped_noesc'], isp.transform_escaped)
410 transform_checker(tests, isp.transform_ipy_prompt)
525
526
527 def test_escaped_shell():
528 transform_checker(syntax['escaped_shell'], isp.transform_escaped)
529
530
531 def test_escaped_help():
532 transform_checker(syntax['escaped_help'], isp.transform_escaped)
533
534
535 def test_escaped_magic():
536 transform_checker(syntax['escaped_magic'], isp.transform_escaped)
537
538
539 def test_escaped_quote():
540 transform_checker(syntax['escaped_quote'], isp.transform_escaped)
541
542
543 def test_escaped_quote2():
544 transform_checker(syntax['escaped_quote2'], isp.transform_escaped)
545
546
547 def test_escaped_paren():
548 transform_checker(syntax['escaped_paren'], isp.transform_escaped)
549
550
551 class IPythonInputTestCase(InputSplitterTestCase):
552 """By just creating a new class whose .isp is a different instance, we
553 re-run the same test battery on the new input splitter.
554
555 In addition, this runs the tests over the syntax and syntax_ml dicts that
556 were tested by individual functions, as part of the OO interface.
557 """
558 def setUp(self):
559 self.isp = isp.IPythonInputSplitter()
560
561 def test_syntax(self):
562 """Call all single-line syntax tests from the main object"""
563 isp = self.isp
564 for example in syntax.itervalues():
565 for raw, out_t in example:
566 if raw.startswith(' '):
567 continue
568
569 isp.push(raw)
570 out = isp.source_reset().rstrip()
571 self.assertEqual(out, out_t)
572
573 def test_syntax_multiline(self):
574 isp = self.isp
575 for example in syntax_ml.itervalues():
576 out_t_parts = []
577 for line_pairs in example:
578 for raw, out_t_part in line_pairs:
579 isp.push(raw)
580 out_t_parts.append(out_t_part)
581
582 out = isp.source_reset().rstrip()
583 out_t = '\n'.join(out_t_parts).rstrip()
584 self.assertEqual(out, out_t)
585
586
587 #-----------------------------------------------------------------------------
588 # Main - use as a script
589 #-----------------------------------------------------------------------------
590
591 if __name__ == '__main__':
592 # A simple demo for interactive experimentation. This code will not get
593 # picked up by any test suite. Useful mostly for illustration and during
594 # development.
595 from IPython.core.inputsplitter import InputSplitter, IPythonInputSplitter
411
596
597 #isp, start_prompt = InputSplitter(), '>>> '
598 isp, start_prompt = IPythonInputSplitter(), 'In> '
599
600 autoindent = True
601 #autoindent = False
602
603 # In practice, this input loop would be wrapped in an outside loop to read
604 # input indefinitely, until some exit/quit command was issued. Here we
605 # only illustrate the basic inner loop.
606 try:
607 while True:
608 prompt = start_prompt
609 while isp.push_accepts_more():
610 indent = ' '*isp.indent_spaces
611 if autoindent:
612 line = indent + raw_input(prompt+indent)
613 else:
614 line = raw_input(prompt)
615 isp.push(line)
616 prompt = '... '
617
618 # Here we just return input so we can use it in a test suite, but a
619 # real interpreter would instead send it for execution somewhere.
620 src = isp.source_reset()
621 print 'Input source was:\n', src # dbg
622 except EOFError:
623 print 'Bye'
@@ -1,473 +1,474 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for working with strings and text.
3 Utilities for working with strings and text.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import __main__
17 import __main__
18
18
19 import os
19 import os
20 import re
20 import re
21 import shutil
21 import shutil
22 import types
22 import types
23
23
24 from IPython.external.path import path
24 from IPython.external.path import path
25
25
26 from IPython.utils.generics import result_display
26 from IPython.utils.generics import result_display
27 from IPython.utils.io import nlprint
27 from IPython.utils.io import nlprint
28 from IPython.utils.data import flatten
28 from IPython.utils.data import flatten
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Code
31 # Code
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 StringTypes = types.StringTypes
34 StringTypes = types.StringTypes
35
35
36
36
37 def unquote_ends(istr):
37 def unquote_ends(istr):
38 """Remove a single pair of quotes from the endpoints of a string."""
38 """Remove a single pair of quotes from the endpoints of a string."""
39
39
40 if not istr:
40 if not istr:
41 return istr
41 return istr
42 if (istr[0]=="'" and istr[-1]=="'") or \
42 if (istr[0]=="'" and istr[-1]=="'") or \
43 (istr[0]=='"' and istr[-1]=='"'):
43 (istr[0]=='"' and istr[-1]=='"'):
44 return istr[1:-1]
44 return istr[1:-1]
45 else:
45 else:
46 return istr
46 return istr
47
47
48
48
49 class LSString(str):
49 class LSString(str):
50 """String derivative with a special access attributes.
50 """String derivative with a special access attributes.
51
51
52 These are normal strings, but with the special attributes:
52 These are normal strings, but with the special attributes:
53
53
54 .l (or .list) : value as list (split on newlines).
54 .l (or .list) : value as list (split on newlines).
55 .n (or .nlstr): original value (the string itself).
55 .n (or .nlstr): original value (the string itself).
56 .s (or .spstr): value as whitespace-separated string.
56 .s (or .spstr): value as whitespace-separated string.
57 .p (or .paths): list of path objects
57 .p (or .paths): list of path objects
58
58
59 Any values which require transformations are computed only once and
59 Any values which require transformations are computed only once and
60 cached.
60 cached.
61
61
62 Such strings are very useful to efficiently interact with the shell, which
62 Such strings are very useful to efficiently interact with the shell, which
63 typically only understands whitespace-separated options for commands."""
63 typically only understands whitespace-separated options for commands."""
64
64
65 def get_list(self):
65 def get_list(self):
66 try:
66 try:
67 return self.__list
67 return self.__list
68 except AttributeError:
68 except AttributeError:
69 self.__list = self.split('\n')
69 self.__list = self.split('\n')
70 return self.__list
70 return self.__list
71
71
72 l = list = property(get_list)
72 l = list = property(get_list)
73
73
74 def get_spstr(self):
74 def get_spstr(self):
75 try:
75 try:
76 return self.__spstr
76 return self.__spstr
77 except AttributeError:
77 except AttributeError:
78 self.__spstr = self.replace('\n',' ')
78 self.__spstr = self.replace('\n',' ')
79 return self.__spstr
79 return self.__spstr
80
80
81 s = spstr = property(get_spstr)
81 s = spstr = property(get_spstr)
82
82
83 def get_nlstr(self):
83 def get_nlstr(self):
84 return self
84 return self
85
85
86 n = nlstr = property(get_nlstr)
86 n = nlstr = property(get_nlstr)
87
87
88 def get_paths(self):
88 def get_paths(self):
89 try:
89 try:
90 return self.__paths
90 return self.__paths
91 except AttributeError:
91 except AttributeError:
92 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
92 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
93 return self.__paths
93 return self.__paths
94
94
95 p = paths = property(get_paths)
95 p = paths = property(get_paths)
96
96
97
97
98 def print_lsstring(arg):
98 def print_lsstring(arg):
99 """ Prettier (non-repr-like) and more informative printer for LSString """
99 """ Prettier (non-repr-like) and more informative printer for LSString """
100 print "LSString (.p, .n, .l, .s available). Value:"
100 print "LSString (.p, .n, .l, .s available). Value:"
101 print arg
101 print arg
102
102
103
103
104 print_lsstring = result_display.when_type(LSString)(print_lsstring)
104 print_lsstring = result_display.when_type(LSString)(print_lsstring)
105
105
106
106
107 class SList(list):
107 class SList(list):
108 """List derivative with a special access attributes.
108 """List derivative with a special access attributes.
109
109
110 These are normal lists, but with the special attributes:
110 These are normal lists, but with the special attributes:
111
111
112 .l (or .list) : value as list (the list itself).
112 .l (or .list) : value as list (the list itself).
113 .n (or .nlstr): value as a string, joined on newlines.
113 .n (or .nlstr): value as a string, joined on newlines.
114 .s (or .spstr): value as a string, joined on spaces.
114 .s (or .spstr): value as a string, joined on spaces.
115 .p (or .paths): list of path objects
115 .p (or .paths): list of path objects
116
116
117 Any values which require transformations are computed only once and
117 Any values which require transformations are computed only once and
118 cached."""
118 cached."""
119
119
120 def get_list(self):
120 def get_list(self):
121 return self
121 return self
122
122
123 l = list = property(get_list)
123 l = list = property(get_list)
124
124
125 def get_spstr(self):
125 def get_spstr(self):
126 try:
126 try:
127 return self.__spstr
127 return self.__spstr
128 except AttributeError:
128 except AttributeError:
129 self.__spstr = ' '.join(self)
129 self.__spstr = ' '.join(self)
130 return self.__spstr
130 return self.__spstr
131
131
132 s = spstr = property(get_spstr)
132 s = spstr = property(get_spstr)
133
133
134 def get_nlstr(self):
134 def get_nlstr(self):
135 try:
135 try:
136 return self.__nlstr
136 return self.__nlstr
137 except AttributeError:
137 except AttributeError:
138 self.__nlstr = '\n'.join(self)
138 self.__nlstr = '\n'.join(self)
139 return self.__nlstr
139 return self.__nlstr
140
140
141 n = nlstr = property(get_nlstr)
141 n = nlstr = property(get_nlstr)
142
142
143 def get_paths(self):
143 def get_paths(self):
144 try:
144 try:
145 return self.__paths
145 return self.__paths
146 except AttributeError:
146 except AttributeError:
147 self.__paths = [path(p) for p in self if os.path.exists(p)]
147 self.__paths = [path(p) for p in self if os.path.exists(p)]
148 return self.__paths
148 return self.__paths
149
149
150 p = paths = property(get_paths)
150 p = paths = property(get_paths)
151
151
152 def grep(self, pattern, prune = False, field = None):
152 def grep(self, pattern, prune = False, field = None):
153 """ Return all strings matching 'pattern' (a regex or callable)
153 """ Return all strings matching 'pattern' (a regex or callable)
154
154
155 This is case-insensitive. If prune is true, return all items
155 This is case-insensitive. If prune is true, return all items
156 NOT matching the pattern.
156 NOT matching the pattern.
157
157
158 If field is specified, the match must occur in the specified
158 If field is specified, the match must occur in the specified
159 whitespace-separated field.
159 whitespace-separated field.
160
160
161 Examples::
161 Examples::
162
162
163 a.grep( lambda x: x.startswith('C') )
163 a.grep( lambda x: x.startswith('C') )
164 a.grep('Cha.*log', prune=1)
164 a.grep('Cha.*log', prune=1)
165 a.grep('chm', field=-1)
165 a.grep('chm', field=-1)
166 """
166 """
167
167
168 def match_target(s):
168 def match_target(s):
169 if field is None:
169 if field is None:
170 return s
170 return s
171 parts = s.split()
171 parts = s.split()
172 try:
172 try:
173 tgt = parts[field]
173 tgt = parts[field]
174 return tgt
174 return tgt
175 except IndexError:
175 except IndexError:
176 return ""
176 return ""
177
177
178 if isinstance(pattern, basestring):
178 if isinstance(pattern, basestring):
179 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
179 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
180 else:
180 else:
181 pred = pattern
181 pred = pattern
182 if not prune:
182 if not prune:
183 return SList([el for el in self if pred(match_target(el))])
183 return SList([el for el in self if pred(match_target(el))])
184 else:
184 else:
185 return SList([el for el in self if not pred(match_target(el))])
185 return SList([el for el in self if not pred(match_target(el))])
186
186
187 def fields(self, *fields):
187 def fields(self, *fields):
188 """ Collect whitespace-separated fields from string list
188 """ Collect whitespace-separated fields from string list
189
189
190 Allows quick awk-like usage of string lists.
190 Allows quick awk-like usage of string lists.
191
191
192 Example data (in var a, created by 'a = !ls -l')::
192 Example data (in var a, created by 'a = !ls -l')::
193 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
193 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
194 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
194 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
195
195
196 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
196 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
197 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
197 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
198 (note the joining by space).
198 (note the joining by space).
199 a.fields(-1) is ['ChangeLog', 'IPython']
199 a.fields(-1) is ['ChangeLog', 'IPython']
200
200
201 IndexErrors are ignored.
201 IndexErrors are ignored.
202
202
203 Without args, fields() just split()'s the strings.
203 Without args, fields() just split()'s the strings.
204 """
204 """
205 if len(fields) == 0:
205 if len(fields) == 0:
206 return [el.split() for el in self]
206 return [el.split() for el in self]
207
207
208 res = SList()
208 res = SList()
209 for el in [f.split() for f in self]:
209 for el in [f.split() for f in self]:
210 lineparts = []
210 lineparts = []
211
211
212 for fd in fields:
212 for fd in fields:
213 try:
213 try:
214 lineparts.append(el[fd])
214 lineparts.append(el[fd])
215 except IndexError:
215 except IndexError:
216 pass
216 pass
217 if lineparts:
217 if lineparts:
218 res.append(" ".join(lineparts))
218 res.append(" ".join(lineparts))
219
219
220 return res
220 return res
221
221
222 def sort(self,field= None, nums = False):
222 def sort(self,field= None, nums = False):
223 """ sort by specified fields (see fields())
223 """ sort by specified fields (see fields())
224
224
225 Example::
225 Example::
226 a.sort(1, nums = True)
226 a.sort(1, nums = True)
227
227
228 Sorts a by second field, in numerical order (so that 21 > 3)
228 Sorts a by second field, in numerical order (so that 21 > 3)
229
229
230 """
230 """
231
231
232 #decorate, sort, undecorate
232 #decorate, sort, undecorate
233 if field is not None:
233 if field is not None:
234 dsu = [[SList([line]).fields(field), line] for line in self]
234 dsu = [[SList([line]).fields(field), line] for line in self]
235 else:
235 else:
236 dsu = [[line, line] for line in self]
236 dsu = [[line, line] for line in self]
237 if nums:
237 if nums:
238 for i in range(len(dsu)):
238 for i in range(len(dsu)):
239 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
239 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
240 try:
240 try:
241 n = int(numstr)
241 n = int(numstr)
242 except ValueError:
242 except ValueError:
243 n = 0;
243 n = 0;
244 dsu[i][0] = n
244 dsu[i][0] = n
245
245
246
246
247 dsu.sort()
247 dsu.sort()
248 return SList([t[1] for t in dsu])
248 return SList([t[1] for t in dsu])
249
249
250
250
251 def print_slist(arg):
251 def print_slist(arg):
252 """ Prettier (non-repr-like) and more informative printer for SList """
252 """ Prettier (non-repr-like) and more informative printer for SList """
253 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
253 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
254 if hasattr(arg, 'hideonce') and arg.hideonce:
254 if hasattr(arg, 'hideonce') and arg.hideonce:
255 arg.hideonce = False
255 arg.hideonce = False
256 return
256 return
257
257
258 nlprint(arg)
258 nlprint(arg)
259
259
260
260
261 print_slist = result_display.when_type(SList)(print_slist)
261 print_slist = result_display.when_type(SList)(print_slist)
262
262
263
263
264 def esc_quotes(strng):
264 def esc_quotes(strng):
265 """Return the input string with single and double quotes escaped out"""
265 """Return the input string with single and double quotes escaped out"""
266
266
267 return strng.replace('"','\\"').replace("'","\\'")
267 return strng.replace('"','\\"').replace("'","\\'")
268
268
269
269
270 def make_quoted_expr(s):
270 def make_quoted_expr(s):
271 """Return string s in appropriate quotes, using raw string if possible.
271 """Return string s in appropriate quotes, using raw string if possible.
272
272
273 XXX - example removed because it caused encoding errors in documentation
273 XXX - example removed because it caused encoding errors in documentation
274 generation. We need a new example that doesn't contain invalid chars.
274 generation. We need a new example that doesn't contain invalid chars.
275
275
276 Note the use of raw string and padding at the end to allow trailing
276 Note the use of raw string and padding at the end to allow trailing
277 backslash.
277 backslash.
278 """
278 """
279
279
280 tail = ''
280 tail = ''
281 tailpadding = ''
281 tailpadding = ''
282 raw = ''
282 raw = ''
283 if "\\" in s:
283 if "\\" in s:
284 raw = 'r'
284 raw = 'r'
285 if s.endswith('\\'):
285 if s.endswith('\\'):
286 tail = '[:-1]'
286 tail = '[:-1]'
287 tailpadding = '_'
287 tailpadding = '_'
288 if '"' not in s:
288 if '"' not in s:
289 quote = '"'
289 quote = '"'
290 elif "'" not in s:
290 elif "'" not in s:
291 quote = "'"
291 quote = "'"
292 elif '"""' not in s and not s.endswith('"'):
292 elif '"""' not in s and not s.endswith('"'):
293 quote = '"""'
293 quote = '"""'
294 elif "'''" not in s and not s.endswith("'"):
294 elif "'''" not in s and not s.endswith("'"):
295 quote = "'''"
295 quote = "'''"
296 else:
296 else:
297 # give up, backslash-escaped string will do
297 # give up, backslash-escaped string will do
298 return '"%s"' % esc_quotes(s)
298 return '"%s"' % esc_quotes(s).strip()
299 res = raw + quote + s + tailpadding + quote + tail
299 txt = (s + tailpadding).strip()
300 res = raw + quote + txt + quote + tail
300 return res
301 return res
301
302
302
303
303 def qw(words,flat=0,sep=None,maxsplit=-1):
304 def qw(words,flat=0,sep=None,maxsplit=-1):
304 """Similar to Perl's qw() operator, but with some more options.
305 """Similar to Perl's qw() operator, but with some more options.
305
306
306 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
307 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
307
308
308 words can also be a list itself, and with flat=1, the output will be
309 words can also be a list itself, and with flat=1, the output will be
309 recursively flattened.
310 recursively flattened.
310
311
311 Examples:
312 Examples:
312
313
313 >>> qw('1 2')
314 >>> qw('1 2')
314 ['1', '2']
315 ['1', '2']
315
316
316 >>> qw(['a b','1 2',['m n','p q']])
317 >>> qw(['a b','1 2',['m n','p q']])
317 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
318 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
318
319
319 >>> qw(['a b','1 2',['m n','p q']],flat=1)
320 >>> qw(['a b','1 2',['m n','p q']],flat=1)
320 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
321 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
321 """
322 """
322
323
323 if type(words) in StringTypes:
324 if type(words) in StringTypes:
324 return [word.strip() for word in words.split(sep,maxsplit)
325 return [word.strip() for word in words.split(sep,maxsplit)
325 if word and not word.isspace() ]
326 if word and not word.isspace() ]
326 if flat:
327 if flat:
327 return flatten(map(qw,words,[1]*len(words)))
328 return flatten(map(qw,words,[1]*len(words)))
328 return map(qw,words)
329 return map(qw,words)
329
330
330
331
331 def qwflat(words,sep=None,maxsplit=-1):
332 def qwflat(words,sep=None,maxsplit=-1):
332 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
333 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
333 return qw(words,1,sep,maxsplit)
334 return qw(words,1,sep,maxsplit)
334
335
335
336
336 def qw_lol(indata):
337 def qw_lol(indata):
337 """qw_lol('a b') -> [['a','b']],
338 """qw_lol('a b') -> [['a','b']],
338 otherwise it's just a call to qw().
339 otherwise it's just a call to qw().
339
340
340 We need this to make sure the modules_some keys *always* end up as a
341 We need this to make sure the modules_some keys *always* end up as a
341 list of lists."""
342 list of lists."""
342
343
343 if type(indata) in StringTypes:
344 if type(indata) in StringTypes:
344 return [qw(indata)]
345 return [qw(indata)]
345 else:
346 else:
346 return qw(indata)
347 return qw(indata)
347
348
348
349
349 def grep(pat,list,case=1):
350 def grep(pat,list,case=1):
350 """Simple minded grep-like function.
351 """Simple minded grep-like function.
351 grep(pat,list) returns occurrences of pat in list, None on failure.
352 grep(pat,list) returns occurrences of pat in list, None on failure.
352
353
353 It only does simple string matching, with no support for regexps. Use the
354 It only does simple string matching, with no support for regexps. Use the
354 option case=0 for case-insensitive matching."""
355 option case=0 for case-insensitive matching."""
355
356
356 # This is pretty crude. At least it should implement copying only references
357 # This is pretty crude. At least it should implement copying only references
357 # to the original data in case it's big. Now it copies the data for output.
358 # to the original data in case it's big. Now it copies the data for output.
358 out=[]
359 out=[]
359 if case:
360 if case:
360 for term in list:
361 for term in list:
361 if term.find(pat)>-1: out.append(term)
362 if term.find(pat)>-1: out.append(term)
362 else:
363 else:
363 lpat=pat.lower()
364 lpat=pat.lower()
364 for term in list:
365 for term in list:
365 if term.lower().find(lpat)>-1: out.append(term)
366 if term.lower().find(lpat)>-1: out.append(term)
366
367
367 if len(out): return out
368 if len(out): return out
368 else: return None
369 else: return None
369
370
370
371
371 def dgrep(pat,*opts):
372 def dgrep(pat,*opts):
372 """Return grep() on dir()+dir(__builtins__).
373 """Return grep() on dir()+dir(__builtins__).
373
374
374 A very common use of grep() when working interactively."""
375 A very common use of grep() when working interactively."""
375
376
376 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
377 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
377
378
378
379
379 def idgrep(pat):
380 def idgrep(pat):
380 """Case-insensitive dgrep()"""
381 """Case-insensitive dgrep()"""
381
382
382 return dgrep(pat,0)
383 return dgrep(pat,0)
383
384
384
385
385 def igrep(pat,list):
386 def igrep(pat,list):
386 """Synonym for case-insensitive grep."""
387 """Synonym for case-insensitive grep."""
387
388
388 return grep(pat,list,case=0)
389 return grep(pat,list,case=0)
389
390
390
391
391 def indent(str,nspaces=4,ntabs=0):
392 def indent(str,nspaces=4,ntabs=0):
392 """Indent a string a given number of spaces or tabstops.
393 """Indent a string a given number of spaces or tabstops.
393
394
394 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
395 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
395 """
396 """
396 if str is None:
397 if str is None:
397 return
398 return
398 ind = '\t'*ntabs+' '*nspaces
399 ind = '\t'*ntabs+' '*nspaces
399 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
400 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
400 if outstr.endswith(os.linesep+ind):
401 if outstr.endswith(os.linesep+ind):
401 return outstr[:-len(ind)]
402 return outstr[:-len(ind)]
402 else:
403 else:
403 return outstr
404 return outstr
404
405
405 def native_line_ends(filename,backup=1):
406 def native_line_ends(filename,backup=1):
406 """Convert (in-place) a file to line-ends native to the current OS.
407 """Convert (in-place) a file to line-ends native to the current OS.
407
408
408 If the optional backup argument is given as false, no backup of the
409 If the optional backup argument is given as false, no backup of the
409 original file is left. """
410 original file is left. """
410
411
411 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
412 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
412
413
413 bak_filename = filename + backup_suffixes[os.name]
414 bak_filename = filename + backup_suffixes[os.name]
414
415
415 original = open(filename).read()
416 original = open(filename).read()
416 shutil.copy2(filename,bak_filename)
417 shutil.copy2(filename,bak_filename)
417 try:
418 try:
418 new = open(filename,'wb')
419 new = open(filename,'wb')
419 new.write(os.linesep.join(original.splitlines()))
420 new.write(os.linesep.join(original.splitlines()))
420 new.write(os.linesep) # ALWAYS put an eol at the end of the file
421 new.write(os.linesep) # ALWAYS put an eol at the end of the file
421 new.close()
422 new.close()
422 except:
423 except:
423 os.rename(bak_filename,filename)
424 os.rename(bak_filename,filename)
424 if not backup:
425 if not backup:
425 try:
426 try:
426 os.remove(bak_filename)
427 os.remove(bak_filename)
427 except:
428 except:
428 pass
429 pass
429
430
430
431
431 def list_strings(arg):
432 def list_strings(arg):
432 """Always return a list of strings, given a string or list of strings
433 """Always return a list of strings, given a string or list of strings
433 as input.
434 as input.
434
435
435 :Examples:
436 :Examples:
436
437
437 In [7]: list_strings('A single string')
438 In [7]: list_strings('A single string')
438 Out[7]: ['A single string']
439 Out[7]: ['A single string']
439
440
440 In [8]: list_strings(['A single string in a list'])
441 In [8]: list_strings(['A single string in a list'])
441 Out[8]: ['A single string in a list']
442 Out[8]: ['A single string in a list']
442
443
443 In [9]: list_strings(['A','list','of','strings'])
444 In [9]: list_strings(['A','list','of','strings'])
444 Out[9]: ['A', 'list', 'of', 'strings']
445 Out[9]: ['A', 'list', 'of', 'strings']
445 """
446 """
446
447
447 if isinstance(arg,basestring): return [arg]
448 if isinstance(arg,basestring): return [arg]
448 else: return arg
449 else: return arg
449
450
450
451
451 def marquee(txt='',width=78,mark='*'):
452 def marquee(txt='',width=78,mark='*'):
452 """Return the input string centered in a 'marquee'.
453 """Return the input string centered in a 'marquee'.
453
454
454 :Examples:
455 :Examples:
455
456
456 In [16]: marquee('A test',40)
457 In [16]: marquee('A test',40)
457 Out[16]: '**************** A test ****************'
458 Out[16]: '**************** A test ****************'
458
459
459 In [17]: marquee('A test',40,'-')
460 In [17]: marquee('A test',40,'-')
460 Out[17]: '---------------- A test ----------------'
461 Out[17]: '---------------- A test ----------------'
461
462
462 In [18]: marquee('A test',40,' ')
463 In [18]: marquee('A test',40,' ')
463 Out[18]: ' A test '
464 Out[18]: ' A test '
464
465
465 """
466 """
466 if not txt:
467 if not txt:
467 return (mark*width)[:width]
468 return (mark*width)[:width]
468 nmark = (width-len(txt)-2)/len(mark)/2
469 nmark = (width-len(txt)-2)/len(mark)/2
469 if nmark < 0: nmark =0
470 if nmark < 0: nmark =0
470 marks = mark*nmark
471 marks = mark*nmark
471 return '%s %s %s' % (marks,txt,marks)
472 return '%s %s %s' % (marks,txt,marks)
472
473
473
474
General Comments 0
You need to be logged in to leave comments. Login now