##// END OF EJS Templates
Remove unused code
Thomas Kluyver -
Show More
@@ -1,393 +1,392 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The :class:`~IPython.core.application.Application` object for the command
4 The :class:`~IPython.core.application.Application` object for the command
5 line :command:`ipython` program.
5 line :command:`ipython` program.
6
6
7 Authors
7 Authors
8 -------
8 -------
9
9
10 * Brian Granger
10 * Brian Granger
11 * Fernando Perez
11 * Fernando Perez
12 * Min Ragan-Kelley
12 * Min Ragan-Kelley
13 """
13 """
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Copyright (C) 2008-2011 The IPython Development Team
16 # Copyright (C) 2008-2011 The IPython Development Team
17 #
17 #
18 # 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
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 from __future__ import absolute_import
26 from __future__ import absolute_import
27
27
28 import logging
28 import logging
29 import os
29 import os
30 import sys
30 import sys
31
31
32 from IPython.config.loader import (
32 from IPython.config.loader import (
33 Config, PyFileConfigLoader, ConfigFileNotFound
33 Config, PyFileConfigLoader, ConfigFileNotFound
34 )
34 )
35 from IPython.config.application import boolean_flag, catch_config_error
35 from IPython.config.application import boolean_flag, catch_config_error
36 from IPython.core import release
36 from IPython.core import release
37 from IPython.core import usage
37 from IPython.core import usage
38 from IPython.core.completer import IPCompleter
38 from IPython.core.completer import IPCompleter
39 from IPython.core.crashhandler import CrashHandler
39 from IPython.core.crashhandler import CrashHandler
40 from IPython.core.formatters import PlainTextFormatter
40 from IPython.core.formatters import PlainTextFormatter
41 from IPython.core.history import HistoryManager
41 from IPython.core.history import HistoryManager
42 from IPython.core.prompts import PromptManager
42 from IPython.core.prompts import PromptManager
43 from IPython.core.application import (
43 from IPython.core.application import (
44 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
44 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
45 )
45 )
46 from IPython.core.magics import ScriptMagics
46 from IPython.core.magics import ScriptMagics
47 from IPython.core.shellapp import (
47 from IPython.core.shellapp import (
48 InteractiveShellApp, shell_flags, shell_aliases
48 InteractiveShellApp, shell_flags, shell_aliases
49 )
49 )
50 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
50 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
51 from IPython.lib import inputhook
52 from IPython.utils import warn
51 from IPython.utils import warn
53 from IPython.utils.path import get_ipython_dir, check_for_old_config
52 from IPython.utils.path import get_ipython_dir, check_for_old_config
54 from IPython.utils.traitlets import (
53 from IPython.utils.traitlets import (
55 Bool, List, Dict, CaselessStrEnum
54 Bool, List, Dict, CaselessStrEnum
56 )
55 )
57
56
58 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
59 # Globals, utilities and helpers
58 # Globals, utilities and helpers
60 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
61
60
62 #: The default config file name for this application.
61 #: The default config file name for this application.
63 default_config_file_name = u'ipython_config.py'
62 default_config_file_name = u'ipython_config.py'
64
63
65 _examples = """
64 _examples = """
66 ipython --pylab # start in pylab mode
65 ipython --pylab # start in pylab mode
67 ipython --pylab=qt # start in pylab mode with the qt4 backend
66 ipython --pylab=qt # start in pylab mode with the qt4 backend
68 ipython --log-level=DEBUG # set logging to DEBUG
67 ipython --log-level=DEBUG # set logging to DEBUG
69 ipython --profile=foo # start with profile foo
68 ipython --profile=foo # start with profile foo
70
69
71 ipython qtconsole # start the qtconsole GUI application
70 ipython qtconsole # start the qtconsole GUI application
72 ipython help qtconsole # show the help for the qtconsole subcmd
71 ipython help qtconsole # show the help for the qtconsole subcmd
73
72
74 ipython console # start the terminal-based console application
73 ipython console # start the terminal-based console application
75 ipython help console # show the help for the console subcmd
74 ipython help console # show the help for the console subcmd
76
75
77 ipython notebook # start the IPython notebook
76 ipython notebook # start the IPython notebook
78 ipython help notebook # show the help for the notebook subcmd
77 ipython help notebook # show the help for the notebook subcmd
79
78
80 ipython profile create foo # create profile foo w/ default config files
79 ipython profile create foo # create profile foo w/ default config files
81 ipython help profile # show the help for the profile subcmd
80 ipython help profile # show the help for the profile subcmd
82
81
83 ipython locate # print the path to the IPython directory
82 ipython locate # print the path to the IPython directory
84 ipython locate profile foo # print the path to the directory for profile `foo`
83 ipython locate profile foo # print the path to the directory for profile `foo`
85 """
84 """
86
85
87 #-----------------------------------------------------------------------------
86 #-----------------------------------------------------------------------------
88 # Crash handler for this application
87 # Crash handler for this application
89 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
90
89
91 class IPAppCrashHandler(CrashHandler):
90 class IPAppCrashHandler(CrashHandler):
92 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
91 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
93
92
94 def __init__(self, app):
93 def __init__(self, app):
95 contact_name = release.author
94 contact_name = release.author
96 contact_email = release.author_email
95 contact_email = release.author_email
97 bug_tracker = 'https://github.com/ipython/ipython/issues'
96 bug_tracker = 'https://github.com/ipython/ipython/issues'
98 super(IPAppCrashHandler,self).__init__(
97 super(IPAppCrashHandler,self).__init__(
99 app, contact_name, contact_email, bug_tracker
98 app, contact_name, contact_email, bug_tracker
100 )
99 )
101
100
102 def make_report(self,traceback):
101 def make_report(self,traceback):
103 """Return a string containing a crash report."""
102 """Return a string containing a crash report."""
104
103
105 sec_sep = self.section_sep
104 sec_sep = self.section_sep
106 # Start with parent report
105 # Start with parent report
107 report = [super(IPAppCrashHandler, self).make_report(traceback)]
106 report = [super(IPAppCrashHandler, self).make_report(traceback)]
108 # Add interactive-specific info we may have
107 # Add interactive-specific info we may have
109 rpt_add = report.append
108 rpt_add = report.append
110 try:
109 try:
111 rpt_add(sec_sep+"History of session input:")
110 rpt_add(sec_sep+"History of session input:")
112 for line in self.app.shell.user_ns['_ih']:
111 for line in self.app.shell.user_ns['_ih']:
113 rpt_add(line)
112 rpt_add(line)
114 rpt_add('\n*** Last line of input (may not be in above history):\n')
113 rpt_add('\n*** Last line of input (may not be in above history):\n')
115 rpt_add(self.app.shell._last_input_line+'\n')
114 rpt_add(self.app.shell._last_input_line+'\n')
116 except:
115 except:
117 pass
116 pass
118
117
119 return ''.join(report)
118 return ''.join(report)
120
119
121 #-----------------------------------------------------------------------------
120 #-----------------------------------------------------------------------------
122 # Aliases and Flags
121 # Aliases and Flags
123 #-----------------------------------------------------------------------------
122 #-----------------------------------------------------------------------------
124 flags = dict(base_flags)
123 flags = dict(base_flags)
125 flags.update(shell_flags)
124 flags.update(shell_flags)
126 frontend_flags = {}
125 frontend_flags = {}
127 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
126 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
128 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
127 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
129 'Turn on auto editing of files with syntax errors.',
128 'Turn on auto editing of files with syntax errors.',
130 'Turn off auto editing of files with syntax errors.'
129 'Turn off auto editing of files with syntax errors.'
131 )
130 )
132 addflag('banner', 'TerminalIPythonApp.display_banner',
131 addflag('banner', 'TerminalIPythonApp.display_banner',
133 "Display a banner upon starting IPython.",
132 "Display a banner upon starting IPython.",
134 "Don't display a banner upon starting IPython."
133 "Don't display a banner upon starting IPython."
135 )
134 )
136 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
135 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
137 """Set to confirm when you try to exit IPython with an EOF (Control-D
136 """Set to confirm when you try to exit IPython with an EOF (Control-D
138 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
137 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
139 you can force a direct exit without any confirmation.""",
138 you can force a direct exit without any confirmation.""",
140 "Don't prompt the user when exiting."
139 "Don't prompt the user when exiting."
141 )
140 )
142 addflag('term-title', 'TerminalInteractiveShell.term_title',
141 addflag('term-title', 'TerminalInteractiveShell.term_title',
143 "Enable auto setting the terminal title.",
142 "Enable auto setting the terminal title.",
144 "Disable auto setting the terminal title."
143 "Disable auto setting the terminal title."
145 )
144 )
146 classic_config = Config()
145 classic_config = Config()
147 classic_config.InteractiveShell.cache_size = 0
146 classic_config.InteractiveShell.cache_size = 0
148 classic_config.PlainTextFormatter.pprint = False
147 classic_config.PlainTextFormatter.pprint = False
149 classic_config.PromptManager.in_template = '>>> '
148 classic_config.PromptManager.in_template = '>>> '
150 classic_config.PromptManager.in2_template = '... '
149 classic_config.PromptManager.in2_template = '... '
151 classic_config.PromptManager.out_template = ''
150 classic_config.PromptManager.out_template = ''
152 classic_config.InteractiveShell.separate_in = ''
151 classic_config.InteractiveShell.separate_in = ''
153 classic_config.InteractiveShell.separate_out = ''
152 classic_config.InteractiveShell.separate_out = ''
154 classic_config.InteractiveShell.separate_out2 = ''
153 classic_config.InteractiveShell.separate_out2 = ''
155 classic_config.InteractiveShell.colors = 'NoColor'
154 classic_config.InteractiveShell.colors = 'NoColor'
156 classic_config.InteractiveShell.xmode = 'Plain'
155 classic_config.InteractiveShell.xmode = 'Plain'
157
156
158 frontend_flags['classic']=(
157 frontend_flags['classic']=(
159 classic_config,
158 classic_config,
160 "Gives IPython a similar feel to the classic Python prompt."
159 "Gives IPython a similar feel to the classic Python prompt."
161 )
160 )
162 # # log doesn't make so much sense this way anymore
161 # # log doesn't make so much sense this way anymore
163 # paa('--log','-l',
162 # paa('--log','-l',
164 # action='store_true', dest='InteractiveShell.logstart',
163 # action='store_true', dest='InteractiveShell.logstart',
165 # help="Start logging to the default log file (./ipython_log.py).")
164 # help="Start logging to the default log file (./ipython_log.py).")
166 #
165 #
167 # # quick is harder to implement
166 # # quick is harder to implement
168 frontend_flags['quick']=(
167 frontend_flags['quick']=(
169 {'TerminalIPythonApp' : {'quick' : True}},
168 {'TerminalIPythonApp' : {'quick' : True}},
170 "Enable quick startup with no config files."
169 "Enable quick startup with no config files."
171 )
170 )
172
171
173 frontend_flags['i'] = (
172 frontend_flags['i'] = (
174 {'TerminalIPythonApp' : {'force_interact' : True}},
173 {'TerminalIPythonApp' : {'force_interact' : True}},
175 """If running code from the command line, become interactive afterwards.
174 """If running code from the command line, become interactive afterwards.
176 Note: can also be given simply as '-i.'"""
175 Note: can also be given simply as '-i.'"""
177 )
176 )
178 flags.update(frontend_flags)
177 flags.update(frontend_flags)
179
178
180 aliases = dict(base_aliases)
179 aliases = dict(base_aliases)
181 aliases.update(shell_aliases)
180 aliases.update(shell_aliases)
182
181
183 #-----------------------------------------------------------------------------
182 #-----------------------------------------------------------------------------
184 # Main classes and functions
183 # Main classes and functions
185 #-----------------------------------------------------------------------------
184 #-----------------------------------------------------------------------------
186
185
187
186
188 class LocateIPythonApp(BaseIPythonApplication):
187 class LocateIPythonApp(BaseIPythonApplication):
189 description = """print the path to the IPython dir"""
188 description = """print the path to the IPython dir"""
190 subcommands = Dict(dict(
189 subcommands = Dict(dict(
191 profile=('IPython.core.profileapp.ProfileLocate',
190 profile=('IPython.core.profileapp.ProfileLocate',
192 "print the path to an IPython profile directory",
191 "print the path to an IPython profile directory",
193 ),
192 ),
194 ))
193 ))
195 def start(self):
194 def start(self):
196 if self.subapp is not None:
195 if self.subapp is not None:
197 return self.subapp.start()
196 return self.subapp.start()
198 else:
197 else:
199 print self.ipython_dir
198 print self.ipython_dir
200
199
201
200
202 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
201 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
203 name = u'ipython'
202 name = u'ipython'
204 description = usage.cl_usage
203 description = usage.cl_usage
205 default_config_file_name = default_config_file_name
204 default_config_file_name = default_config_file_name
206 crash_handler_class = IPAppCrashHandler
205 crash_handler_class = IPAppCrashHandler
207 examples = _examples
206 examples = _examples
208
207
209 flags = Dict(flags)
208 flags = Dict(flags)
210 aliases = Dict(aliases)
209 aliases = Dict(aliases)
211 classes = List()
210 classes = List()
212 def _classes_default(self):
211 def _classes_default(self):
213 """This has to be in a method, for TerminalIPythonApp to be available."""
212 """This has to be in a method, for TerminalIPythonApp to be available."""
214 return [
213 return [
215 InteractiveShellApp, # ShellApp comes before TerminalApp, because
214 InteractiveShellApp, # ShellApp comes before TerminalApp, because
216 self.__class__, # it will also affect subclasses (e.g. QtConsole)
215 self.__class__, # it will also affect subclasses (e.g. QtConsole)
217 TerminalInteractiveShell,
216 TerminalInteractiveShell,
218 PromptManager,
217 PromptManager,
219 HistoryManager,
218 HistoryManager,
220 ProfileDir,
219 ProfileDir,
221 PlainTextFormatter,
220 PlainTextFormatter,
222 IPCompleter,
221 IPCompleter,
223 ScriptMagics,
222 ScriptMagics,
224 ]
223 ]
225
224
226 subcommands = Dict(dict(
225 subcommands = Dict(dict(
227 qtconsole=('IPython.frontend.qt.console.qtconsoleapp.IPythonQtConsoleApp',
226 qtconsole=('IPython.frontend.qt.console.qtconsoleapp.IPythonQtConsoleApp',
228 """Launch the IPython Qt Console."""
227 """Launch the IPython Qt Console."""
229 ),
228 ),
230 notebook=('IPython.frontend.html.notebook.notebookapp.NotebookApp',
229 notebook=('IPython.frontend.html.notebook.notebookapp.NotebookApp',
231 """Launch the IPython HTML Notebook Server."""
230 """Launch the IPython HTML Notebook Server."""
232 ),
231 ),
233 profile = ("IPython.core.profileapp.ProfileApp",
232 profile = ("IPython.core.profileapp.ProfileApp",
234 "Create and manage IPython profiles."
233 "Create and manage IPython profiles."
235 ),
234 ),
236 kernel = ("IPython.zmq.ipkernel.IPKernelApp",
235 kernel = ("IPython.zmq.ipkernel.IPKernelApp",
237 "Start a kernel without an attached frontend."
236 "Start a kernel without an attached frontend."
238 ),
237 ),
239 console=('IPython.frontend.terminal.console.app.ZMQTerminalIPythonApp',
238 console=('IPython.frontend.terminal.console.app.ZMQTerminalIPythonApp',
240 """Launch the IPython terminal-based Console."""
239 """Launch the IPython terminal-based Console."""
241 ),
240 ),
242 locate=('IPython.frontend.terminal.ipapp.LocateIPythonApp',
241 locate=('IPython.frontend.terminal.ipapp.LocateIPythonApp',
243 LocateIPythonApp.description
242 LocateIPythonApp.description
244 ),
243 ),
245 ))
244 ))
246
245
247 # *do* autocreate requested profile, but don't create the config file.
246 # *do* autocreate requested profile, but don't create the config file.
248 auto_create=Bool(True)
247 auto_create=Bool(True)
249 # configurables
248 # configurables
250 ignore_old_config=Bool(False, config=True,
249 ignore_old_config=Bool(False, config=True,
251 help="Suppress warning messages about legacy config files"
250 help="Suppress warning messages about legacy config files"
252 )
251 )
253 quick = Bool(False, config=True,
252 quick = Bool(False, config=True,
254 help="""Start IPython quickly by skipping the loading of config files."""
253 help="""Start IPython quickly by skipping the loading of config files."""
255 )
254 )
256 def _quick_changed(self, name, old, new):
255 def _quick_changed(self, name, old, new):
257 if new:
256 if new:
258 self.load_config_file = lambda *a, **kw: None
257 self.load_config_file = lambda *a, **kw: None
259 self.ignore_old_config=True
258 self.ignore_old_config=True
260
259
261 display_banner = Bool(True, config=True,
260 display_banner = Bool(True, config=True,
262 help="Whether to display a banner upon starting IPython."
261 help="Whether to display a banner upon starting IPython."
263 )
262 )
264
263
265 # if there is code of files to run from the cmd line, don't interact
264 # if there is code of files to run from the cmd line, don't interact
266 # unless the --i flag (App.force_interact) is true.
265 # unless the --i flag (App.force_interact) is true.
267 force_interact = Bool(False, config=True,
266 force_interact = Bool(False, config=True,
268 help="""If a command or file is given via the command-line,
267 help="""If a command or file is given via the command-line,
269 e.g. 'ipython foo.py"""
268 e.g. 'ipython foo.py"""
270 )
269 )
271 def _force_interact_changed(self, name, old, new):
270 def _force_interact_changed(self, name, old, new):
272 if new:
271 if new:
273 self.interact = True
272 self.interact = True
274
273
275 def _file_to_run_changed(self, name, old, new):
274 def _file_to_run_changed(self, name, old, new):
276 if new:
275 if new:
277 self.something_to_run = True
276 self.something_to_run = True
278 if new and not self.force_interact:
277 if new and not self.force_interact:
279 self.interact = False
278 self.interact = False
280 _code_to_run_changed = _file_to_run_changed
279 _code_to_run_changed = _file_to_run_changed
281 _module_to_run_changed = _file_to_run_changed
280 _module_to_run_changed = _file_to_run_changed
282
281
283 # internal, not-configurable
282 # internal, not-configurable
284 interact=Bool(True)
283 interact=Bool(True)
285 something_to_run=Bool(False)
284 something_to_run=Bool(False)
286
285
287 def parse_command_line(self, argv=None):
286 def parse_command_line(self, argv=None):
288 """override to allow old '-pylab' flag with deprecation warning"""
287 """override to allow old '-pylab' flag with deprecation warning"""
289
288
290 argv = sys.argv[1:] if argv is None else argv
289 argv = sys.argv[1:] if argv is None else argv
291
290
292 if '-pylab' in argv:
291 if '-pylab' in argv:
293 # deprecated `-pylab` given,
292 # deprecated `-pylab` given,
294 # warn and transform into current syntax
293 # warn and transform into current syntax
295 argv = argv[:] # copy, don't clobber
294 argv = argv[:] # copy, don't clobber
296 idx = argv.index('-pylab')
295 idx = argv.index('-pylab')
297 warn.warn("`-pylab` flag has been deprecated.\n"
296 warn.warn("`-pylab` flag has been deprecated.\n"
298 " Use `--pylab` instead, or `--pylab=foo` to specify a backend.")
297 " Use `--pylab` instead, or `--pylab=foo` to specify a backend.")
299 sub = '--pylab'
298 sub = '--pylab'
300 if len(argv) > idx+1:
299 if len(argv) > idx+1:
301 # check for gui arg, as in '-pylab qt'
300 # check for gui arg, as in '-pylab qt'
302 gui = argv[idx+1]
301 gui = argv[idx+1]
303 if gui in ('wx', 'qt', 'qt4', 'gtk', 'auto'):
302 if gui in ('wx', 'qt', 'qt4', 'gtk', 'auto'):
304 sub = '--pylab='+gui
303 sub = '--pylab='+gui
305 argv.pop(idx+1)
304 argv.pop(idx+1)
306 argv[idx] = sub
305 argv[idx] = sub
307
306
308 return super(TerminalIPythonApp, self).parse_command_line(argv)
307 return super(TerminalIPythonApp, self).parse_command_line(argv)
309
308
310 @catch_config_error
309 @catch_config_error
311 def initialize(self, argv=None):
310 def initialize(self, argv=None):
312 """Do actions after construct, but before starting the app."""
311 """Do actions after construct, but before starting the app."""
313 super(TerminalIPythonApp, self).initialize(argv)
312 super(TerminalIPythonApp, self).initialize(argv)
314 if self.subapp is not None:
313 if self.subapp is not None:
315 # don't bother initializing further, starting subapp
314 # don't bother initializing further, starting subapp
316 return
315 return
317 if not self.ignore_old_config:
316 if not self.ignore_old_config:
318 check_for_old_config(self.ipython_dir)
317 check_for_old_config(self.ipython_dir)
319 # print self.extra_args
318 # print self.extra_args
320 if self.extra_args and not self.something_to_run:
319 if self.extra_args and not self.something_to_run:
321 self.file_to_run = self.extra_args[0]
320 self.file_to_run = self.extra_args[0]
322 self.init_path()
321 self.init_path()
323 # create the shell
322 # create the shell
324 self.init_shell()
323 self.init_shell()
325 # and draw the banner
324 # and draw the banner
326 self.init_banner()
325 self.init_banner()
327 # Now a variety of things that happen after the banner is printed.
326 # Now a variety of things that happen after the banner is printed.
328 self.init_gui_pylab()
327 self.init_gui_pylab()
329 self.init_extensions()
328 self.init_extensions()
330 self.init_code()
329 self.init_code()
331
330
332 def init_shell(self):
331 def init_shell(self):
333 """initialize the InteractiveShell instance"""
332 """initialize the InteractiveShell instance"""
334 # Create an InteractiveShell instance.
333 # Create an InteractiveShell instance.
335 # shell.display_banner should always be False for the terminal
334 # shell.display_banner should always be False for the terminal
336 # based app, because we call shell.show_banner() by hand below
335 # based app, because we call shell.show_banner() by hand below
337 # so the banner shows *before* all extension loading stuff.
336 # so the banner shows *before* all extension loading stuff.
338 self.shell = TerminalInteractiveShell.instance(config=self.config,
337 self.shell = TerminalInteractiveShell.instance(config=self.config,
339 display_banner=False, profile_dir=self.profile_dir,
338 display_banner=False, profile_dir=self.profile_dir,
340 ipython_dir=self.ipython_dir)
339 ipython_dir=self.ipython_dir)
341 self.shell.configurables.append(self)
340 self.shell.configurables.append(self)
342
341
343 def init_banner(self):
342 def init_banner(self):
344 """optionally display the banner"""
343 """optionally display the banner"""
345 if self.display_banner and self.interact:
344 if self.display_banner and self.interact:
346 self.shell.show_banner()
345 self.shell.show_banner()
347 # Make sure there is a space below the banner.
346 # Make sure there is a space below the banner.
348 if self.log_level <= logging.INFO: print
347 if self.log_level <= logging.INFO: print
349
348
350 def _pylab_changed(self, name, old, new):
349 def _pylab_changed(self, name, old, new):
351 """Replace --pylab='inline' with --pylab='auto'"""
350 """Replace --pylab='inline' with --pylab='auto'"""
352 if new == 'inline':
351 if new == 'inline':
353 warn.warn("'inline' not available as pylab backend, "
352 warn.warn("'inline' not available as pylab backend, "
354 "using 'auto' instead.")
353 "using 'auto' instead.")
355 self.pylab = 'auto'
354 self.pylab = 'auto'
356
355
357 def start(self):
356 def start(self):
358 if self.subapp is not None:
357 if self.subapp is not None:
359 return self.subapp.start()
358 return self.subapp.start()
360 # perform any prexec steps:
359 # perform any prexec steps:
361 if self.interact:
360 if self.interact:
362 self.log.debug("Starting IPython's mainloop...")
361 self.log.debug("Starting IPython's mainloop...")
363 self.shell.mainloop()
362 self.shell.mainloop()
364 else:
363 else:
365 self.log.debug("IPython not interactive...")
364 self.log.debug("IPython not interactive...")
366
365
367
366
368 def load_default_config(ipython_dir=None):
367 def load_default_config(ipython_dir=None):
369 """Load the default config file from the default ipython_dir.
368 """Load the default config file from the default ipython_dir.
370
369
371 This is useful for embedded shells.
370 This is useful for embedded shells.
372 """
371 """
373 if ipython_dir is None:
372 if ipython_dir is None:
374 ipython_dir = get_ipython_dir()
373 ipython_dir = get_ipython_dir()
375 profile_dir = os.path.join(ipython_dir, 'profile_default')
374 profile_dir = os.path.join(ipython_dir, 'profile_default')
376 cl = PyFileConfigLoader(default_config_file_name, profile_dir)
375 cl = PyFileConfigLoader(default_config_file_name, profile_dir)
377 try:
376 try:
378 config = cl.load_config()
377 config = cl.load_config()
379 except ConfigFileNotFound:
378 except ConfigFileNotFound:
380 # no config found
379 # no config found
381 config = Config()
380 config = Config()
382 return config
381 return config
383
382
384
383
385 def launch_new_instance():
384 def launch_new_instance():
386 """Create and run a full blown IPython instance"""
385 """Create and run a full blown IPython instance"""
387 app = TerminalIPythonApp.instance()
386 app = TerminalIPythonApp.instance()
388 app.initialize()
387 app.initialize()
389 app.start()
388 app.start()
390
389
391
390
392 if __name__ == '__main__':
391 if __name__ == '__main__':
393 launch_new_instance()
392 launch_new_instance()
@@ -1,850 +1,824 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 Inheritance diagram:
5 Inheritance diagram:
6
6
7 .. inheritance-diagram:: IPython.utils.text
7 .. inheritance-diagram:: IPython.utils.text
8 :parts: 3
8 :parts: 3
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import __main__
22 import __main__
23
23
24 import os
24 import os
25 import re
25 import re
26 import shutil
27 import sys
26 import sys
28 import textwrap
27 import textwrap
29 from string import Formatter
28 from string import Formatter
30
29
31 from IPython.external.path import path
30 from IPython.external.path import path
32 from IPython.testing.skipdoctest import skip_doctest_py3, skip_doctest
31 from IPython.testing.skipdoctest import skip_doctest_py3, skip_doctest
33 from IPython.utils import py3compat
32 from IPython.utils import py3compat
34 from IPython.utils.io import nlprint
33 from IPython.utils.io import nlprint
35 from IPython.utils.data import flatten
34 from IPython.utils.data import flatten
36
35
37 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
38 # Code
37 # Code
39 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
40
39
41 def unquote_ends(istr):
40 def unquote_ends(istr):
42 """Remove a single pair of quotes from the endpoints of a string."""
41 """Remove a single pair of quotes from the endpoints of a string."""
43
42
44 if not istr:
43 if not istr:
45 return istr
44 return istr
46 if (istr[0]=="'" and istr[-1]=="'") or \
45 if (istr[0]=="'" and istr[-1]=="'") or \
47 (istr[0]=='"' and istr[-1]=='"'):
46 (istr[0]=='"' and istr[-1]=='"'):
48 return istr[1:-1]
47 return istr[1:-1]
49 else:
48 else:
50 return istr
49 return istr
51
50
52
51
53 class LSString(str):
52 class LSString(str):
54 """String derivative with a special access attributes.
53 """String derivative with a special access attributes.
55
54
56 These are normal strings, but with the special attributes:
55 These are normal strings, but with the special attributes:
57
56
58 .l (or .list) : value as list (split on newlines).
57 .l (or .list) : value as list (split on newlines).
59 .n (or .nlstr): original value (the string itself).
58 .n (or .nlstr): original value (the string itself).
60 .s (or .spstr): value as whitespace-separated string.
59 .s (or .spstr): value as whitespace-separated string.
61 .p (or .paths): list of path objects
60 .p (or .paths): list of path objects
62
61
63 Any values which require transformations are computed only once and
62 Any values which require transformations are computed only once and
64 cached.
63 cached.
65
64
66 Such strings are very useful to efficiently interact with the shell, which
65 Such strings are very useful to efficiently interact with the shell, which
67 typically only understands whitespace-separated options for commands."""
66 typically only understands whitespace-separated options for commands."""
68
67
69 def get_list(self):
68 def get_list(self):
70 try:
69 try:
71 return self.__list
70 return self.__list
72 except AttributeError:
71 except AttributeError:
73 self.__list = self.split('\n')
72 self.__list = self.split('\n')
74 return self.__list
73 return self.__list
75
74
76 l = list = property(get_list)
75 l = list = property(get_list)
77
76
78 def get_spstr(self):
77 def get_spstr(self):
79 try:
78 try:
80 return self.__spstr
79 return self.__spstr
81 except AttributeError:
80 except AttributeError:
82 self.__spstr = self.replace('\n',' ')
81 self.__spstr = self.replace('\n',' ')
83 return self.__spstr
82 return self.__spstr
84
83
85 s = spstr = property(get_spstr)
84 s = spstr = property(get_spstr)
86
85
87 def get_nlstr(self):
86 def get_nlstr(self):
88 return self
87 return self
89
88
90 n = nlstr = property(get_nlstr)
89 n = nlstr = property(get_nlstr)
91
90
92 def get_paths(self):
91 def get_paths(self):
93 try:
92 try:
94 return self.__paths
93 return self.__paths
95 except AttributeError:
94 except AttributeError:
96 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
95 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
97 return self.__paths
96 return self.__paths
98
97
99 p = paths = property(get_paths)
98 p = paths = property(get_paths)
100
99
101 # FIXME: We need to reimplement type specific displayhook and then add this
100 # FIXME: We need to reimplement type specific displayhook and then add this
102 # back as a custom printer. This should also be moved outside utils into the
101 # back as a custom printer. This should also be moved outside utils into the
103 # core.
102 # core.
104
103
105 # def print_lsstring(arg):
104 # def print_lsstring(arg):
106 # """ Prettier (non-repr-like) and more informative printer for LSString """
105 # """ Prettier (non-repr-like) and more informative printer for LSString """
107 # print "LSString (.p, .n, .l, .s available). Value:"
106 # print "LSString (.p, .n, .l, .s available). Value:"
108 # print arg
107 # print arg
109 #
108 #
110 #
109 #
111 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
110 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
112
111
113
112
114 class SList(list):
113 class SList(list):
115 """List derivative with a special access attributes.
114 """List derivative with a special access attributes.
116
115
117 These are normal lists, but with the special attributes:
116 These are normal lists, but with the special attributes:
118
117
119 .l (or .list) : value as list (the list itself).
118 .l (or .list) : value as list (the list itself).
120 .n (or .nlstr): value as a string, joined on newlines.
119 .n (or .nlstr): value as a string, joined on newlines.
121 .s (or .spstr): value as a string, joined on spaces.
120 .s (or .spstr): value as a string, joined on spaces.
122 .p (or .paths): list of path objects
121 .p (or .paths): list of path objects
123
122
124 Any values which require transformations are computed only once and
123 Any values which require transformations are computed only once and
125 cached."""
124 cached."""
126
125
127 def get_list(self):
126 def get_list(self):
128 return self
127 return self
129
128
130 l = list = property(get_list)
129 l = list = property(get_list)
131
130
132 def get_spstr(self):
131 def get_spstr(self):
133 try:
132 try:
134 return self.__spstr
133 return self.__spstr
135 except AttributeError:
134 except AttributeError:
136 self.__spstr = ' '.join(self)
135 self.__spstr = ' '.join(self)
137 return self.__spstr
136 return self.__spstr
138
137
139 s = spstr = property(get_spstr)
138 s = spstr = property(get_spstr)
140
139
141 def get_nlstr(self):
140 def get_nlstr(self):
142 try:
141 try:
143 return self.__nlstr
142 return self.__nlstr
144 except AttributeError:
143 except AttributeError:
145 self.__nlstr = '\n'.join(self)
144 self.__nlstr = '\n'.join(self)
146 return self.__nlstr
145 return self.__nlstr
147
146
148 n = nlstr = property(get_nlstr)
147 n = nlstr = property(get_nlstr)
149
148
150 def get_paths(self):
149 def get_paths(self):
151 try:
150 try:
152 return self.__paths
151 return self.__paths
153 except AttributeError:
152 except AttributeError:
154 self.__paths = [path(p) for p in self if os.path.exists(p)]
153 self.__paths = [path(p) for p in self if os.path.exists(p)]
155 return self.__paths
154 return self.__paths
156
155
157 p = paths = property(get_paths)
156 p = paths = property(get_paths)
158
157
159 def grep(self, pattern, prune = False, field = None):
158 def grep(self, pattern, prune = False, field = None):
160 """ Return all strings matching 'pattern' (a regex or callable)
159 """ Return all strings matching 'pattern' (a regex or callable)
161
160
162 This is case-insensitive. If prune is true, return all items
161 This is case-insensitive. If prune is true, return all items
163 NOT matching the pattern.
162 NOT matching the pattern.
164
163
165 If field is specified, the match must occur in the specified
164 If field is specified, the match must occur in the specified
166 whitespace-separated field.
165 whitespace-separated field.
167
166
168 Examples::
167 Examples::
169
168
170 a.grep( lambda x: x.startswith('C') )
169 a.grep( lambda x: x.startswith('C') )
171 a.grep('Cha.*log', prune=1)
170 a.grep('Cha.*log', prune=1)
172 a.grep('chm', field=-1)
171 a.grep('chm', field=-1)
173 """
172 """
174
173
175 def match_target(s):
174 def match_target(s):
176 if field is None:
175 if field is None:
177 return s
176 return s
178 parts = s.split()
177 parts = s.split()
179 try:
178 try:
180 tgt = parts[field]
179 tgt = parts[field]
181 return tgt
180 return tgt
182 except IndexError:
181 except IndexError:
183 return ""
182 return ""
184
183
185 if isinstance(pattern, basestring):
184 if isinstance(pattern, basestring):
186 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
185 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
187 else:
186 else:
188 pred = pattern
187 pred = pattern
189 if not prune:
188 if not prune:
190 return SList([el for el in self if pred(match_target(el))])
189 return SList([el for el in self if pred(match_target(el))])
191 else:
190 else:
192 return SList([el for el in self if not pred(match_target(el))])
191 return SList([el for el in self if not pred(match_target(el))])
193
192
194 def fields(self, *fields):
193 def fields(self, *fields):
195 """ Collect whitespace-separated fields from string list
194 """ Collect whitespace-separated fields from string list
196
195
197 Allows quick awk-like usage of string lists.
196 Allows quick awk-like usage of string lists.
198
197
199 Example data (in var a, created by 'a = !ls -l')::
198 Example data (in var a, created by 'a = !ls -l')::
200 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
199 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
201 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
200 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
202
201
203 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
202 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
204 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
203 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
205 (note the joining by space).
204 (note the joining by space).
206 a.fields(-1) is ['ChangeLog', 'IPython']
205 a.fields(-1) is ['ChangeLog', 'IPython']
207
206
208 IndexErrors are ignored.
207 IndexErrors are ignored.
209
208
210 Without args, fields() just split()'s the strings.
209 Without args, fields() just split()'s the strings.
211 """
210 """
212 if len(fields) == 0:
211 if len(fields) == 0:
213 return [el.split() for el in self]
212 return [el.split() for el in self]
214
213
215 res = SList()
214 res = SList()
216 for el in [f.split() for f in self]:
215 for el in [f.split() for f in self]:
217 lineparts = []
216 lineparts = []
218
217
219 for fd in fields:
218 for fd in fields:
220 try:
219 try:
221 lineparts.append(el[fd])
220 lineparts.append(el[fd])
222 except IndexError:
221 except IndexError:
223 pass
222 pass
224 if lineparts:
223 if lineparts:
225 res.append(" ".join(lineparts))
224 res.append(" ".join(lineparts))
226
225
227 return res
226 return res
228
227
229 def sort(self,field= None, nums = False):
228 def sort(self,field= None, nums = False):
230 """ sort by specified fields (see fields())
229 """ sort by specified fields (see fields())
231
230
232 Example::
231 Example::
233 a.sort(1, nums = True)
232 a.sort(1, nums = True)
234
233
235 Sorts a by second field, in numerical order (so that 21 > 3)
234 Sorts a by second field, in numerical order (so that 21 > 3)
236
235
237 """
236 """
238
237
239 #decorate, sort, undecorate
238 #decorate, sort, undecorate
240 if field is not None:
239 if field is not None:
241 dsu = [[SList([line]).fields(field), line] for line in self]
240 dsu = [[SList([line]).fields(field), line] for line in self]
242 else:
241 else:
243 dsu = [[line, line] for line in self]
242 dsu = [[line, line] for line in self]
244 if nums:
243 if nums:
245 for i in range(len(dsu)):
244 for i in range(len(dsu)):
246 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
245 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
247 try:
246 try:
248 n = int(numstr)
247 n = int(numstr)
249 except ValueError:
248 except ValueError:
250 n = 0;
249 n = 0;
251 dsu[i][0] = n
250 dsu[i][0] = n
252
251
253
252
254 dsu.sort()
253 dsu.sort()
255 return SList([t[1] for t in dsu])
254 return SList([t[1] for t in dsu])
256
255
257
256
258 # FIXME: We need to reimplement type specific displayhook and then add this
257 # FIXME: We need to reimplement type specific displayhook and then add this
259 # back as a custom printer. This should also be moved outside utils into the
258 # back as a custom printer. This should also be moved outside utils into the
260 # core.
259 # core.
261
260
262 # def print_slist(arg):
261 # def print_slist(arg):
263 # """ Prettier (non-repr-like) and more informative printer for SList """
262 # """ Prettier (non-repr-like) and more informative printer for SList """
264 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
263 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
265 # if hasattr(arg, 'hideonce') and arg.hideonce:
264 # if hasattr(arg, 'hideonce') and arg.hideonce:
266 # arg.hideonce = False
265 # arg.hideonce = False
267 # return
266 # return
268 #
267 #
269 # nlprint(arg)
268 # nlprint(arg)
270 #
269 #
271 # print_slist = result_display.when_type(SList)(print_slist)
270 # print_slist = result_display.when_type(SList)(print_slist)
272
271
273
272
274 def esc_quotes(strng):
273 def esc_quotes(strng):
275 """Return the input string with single and double quotes escaped out"""
274 """Return the input string with single and double quotes escaped out"""
276
275
277 return strng.replace('"','\\"').replace("'","\\'")
276 return strng.replace('"','\\"').replace("'","\\'")
278
277
279
278
280 def qw(words,flat=0,sep=None,maxsplit=-1):
279 def qw(words,flat=0,sep=None,maxsplit=-1):
281 """Similar to Perl's qw() operator, but with some more options.
280 """Similar to Perl's qw() operator, but with some more options.
282
281
283 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
282 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
284
283
285 words can also be a list itself, and with flat=1, the output will be
284 words can also be a list itself, and with flat=1, the output will be
286 recursively flattened.
285 recursively flattened.
287
286
288 Examples:
287 Examples:
289
288
290 >>> qw('1 2')
289 >>> qw('1 2')
291 ['1', '2']
290 ['1', '2']
292
291
293 >>> qw(['a b','1 2',['m n','p q']])
292 >>> qw(['a b','1 2',['m n','p q']])
294 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
293 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
295
294
296 >>> qw(['a b','1 2',['m n','p q']],flat=1)
295 >>> qw(['a b','1 2',['m n','p q']],flat=1)
297 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
296 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
298 """
297 """
299
298
300 if isinstance(words, basestring):
299 if isinstance(words, basestring):
301 return [word.strip() for word in words.split(sep,maxsplit)
300 return [word.strip() for word in words.split(sep,maxsplit)
302 if word and not word.isspace() ]
301 if word and not word.isspace() ]
303 if flat:
302 if flat:
304 return flatten(map(qw,words,[1]*len(words)))
303 return flatten(map(qw,words,[1]*len(words)))
305 return map(qw,words)
304 return map(qw,words)
306
305
307
306
308 def qwflat(words,sep=None,maxsplit=-1):
307 def qwflat(words,sep=None,maxsplit=-1):
309 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
308 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
310 return qw(words,1,sep,maxsplit)
309 return qw(words,1,sep,maxsplit)
311
310
312
311
313 def qw_lol(indata):
312 def qw_lol(indata):
314 """qw_lol('a b') -> [['a','b']],
313 """qw_lol('a b') -> [['a','b']],
315 otherwise it's just a call to qw().
314 otherwise it's just a call to qw().
316
315
317 We need this to make sure the modules_some keys *always* end up as a
316 We need this to make sure the modules_some keys *always* end up as a
318 list of lists."""
317 list of lists."""
319
318
320 if isinstance(indata, basestring):
319 if isinstance(indata, basestring):
321 return [qw(indata)]
320 return [qw(indata)]
322 else:
321 else:
323 return qw(indata)
322 return qw(indata)
324
323
325
324
326 def grep(pat,list,case=1):
325 def grep(pat,list,case=1):
327 """Simple minded grep-like function.
326 """Simple minded grep-like function.
328 grep(pat,list) returns occurrences of pat in list, None on failure.
327 grep(pat,list) returns occurrences of pat in list, None on failure.
329
328
330 It only does simple string matching, with no support for regexps. Use the
329 It only does simple string matching, with no support for regexps. Use the
331 option case=0 for case-insensitive matching."""
330 option case=0 for case-insensitive matching."""
332
331
333 # This is pretty crude. At least it should implement copying only references
332 # This is pretty crude. At least it should implement copying only references
334 # to the original data in case it's big. Now it copies the data for output.
333 # to the original data in case it's big. Now it copies the data for output.
335 out=[]
334 out=[]
336 if case:
335 if case:
337 for term in list:
336 for term in list:
338 if term.find(pat)>-1: out.append(term)
337 if term.find(pat)>-1: out.append(term)
339 else:
338 else:
340 lpat=pat.lower()
339 lpat=pat.lower()
341 for term in list:
340 for term in list:
342 if term.lower().find(lpat)>-1: out.append(term)
341 if term.lower().find(lpat)>-1: out.append(term)
343
342
344 if len(out): return out
343 if len(out): return out
345 else: return None
344 else: return None
346
345
347
346
348 def dgrep(pat,*opts):
347 def dgrep(pat,*opts):
349 """Return grep() on dir()+dir(__builtins__).
348 """Return grep() on dir()+dir(__builtins__).
350
349
351 A very common use of grep() when working interactively."""
350 A very common use of grep() when working interactively."""
352
351
353 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
352 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
354
353
355
354
356 def idgrep(pat):
355 def idgrep(pat):
357 """Case-insensitive dgrep()"""
356 """Case-insensitive dgrep()"""
358
357
359 return dgrep(pat,0)
358 return dgrep(pat,0)
360
359
361
360
362 def igrep(pat,list):
361 def igrep(pat,list):
363 """Synonym for case-insensitive grep."""
362 """Synonym for case-insensitive grep."""
364
363
365 return grep(pat,list,case=0)
364 return grep(pat,list,case=0)
366
365
367
366
368 def indent(instr,nspaces=4, ntabs=0, flatten=False):
367 def indent(instr,nspaces=4, ntabs=0, flatten=False):
369 """Indent a string a given number of spaces or tabstops.
368 """Indent a string a given number of spaces or tabstops.
370
369
371 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
370 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
372
371
373 Parameters
372 Parameters
374 ----------
373 ----------
375
374
376 instr : basestring
375 instr : basestring
377 The string to be indented.
376 The string to be indented.
378 nspaces : int (default: 4)
377 nspaces : int (default: 4)
379 The number of spaces to be indented.
378 The number of spaces to be indented.
380 ntabs : int (default: 0)
379 ntabs : int (default: 0)
381 The number of tabs to be indented.
380 The number of tabs to be indented.
382 flatten : bool (default: False)
381 flatten : bool (default: False)
383 Whether to scrub existing indentation. If True, all lines will be
382 Whether to scrub existing indentation. If True, all lines will be
384 aligned to the same indentation. If False, existing indentation will
383 aligned to the same indentation. If False, existing indentation will
385 be strictly increased.
384 be strictly increased.
386
385
387 Returns
386 Returns
388 -------
387 -------
389
388
390 str|unicode : string indented by ntabs and nspaces.
389 str|unicode : string indented by ntabs and nspaces.
391
390
392 """
391 """
393 if instr is None:
392 if instr is None:
394 return
393 return
395 ind = '\t'*ntabs+' '*nspaces
394 ind = '\t'*ntabs+' '*nspaces
396 if flatten:
395 if flatten:
397 pat = re.compile(r'^\s*', re.MULTILINE)
396 pat = re.compile(r'^\s*', re.MULTILINE)
398 else:
397 else:
399 pat = re.compile(r'^', re.MULTILINE)
398 pat = re.compile(r'^', re.MULTILINE)
400 outstr = re.sub(pat, ind, instr)
399 outstr = re.sub(pat, ind, instr)
401 if outstr.endswith(os.linesep+ind):
400 if outstr.endswith(os.linesep+ind):
402 return outstr[:-len(ind)]
401 return outstr[:-len(ind)]
403 else:
402 else:
404 return outstr
403 return outstr
405
404
406 def native_line_ends(filename,backup=1):
407 """Convert (in-place) a file to line-ends native to the current OS.
408
409 If the optional backup argument is given as false, no backup of the
410 original file is left. """
411
412 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
413
414 bak_filename = filename + backup_suffixes[os.name]
415
416 original = open(filename).read()
417 shutil.copy2(filename,bak_filename)
418 try:
419 new = open(filename,'wb')
420 new.write(os.linesep.join(original.splitlines()))
421 new.write(os.linesep) # ALWAYS put an eol at the end of the file
422 new.close()
423 except:
424 os.rename(bak_filename,filename)
425 if not backup:
426 try:
427 os.remove(bak_filename)
428 except:
429 pass
430
431
405
432 def list_strings(arg):
406 def list_strings(arg):
433 """Always return a list of strings, given a string or list of strings
407 """Always return a list of strings, given a string or list of strings
434 as input.
408 as input.
435
409
436 :Examples:
410 :Examples:
437
411
438 In [7]: list_strings('A single string')
412 In [7]: list_strings('A single string')
439 Out[7]: ['A single string']
413 Out[7]: ['A single string']
440
414
441 In [8]: list_strings(['A single string in a list'])
415 In [8]: list_strings(['A single string in a list'])
442 Out[8]: ['A single string in a list']
416 Out[8]: ['A single string in a list']
443
417
444 In [9]: list_strings(['A','list','of','strings'])
418 In [9]: list_strings(['A','list','of','strings'])
445 Out[9]: ['A', 'list', 'of', 'strings']
419 Out[9]: ['A', 'list', 'of', 'strings']
446 """
420 """
447
421
448 if isinstance(arg,basestring): return [arg]
422 if isinstance(arg,basestring): return [arg]
449 else: return arg
423 else: return arg
450
424
451
425
452 def marquee(txt='',width=78,mark='*'):
426 def marquee(txt='',width=78,mark='*'):
453 """Return the input string centered in a 'marquee'.
427 """Return the input string centered in a 'marquee'.
454
428
455 :Examples:
429 :Examples:
456
430
457 In [16]: marquee('A test',40)
431 In [16]: marquee('A test',40)
458 Out[16]: '**************** A test ****************'
432 Out[16]: '**************** A test ****************'
459
433
460 In [17]: marquee('A test',40,'-')
434 In [17]: marquee('A test',40,'-')
461 Out[17]: '---------------- A test ----------------'
435 Out[17]: '---------------- A test ----------------'
462
436
463 In [18]: marquee('A test',40,' ')
437 In [18]: marquee('A test',40,' ')
464 Out[18]: ' A test '
438 Out[18]: ' A test '
465
439
466 """
440 """
467 if not txt:
441 if not txt:
468 return (mark*width)[:width]
442 return (mark*width)[:width]
469 nmark = (width-len(txt)-2)//len(mark)//2
443 nmark = (width-len(txt)-2)//len(mark)//2
470 if nmark < 0: nmark =0
444 if nmark < 0: nmark =0
471 marks = mark*nmark
445 marks = mark*nmark
472 return '%s %s %s' % (marks,txt,marks)
446 return '%s %s %s' % (marks,txt,marks)
473
447
474
448
475 ini_spaces_re = re.compile(r'^(\s+)')
449 ini_spaces_re = re.compile(r'^(\s+)')
476
450
477 def num_ini_spaces(strng):
451 def num_ini_spaces(strng):
478 """Return the number of initial spaces in a string"""
452 """Return the number of initial spaces in a string"""
479
453
480 ini_spaces = ini_spaces_re.match(strng)
454 ini_spaces = ini_spaces_re.match(strng)
481 if ini_spaces:
455 if ini_spaces:
482 return ini_spaces.end()
456 return ini_spaces.end()
483 else:
457 else:
484 return 0
458 return 0
485
459
486
460
487 def format_screen(strng):
461 def format_screen(strng):
488 """Format a string for screen printing.
462 """Format a string for screen printing.
489
463
490 This removes some latex-type format codes."""
464 This removes some latex-type format codes."""
491 # Paragraph continue
465 # Paragraph continue
492 par_re = re.compile(r'\\$',re.MULTILINE)
466 par_re = re.compile(r'\\$',re.MULTILINE)
493 strng = par_re.sub('',strng)
467 strng = par_re.sub('',strng)
494 return strng
468 return strng
495
469
496
470
497 def dedent(text):
471 def dedent(text):
498 """Equivalent of textwrap.dedent that ignores unindented first line.
472 """Equivalent of textwrap.dedent that ignores unindented first line.
499
473
500 This means it will still dedent strings like:
474 This means it will still dedent strings like:
501 '''foo
475 '''foo
502 is a bar
476 is a bar
503 '''
477 '''
504
478
505 For use in wrap_paragraphs.
479 For use in wrap_paragraphs.
506 """
480 """
507
481
508 if text.startswith('\n'):
482 if text.startswith('\n'):
509 # text starts with blank line, don't ignore the first line
483 # text starts with blank line, don't ignore the first line
510 return textwrap.dedent(text)
484 return textwrap.dedent(text)
511
485
512 # split first line
486 # split first line
513 splits = text.split('\n',1)
487 splits = text.split('\n',1)
514 if len(splits) == 1:
488 if len(splits) == 1:
515 # only one line
489 # only one line
516 return textwrap.dedent(text)
490 return textwrap.dedent(text)
517
491
518 first, rest = splits
492 first, rest = splits
519 # dedent everything but the first line
493 # dedent everything but the first line
520 rest = textwrap.dedent(rest)
494 rest = textwrap.dedent(rest)
521 return '\n'.join([first, rest])
495 return '\n'.join([first, rest])
522
496
523
497
524 def wrap_paragraphs(text, ncols=80):
498 def wrap_paragraphs(text, ncols=80):
525 """Wrap multiple paragraphs to fit a specified width.
499 """Wrap multiple paragraphs to fit a specified width.
526
500
527 This is equivalent to textwrap.wrap, but with support for multiple
501 This is equivalent to textwrap.wrap, but with support for multiple
528 paragraphs, as separated by empty lines.
502 paragraphs, as separated by empty lines.
529
503
530 Returns
504 Returns
531 -------
505 -------
532
506
533 list of complete paragraphs, wrapped to fill `ncols` columns.
507 list of complete paragraphs, wrapped to fill `ncols` columns.
534 """
508 """
535 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
509 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
536 text = dedent(text).strip()
510 text = dedent(text).strip()
537 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
511 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
538 out_ps = []
512 out_ps = []
539 indent_re = re.compile(r'\n\s+', re.MULTILINE)
513 indent_re = re.compile(r'\n\s+', re.MULTILINE)
540 for p in paragraphs:
514 for p in paragraphs:
541 # presume indentation that survives dedent is meaningful formatting,
515 # presume indentation that survives dedent is meaningful formatting,
542 # so don't fill unless text is flush.
516 # so don't fill unless text is flush.
543 if indent_re.search(p) is None:
517 if indent_re.search(p) is None:
544 # wrap paragraph
518 # wrap paragraph
545 p = textwrap.fill(p, ncols)
519 p = textwrap.fill(p, ncols)
546 out_ps.append(p)
520 out_ps.append(p)
547 return out_ps
521 return out_ps
548
522
549
523
550 def long_substr(data):
524 def long_substr(data):
551 """Return the longest common substring in a list of strings.
525 """Return the longest common substring in a list of strings.
552
526
553 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
527 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
554 """
528 """
555 substr = ''
529 substr = ''
556 if len(data) > 1 and len(data[0]) > 0:
530 if len(data) > 1 and len(data[0]) > 0:
557 for i in range(len(data[0])):
531 for i in range(len(data[0])):
558 for j in range(len(data[0])-i+1):
532 for j in range(len(data[0])-i+1):
559 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
533 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
560 substr = data[0][i:i+j]
534 substr = data[0][i:i+j]
561 elif len(data) == 1:
535 elif len(data) == 1:
562 substr = data[0]
536 substr = data[0]
563 return substr
537 return substr
564
538
565
539
566 def strip_email_quotes(text):
540 def strip_email_quotes(text):
567 """Strip leading email quotation characters ('>').
541 """Strip leading email quotation characters ('>').
568
542
569 Removes any combination of leading '>' interspersed with whitespace that
543 Removes any combination of leading '>' interspersed with whitespace that
570 appears *identically* in all lines of the input text.
544 appears *identically* in all lines of the input text.
571
545
572 Parameters
546 Parameters
573 ----------
547 ----------
574 text : str
548 text : str
575
549
576 Examples
550 Examples
577 --------
551 --------
578
552
579 Simple uses::
553 Simple uses::
580
554
581 In [2]: strip_email_quotes('> > text')
555 In [2]: strip_email_quotes('> > text')
582 Out[2]: 'text'
556 Out[2]: 'text'
583
557
584 In [3]: strip_email_quotes('> > text\\n> > more')
558 In [3]: strip_email_quotes('> > text\\n> > more')
585 Out[3]: 'text\\nmore'
559 Out[3]: 'text\\nmore'
586
560
587 Note how only the common prefix that appears in all lines is stripped::
561 Note how only the common prefix that appears in all lines is stripped::
588
562
589 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
563 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
590 Out[4]: '> text\\n> more\\nmore...'
564 Out[4]: '> text\\n> more\\nmore...'
591
565
592 So if any line has no quote marks ('>') , then none are stripped from any
566 So if any line has no quote marks ('>') , then none are stripped from any
593 of them ::
567 of them ::
594
568
595 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
569 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
596 Out[5]: '> > text\\n> > more\\nlast different'
570 Out[5]: '> > text\\n> > more\\nlast different'
597 """
571 """
598 lines = text.splitlines()
572 lines = text.splitlines()
599 matches = set()
573 matches = set()
600 for line in lines:
574 for line in lines:
601 prefix = re.match(r'^(\s*>[ >]*)', line)
575 prefix = re.match(r'^(\s*>[ >]*)', line)
602 if prefix:
576 if prefix:
603 matches.add(prefix.group(1))
577 matches.add(prefix.group(1))
604 else:
578 else:
605 break
579 break
606 else:
580 else:
607 prefix = long_substr(list(matches))
581 prefix = long_substr(list(matches))
608 if prefix:
582 if prefix:
609 strip = len(prefix)
583 strip = len(prefix)
610 text = '\n'.join([ ln[strip:] for ln in lines])
584 text = '\n'.join([ ln[strip:] for ln in lines])
611 return text
585 return text
612
586
613
587
614 class EvalFormatter(Formatter):
588 class EvalFormatter(Formatter):
615 """A String Formatter that allows evaluation of simple expressions.
589 """A String Formatter that allows evaluation of simple expressions.
616
590
617 Note that this version interprets a : as specifying a format string (as per
591 Note that this version interprets a : as specifying a format string (as per
618 standard string formatting), so if slicing is required, you must explicitly
592 standard string formatting), so if slicing is required, you must explicitly
619 create a slice.
593 create a slice.
620
594
621 This is to be used in templating cases, such as the parallel batch
595 This is to be used in templating cases, such as the parallel batch
622 script templates, where simple arithmetic on arguments is useful.
596 script templates, where simple arithmetic on arguments is useful.
623
597
624 Examples
598 Examples
625 --------
599 --------
626
600
627 In [1]: f = EvalFormatter()
601 In [1]: f = EvalFormatter()
628 In [2]: f.format('{n//4}', n=8)
602 In [2]: f.format('{n//4}', n=8)
629 Out [2]: '2'
603 Out [2]: '2'
630
604
631 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
605 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
632 Out [3]: 'll'
606 Out [3]: 'll'
633 """
607 """
634 def get_field(self, name, args, kwargs):
608 def get_field(self, name, args, kwargs):
635 v = eval(name, kwargs)
609 v = eval(name, kwargs)
636 return v, name
610 return v, name
637
611
638
612
639 @skip_doctest_py3
613 @skip_doctest_py3
640 class FullEvalFormatter(Formatter):
614 class FullEvalFormatter(Formatter):
641 """A String Formatter that allows evaluation of simple expressions.
615 """A String Formatter that allows evaluation of simple expressions.
642
616
643 Any time a format key is not found in the kwargs,
617 Any time a format key is not found in the kwargs,
644 it will be tried as an expression in the kwargs namespace.
618 it will be tried as an expression in the kwargs namespace.
645
619
646 Note that this version allows slicing using [1:2], so you cannot specify
620 Note that this version allows slicing using [1:2], so you cannot specify
647 a format string. Use :class:`EvalFormatter` to permit format strings.
621 a format string. Use :class:`EvalFormatter` to permit format strings.
648
622
649 Examples
623 Examples
650 --------
624 --------
651
625
652 In [1]: f = FullEvalFormatter()
626 In [1]: f = FullEvalFormatter()
653 In [2]: f.format('{n//4}', n=8)
627 In [2]: f.format('{n//4}', n=8)
654 Out[2]: u'2'
628 Out[2]: u'2'
655
629
656 In [3]: f.format('{list(range(5))[2:4]}')
630 In [3]: f.format('{list(range(5))[2:4]}')
657 Out[3]: u'[2, 3]'
631 Out[3]: u'[2, 3]'
658
632
659 In [4]: f.format('{3*2}')
633 In [4]: f.format('{3*2}')
660 Out[4]: u'6'
634 Out[4]: u'6'
661 """
635 """
662 # copied from Formatter._vformat with minor changes to allow eval
636 # copied from Formatter._vformat with minor changes to allow eval
663 # and replace the format_spec code with slicing
637 # and replace the format_spec code with slicing
664 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
638 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
665 if recursion_depth < 0:
639 if recursion_depth < 0:
666 raise ValueError('Max string recursion exceeded')
640 raise ValueError('Max string recursion exceeded')
667 result = []
641 result = []
668 for literal_text, field_name, format_spec, conversion in \
642 for literal_text, field_name, format_spec, conversion in \
669 self.parse(format_string):
643 self.parse(format_string):
670
644
671 # output the literal text
645 # output the literal text
672 if literal_text:
646 if literal_text:
673 result.append(literal_text)
647 result.append(literal_text)
674
648
675 # if there's a field, output it
649 # if there's a field, output it
676 if field_name is not None:
650 if field_name is not None:
677 # this is some markup, find the object and do
651 # this is some markup, find the object and do
678 # the formatting
652 # the formatting
679
653
680 if format_spec:
654 if format_spec:
681 # override format spec, to allow slicing:
655 # override format spec, to allow slicing:
682 field_name = ':'.join([field_name, format_spec])
656 field_name = ':'.join([field_name, format_spec])
683
657
684 # eval the contents of the field for the object
658 # eval the contents of the field for the object
685 # to be formatted
659 # to be formatted
686 obj = eval(field_name, kwargs)
660 obj = eval(field_name, kwargs)
687
661
688 # do any conversion on the resulting object
662 # do any conversion on the resulting object
689 obj = self.convert_field(obj, conversion)
663 obj = self.convert_field(obj, conversion)
690
664
691 # format the object and append to the result
665 # format the object and append to the result
692 result.append(self.format_field(obj, ''))
666 result.append(self.format_field(obj, ''))
693
667
694 return u''.join(py3compat.cast_unicode(s) for s in result)
668 return u''.join(py3compat.cast_unicode(s) for s in result)
695
669
696
670
697 @skip_doctest_py3
671 @skip_doctest_py3
698 class DollarFormatter(FullEvalFormatter):
672 class DollarFormatter(FullEvalFormatter):
699 """Formatter allowing Itpl style $foo replacement, for names and attribute
673 """Formatter allowing Itpl style $foo replacement, for names and attribute
700 access only. Standard {foo} replacement also works, and allows full
674 access only. Standard {foo} replacement also works, and allows full
701 evaluation of its arguments.
675 evaluation of its arguments.
702
676
703 Examples
677 Examples
704 --------
678 --------
705 In [1]: f = DollarFormatter()
679 In [1]: f = DollarFormatter()
706 In [2]: f.format('{n//4}', n=8)
680 In [2]: f.format('{n//4}', n=8)
707 Out[2]: u'2'
681 Out[2]: u'2'
708
682
709 In [3]: f.format('23 * 76 is $result', result=23*76)
683 In [3]: f.format('23 * 76 is $result', result=23*76)
710 Out[3]: u'23 * 76 is 1748'
684 Out[3]: u'23 * 76 is 1748'
711
685
712 In [4]: f.format('$a or {b}', a=1, b=2)
686 In [4]: f.format('$a or {b}', a=1, b=2)
713 Out[4]: u'1 or 2'
687 Out[4]: u'1 or 2'
714 """
688 """
715 _dollar_pattern = re.compile("(.*?)\$(\$?[\w\.]+)")
689 _dollar_pattern = re.compile("(.*?)\$(\$?[\w\.]+)")
716 def parse(self, fmt_string):
690 def parse(self, fmt_string):
717 for literal_txt, field_name, format_spec, conversion \
691 for literal_txt, field_name, format_spec, conversion \
718 in Formatter.parse(self, fmt_string):
692 in Formatter.parse(self, fmt_string):
719
693
720 # Find $foo patterns in the literal text.
694 # Find $foo patterns in the literal text.
721 continue_from = 0
695 continue_from = 0
722 txt = ""
696 txt = ""
723 for m in self._dollar_pattern.finditer(literal_txt):
697 for m in self._dollar_pattern.finditer(literal_txt):
724 new_txt, new_field = m.group(1,2)
698 new_txt, new_field = m.group(1,2)
725 # $$foo --> $foo
699 # $$foo --> $foo
726 if new_field.startswith("$"):
700 if new_field.startswith("$"):
727 txt += new_txt + new_field
701 txt += new_txt + new_field
728 else:
702 else:
729 yield (txt + new_txt, new_field, "", None)
703 yield (txt + new_txt, new_field, "", None)
730 txt = ""
704 txt = ""
731 continue_from = m.end()
705 continue_from = m.end()
732
706
733 # Re-yield the {foo} style pattern
707 # Re-yield the {foo} style pattern
734 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
708 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
735
709
736 #-----------------------------------------------------------------------------
710 #-----------------------------------------------------------------------------
737 # Utils to columnize a list of string
711 # Utils to columnize a list of string
738 #-----------------------------------------------------------------------------
712 #-----------------------------------------------------------------------------
739
713
740 def _chunks(l, n):
714 def _chunks(l, n):
741 """Yield successive n-sized chunks from l."""
715 """Yield successive n-sized chunks from l."""
742 for i in xrange(0, len(l), n):
716 for i in xrange(0, len(l), n):
743 yield l[i:i+n]
717 yield l[i:i+n]
744
718
745
719
746 def _find_optimal(rlist , separator_size=2 , displaywidth=80):
720 def _find_optimal(rlist , separator_size=2 , displaywidth=80):
747 """Calculate optimal info to columnize a list of string"""
721 """Calculate optimal info to columnize a list of string"""
748 for nrow in range(1, len(rlist)+1) :
722 for nrow in range(1, len(rlist)+1) :
749 chk = map(max,_chunks(rlist, nrow))
723 chk = map(max,_chunks(rlist, nrow))
750 sumlength = sum(chk)
724 sumlength = sum(chk)
751 ncols = len(chk)
725 ncols = len(chk)
752 if sumlength+separator_size*(ncols-1) <= displaywidth :
726 if sumlength+separator_size*(ncols-1) <= displaywidth :
753 break;
727 break;
754 return {'columns_numbers' : ncols,
728 return {'columns_numbers' : ncols,
755 'optimal_separator_width':(displaywidth - sumlength)/(ncols-1) if (ncols -1) else 0,
729 'optimal_separator_width':(displaywidth - sumlength)/(ncols-1) if (ncols -1) else 0,
756 'rows_numbers' : nrow,
730 'rows_numbers' : nrow,
757 'columns_width' : chk
731 'columns_width' : chk
758 }
732 }
759
733
760
734
761 def _get_or_default(mylist, i, default=None):
735 def _get_or_default(mylist, i, default=None):
762 """return list item number, or default if don't exist"""
736 """return list item number, or default if don't exist"""
763 if i >= len(mylist):
737 if i >= len(mylist):
764 return default
738 return default
765 else :
739 else :
766 return mylist[i]
740 return mylist[i]
767
741
768
742
769 @skip_doctest
743 @skip_doctest
770 def compute_item_matrix(items, empty=None, *args, **kwargs) :
744 def compute_item_matrix(items, empty=None, *args, **kwargs) :
771 """Returns a nested list, and info to columnize items
745 """Returns a nested list, and info to columnize items
772
746
773 Parameters
747 Parameters
774 ----------
748 ----------
775
749
776 items :
750 items :
777 list of strings to columize
751 list of strings to columize
778 empty : (default None)
752 empty : (default None)
779 default value to fill list if needed
753 default value to fill list if needed
780 separator_size : int (default=2)
754 separator_size : int (default=2)
781 How much caracters will be used as a separation between each columns.
755 How much caracters will be used as a separation between each columns.
782 displaywidth : int (default=80)
756 displaywidth : int (default=80)
783 The width of the area onto wich the columns should enter
757 The width of the area onto wich the columns should enter
784
758
785 Returns
759 Returns
786 -------
760 -------
787
761
788 Returns a tuple of (strings_matrix, dict_info)
762 Returns a tuple of (strings_matrix, dict_info)
789
763
790 strings_matrix :
764 strings_matrix :
791
765
792 nested list of string, the outer most list contains as many list as
766 nested list of string, the outer most list contains as many list as
793 rows, the innermost lists have each as many element as colums. If the
767 rows, the innermost lists have each as many element as colums. If the
794 total number of elements in `items` does not equal the product of
768 total number of elements in `items` does not equal the product of
795 rows*columns, the last element of some lists are filled with `None`.
769 rows*columns, the last element of some lists are filled with `None`.
796
770
797 dict_info :
771 dict_info :
798 some info to make columnize easier:
772 some info to make columnize easier:
799
773
800 columns_numbers : number of columns
774 columns_numbers : number of columns
801 rows_numbers : number of rows
775 rows_numbers : number of rows
802 columns_width : list of with of each columns
776 columns_width : list of with of each columns
803 optimal_separator_width : best separator width between columns
777 optimal_separator_width : best separator width between columns
804
778
805 Examples
779 Examples
806 --------
780 --------
807
781
808 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
782 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
809 ...: compute_item_matrix(l,displaywidth=12)
783 ...: compute_item_matrix(l,displaywidth=12)
810 Out[1]:
784 Out[1]:
811 ([['aaa', 'f', 'k'],
785 ([['aaa', 'f', 'k'],
812 ['b', 'g', 'l'],
786 ['b', 'g', 'l'],
813 ['cc', 'h', None],
787 ['cc', 'h', None],
814 ['d', 'i', None],
788 ['d', 'i', None],
815 ['eeeee', 'j', None]],
789 ['eeeee', 'j', None]],
816 {'columns_numbers': 3,
790 {'columns_numbers': 3,
817 'columns_width': [5, 1, 1],
791 'columns_width': [5, 1, 1],
818 'optimal_separator_width': 2,
792 'optimal_separator_width': 2,
819 'rows_numbers': 5})
793 'rows_numbers': 5})
820
794
821 """
795 """
822 info = _find_optimal(map(len, items), *args, **kwargs)
796 info = _find_optimal(map(len, items), *args, **kwargs)
823 nrow, ncol = info['rows_numbers'], info['columns_numbers']
797 nrow, ncol = info['rows_numbers'], info['columns_numbers']
824 return ([[ _get_or_default(items, c*nrow+i, default=empty) for c in range(ncol) ] for i in range(nrow) ], info)
798 return ([[ _get_or_default(items, c*nrow+i, default=empty) for c in range(ncol) ] for i in range(nrow) ], info)
825
799
826
800
827 def columnize(items, separator=' ', displaywidth=80):
801 def columnize(items, separator=' ', displaywidth=80):
828 """ Transform a list of strings into a single string with columns.
802 """ Transform a list of strings into a single string with columns.
829
803
830 Parameters
804 Parameters
831 ----------
805 ----------
832 items : sequence of strings
806 items : sequence of strings
833 The strings to process.
807 The strings to process.
834
808
835 separator : str, optional [default is two spaces]
809 separator : str, optional [default is two spaces]
836 The string that separates columns.
810 The string that separates columns.
837
811
838 displaywidth : int, optional [default is 80]
812 displaywidth : int, optional [default is 80]
839 Width of the display in number of characters.
813 Width of the display in number of characters.
840
814
841 Returns
815 Returns
842 -------
816 -------
843 The formatted string.
817 The formatted string.
844 """
818 """
845 if not items :
819 if not items :
846 return '\n'
820 return '\n'
847 matrix, info = compute_item_matrix(items, separator_size=len(separator), displaywidth=displaywidth)
821 matrix, info = compute_item_matrix(items, separator_size=len(separator), displaywidth=displaywidth)
848 fmatrix = [filter(None, x) for x in matrix]
822 fmatrix = [filter(None, x) for x in matrix]
849 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['columns_width'])])
823 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['columns_width'])])
850 return '\n'.join(map(sjoin, fmatrix))+'\n'
824 return '\n'.join(map(sjoin, fmatrix))+'\n'
General Comments 0
You need to be logged in to leave comments. Login now