##// END OF EJS Templates
fix plural and embeded
Matthias BUSSONNIER -
Show More
@@ -1,600 +1,603 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A base class for a configurable application.
3 A base class for a configurable application.
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 * Min RK
8 * Min RK
9 """
9 """
10 from __future__ import print_function
10 from __future__ import print_function
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2011 The IPython Development Team
13 # Copyright (C) 2008-2011 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 import logging
23 import logging
24 import os
24 import os
25 import re
25 import re
26 import sys
26 import sys
27 from copy import deepcopy
27 from copy import deepcopy
28 from collections import defaultdict
28 from collections import defaultdict
29
29
30 from IPython.external.decorator import decorator
30 from IPython.external.decorator import decorator
31
31
32 from IPython.config.configurable import SingletonConfigurable
32 from IPython.config.configurable import SingletonConfigurable
33 from IPython.config.loader import (
33 from IPython.config.loader import (
34 KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, JSONFileConfigLoader
34 KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, JSONFileConfigLoader
35 )
35 )
36
36
37 from IPython.utils.traitlets import (
37 from IPython.utils.traitlets import (
38 Unicode, List, Enum, Dict, Instance, TraitError
38 Unicode, List, Enum, Dict, Instance, TraitError
39 )
39 )
40 from IPython.utils.importstring import import_item
40 from IPython.utils.importstring import import_item
41 from IPython.utils.text import indent, wrap_paragraphs, dedent
41 from IPython.utils.text import indent, wrap_paragraphs, dedent
42 from IPython.utils import py3compat
42 from IPython.utils import py3compat
43 from IPython.utils.py3compat import string_types, iteritems
43 from IPython.utils.py3compat import string_types, iteritems
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # function for re-wrapping a helpstring
46 # function for re-wrapping a helpstring
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50 # Descriptions for the various sections
50 # Descriptions for the various sections
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52
52
53 # merge flags&aliases into options
53 # merge flags&aliases into options
54 option_description = """
54 option_description = """
55 Arguments that take values are actually convenience aliases to full
55 Arguments that take values are actually convenience aliases to full
56 Configurables, whose aliases are listed on the help line. For more information
56 Configurables, whose aliases are listed on the help line. For more information
57 on full configurables, see '--help-all'.
57 on full configurables, see '--help-all'.
58 """.strip() # trim newlines of front and back
58 """.strip() # trim newlines of front and back
59
59
60 keyvalue_description = """
60 keyvalue_description = """
61 Parameters are set from command-line arguments of the form:
61 Parameters are set from command-line arguments of the form:
62 `--Class.trait=value`.
62 `--Class.trait=value`.
63 This line is evaluated in Python, so simple expressions are allowed, e.g.::
63 This line is evaluated in Python, so simple expressions are allowed, e.g.::
64 `--C.a='range(3)'` For setting C.a=[0,1,2].
64 `--C.a='range(3)'` For setting C.a=[0,1,2].
65 """.strip() # trim newlines of front and back
65 """.strip() # trim newlines of front and back
66
66
67 # sys.argv can be missing, for example when python is embedded. See the docs
67 # sys.argv can be missing, for example when python is embedded. See the docs
68 # for details: http://docs.python.org/2/c-api/intro.html#embedding-python
68 # for details: http://docs.python.org/2/c-api/intro.html#embedding-python
69 if not hasattr(sys, "argv"):
69 if not hasattr(sys, "argv"):
70 sys.argv = [""]
70 sys.argv = [""]
71
71
72 subcommand_description = """
72 subcommand_description = """
73 Subcommands are launched as `{app} cmd [args]`. For information on using
73 Subcommands are launched as `{app} cmd [args]`. For information on using
74 subcommand 'cmd', do: `{app} cmd -h`.
74 subcommand 'cmd', do: `{app} cmd -h`.
75 """.strip().format(app=os.path.basename(sys.argv[0]))
75 """.strip().format(app=os.path.basename(sys.argv[0]))
76 # get running program name
76 # get running program name
77
77
78 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
79 # Application class
79 # Application class
80 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
81
81
82 @decorator
82 @decorator
83 def catch_config_error(method, app, *args, **kwargs):
83 def catch_config_error(method, app, *args, **kwargs):
84 """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
84 """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
85
85
86 On a TraitError (generally caused by bad config), this will print the trait's
86 On a TraitError (generally caused by bad config), this will print the trait's
87 message, and exit the app.
87 message, and exit the app.
88
88
89 For use on init methods, to prevent invoking excepthook on invalid input.
89 For use on init methods, to prevent invoking excepthook on invalid input.
90 """
90 """
91 try:
91 try:
92 return method(app, *args, **kwargs)
92 return method(app, *args, **kwargs)
93 except (TraitError, ArgumentError) as e:
93 except (TraitError, ArgumentError) as e:
94 app.print_help()
94 app.print_help()
95 app.log.fatal("Bad config encountered during initialization:")
95 app.log.fatal("Bad config encountered during initialization:")
96 app.log.fatal(str(e))
96 app.log.fatal(str(e))
97 app.log.debug("Config at the time: %s", app.config)
97 app.log.debug("Config at the time: %s", app.config)
98 app.exit(1)
98 app.exit(1)
99
99
100
100
101 class ApplicationError(Exception):
101 class ApplicationError(Exception):
102 pass
102 pass
103
103
104 class LevelFormatter(logging.Formatter):
104 class LevelFormatter(logging.Formatter):
105 """Formatter with additional `highlevel` record
105 """Formatter with additional `highlevel` record
106
106
107 This field is empty if log level is less than highlevel_limit,
107 This field is empty if log level is less than highlevel_limit,
108 otherwise it is formatted with self.highlevel_format.
108 otherwise it is formatted with self.highlevel_format.
109
109
110 Useful for adding 'WARNING' to warning messages,
110 Useful for adding 'WARNING' to warning messages,
111 without adding 'INFO' to info, etc.
111 without adding 'INFO' to info, etc.
112 """
112 """
113 highlevel_limit = logging.WARN
113 highlevel_limit = logging.WARN
114 highlevel_format = " %(levelname)s |"
114 highlevel_format = " %(levelname)s |"
115
115
116 def format(self, record):
116 def format(self, record):
117 if record.levelno >= self.highlevel_limit:
117 if record.levelno >= self.highlevel_limit:
118 record.highlevel = self.highlevel_format % record.__dict__
118 record.highlevel = self.highlevel_format % record.__dict__
119 else:
119 else:
120 record.highlevel = ""
120 record.highlevel = ""
121 return super(LevelFormatter, self).format(record)
121 return super(LevelFormatter, self).format(record)
122
122
123
123
124 class Application(SingletonConfigurable):
124 class Application(SingletonConfigurable):
125 """A singleton application with full configuration support."""
125 """A singleton application with full configuration support."""
126
126
127 # The name of the application, will usually match the name of the command
127 # The name of the application, will usually match the name of the command
128 # line application
128 # line application
129 name = Unicode(u'application')
129 name = Unicode(u'application')
130
130
131 # The description of the application that is printed at the beginning
131 # The description of the application that is printed at the beginning
132 # of the help.
132 # of the help.
133 description = Unicode(u'This is an application.')
133 description = Unicode(u'This is an application.')
134 # default section descriptions
134 # default section descriptions
135 option_description = Unicode(option_description)
135 option_description = Unicode(option_description)
136 keyvalue_description = Unicode(keyvalue_description)
136 keyvalue_description = Unicode(keyvalue_description)
137 subcommand_description = Unicode(subcommand_description)
137 subcommand_description = Unicode(subcommand_description)
138
138
139 # The usage and example string that goes at the end of the help string.
139 # The usage and example string that goes at the end of the help string.
140 examples = Unicode()
140 examples = Unicode()
141
141
142 # A sequence of Configurable subclasses whose config=True attributes will
142 # A sequence of Configurable subclasses whose config=True attributes will
143 # be exposed at the command line.
143 # be exposed at the command line.
144 classes = List([])
144 classes = List([])
145
145
146 # The version string of this application.
146 # The version string of this application.
147 version = Unicode(u'0.0')
147 version = Unicode(u'0.0')
148
148
149 # the argv used to initialize the application
149 # the argv used to initialize the application
150 argv = List()
150 argv = List()
151
151
152 # The log level for the application
152 # The log level for the application
153 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
153 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
154 default_value=logging.WARN,
154 default_value=logging.WARN,
155 config=True,
155 config=True,
156 help="Set the log level by value or name.")
156 help="Set the log level by value or name.")
157 def _log_level_changed(self, name, old, new):
157 def _log_level_changed(self, name, old, new):
158 """Adjust the log level when log_level is set."""
158 """Adjust the log level when log_level is set."""
159 if isinstance(new, string_types):
159 if isinstance(new, string_types):
160 new = getattr(logging, new)
160 new = getattr(logging, new)
161 self.log_level = new
161 self.log_level = new
162 self.log.setLevel(new)
162 self.log.setLevel(new)
163
163
164 log_datefmt = Unicode("%Y-%m-%d %H:%M:%S", config=True,
164 log_datefmt = Unicode("%Y-%m-%d %H:%M:%S", config=True,
165 help="The date format used by logging formatters for %(asctime)s"
165 help="The date format used by logging formatters for %(asctime)s"
166 )
166 )
167 def _log_datefmt_changed(self, name, old, new):
167 def _log_datefmt_changed(self, name, old, new):
168 self._log_format_changed()
168 self._log_format_changed()
169
169
170 log_format = Unicode("[%(name)s]%(highlevel)s %(message)s", config=True,
170 log_format = Unicode("[%(name)s]%(highlevel)s %(message)s", config=True,
171 help="The Logging format template",
171 help="The Logging format template",
172 )
172 )
173 def _log_format_changed(self, name, old, new):
173 def _log_format_changed(self, name, old, new):
174 """Change the log formatter when log_format is set."""
174 """Change the log formatter when log_format is set."""
175 _log_handler = self.log.handlers[0]
175 _log_handler = self.log.handlers[0]
176 _log_formatter = LevelFormatter(new, datefmt=self.log_datefmt)
176 _log_formatter = LevelFormatter(new, datefmt=self.log_datefmt)
177 _log_handler.setFormatter(_log_formatter)
177 _log_handler.setFormatter(_log_formatter)
178
178
179 log = Instance(logging.Logger)
179 log = Instance(logging.Logger)
180 def _log_default(self):
180 def _log_default(self):
181 """Start logging for this application.
181 """Start logging for this application.
182
182
183 The default is to log to stderr using a StreamHandler, if no default
183 The default is to log to stderr using a StreamHandler, if no default
184 handler already exists. The log level starts at logging.WARN, but this
184 handler already exists. The log level starts at logging.WARN, but this
185 can be adjusted by setting the ``log_level`` attribute.
185 can be adjusted by setting the ``log_level`` attribute.
186 """
186 """
187 log = logging.getLogger(self.__class__.__name__)
187 log = logging.getLogger(self.__class__.__name__)
188 log.setLevel(self.log_level)
188 log.setLevel(self.log_level)
189 log.propagate = False
189 log.propagate = False
190 _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
190 _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
191 while _log:
191 while _log:
192 if _log.handlers:
192 if _log.handlers:
193 return log
193 return log
194 if not _log.propagate:
194 if not _log.propagate:
195 break
195 break
196 else:
196 else:
197 _log = _log.parent
197 _log = _log.parent
198 if sys.executable.endswith('pythonw.exe'):
198 if sys.executable.endswith('pythonw.exe'):
199 # this should really go to a file, but file-logging is only
199 # this should really go to a file, but file-logging is only
200 # hooked up in parallel applications
200 # hooked up in parallel applications
201 _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
201 _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
202 else:
202 else:
203 _log_handler = logging.StreamHandler()
203 _log_handler = logging.StreamHandler()
204 _log_formatter = LevelFormatter(self.log_format, datefmt=self.log_datefmt)
204 _log_formatter = LevelFormatter(self.log_format, datefmt=self.log_datefmt)
205 _log_handler.setFormatter(_log_formatter)
205 _log_handler.setFormatter(_log_formatter)
206 log.addHandler(_log_handler)
206 log.addHandler(_log_handler)
207 return log
207 return log
208
208
209 # the alias map for configurables
209 # the alias map for configurables
210 aliases = Dict({'log-level' : 'Application.log_level'})
210 aliases = Dict({'log-level' : 'Application.log_level'})
211
211
212 # flags for loading Configurables or store_const style flags
212 # flags for loading Configurables or store_const style flags
213 # flags are loaded from this dict by '--key' flags
213 # flags are loaded from this dict by '--key' flags
214 # this must be a dict of two-tuples, the first element being the Config/dict
214 # this must be a dict of two-tuples, the first element being the Config/dict
215 # and the second being the help string for the flag
215 # and the second being the help string for the flag
216 flags = Dict()
216 flags = Dict()
217 def _flags_changed(self, name, old, new):
217 def _flags_changed(self, name, old, new):
218 """ensure flags dict is valid"""
218 """ensure flags dict is valid"""
219 for key,value in iteritems(new):
219 for key,value in iteritems(new):
220 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
220 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
221 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
221 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
222 assert isinstance(value[1], string_types), "Bad flag: %r:%s"%(key,value)
222 assert isinstance(value[1], string_types), "Bad flag: %r:%s"%(key,value)
223
223
224
224
225 # subcommands for launching other applications
225 # subcommands for launching other applications
226 # if this is not empty, this will be a parent Application
226 # if this is not empty, this will be a parent Application
227 # this must be a dict of two-tuples,
227 # this must be a dict of two-tuples,
228 # the first element being the application class/import string
228 # the first element being the application class/import string
229 # and the second being the help string for the subcommand
229 # and the second being the help string for the subcommand
230 subcommands = Dict()
230 subcommands = Dict()
231 # parse_command_line will initialize a subapp, if requested
231 # parse_command_line will initialize a subapp, if requested
232 subapp = Instance('IPython.config.application.Application', allow_none=True)
232 subapp = Instance('IPython.config.application.Application', allow_none=True)
233
233
234 # extra command-line arguments that don't set config values
234 # extra command-line arguments that don't set config values
235 extra_args = List(Unicode)
235 extra_args = List(Unicode)
236
236
237
237
238 def __init__(self, **kwargs):
238 def __init__(self, **kwargs):
239 SingletonConfigurable.__init__(self, **kwargs)
239 SingletonConfigurable.__init__(self, **kwargs)
240 # Ensure my class is in self.classes, so my attributes appear in command line
240 # Ensure my class is in self.classes, so my attributes appear in command line
241 # options and config files.
241 # options and config files.
242 if self.__class__ not in self.classes:
242 if self.__class__ not in self.classes:
243 self.classes.insert(0, self.__class__)
243 self.classes.insert(0, self.__class__)
244
244
245 def _config_changed(self, name, old, new):
245 def _config_changed(self, name, old, new):
246 SingletonConfigurable._config_changed(self, name, old, new)
246 SingletonConfigurable._config_changed(self, name, old, new)
247 self.log.debug('Config changed:')
247 self.log.debug('Config changed:')
248 self.log.debug(repr(new))
248 self.log.debug(repr(new))
249
249
250 @catch_config_error
250 @catch_config_error
251 def initialize(self, argv=None):
251 def initialize(self, argv=None):
252 """Do the basic steps to configure me.
252 """Do the basic steps to configure me.
253
253
254 Override in subclasses.
254 Override in subclasses.
255 """
255 """
256 self.parse_command_line(argv)
256 self.parse_command_line(argv)
257
257
258
258
259 def start(self):
259 def start(self):
260 """Start the app mainloop.
260 """Start the app mainloop.
261
261
262 Override in subclasses.
262 Override in subclasses.
263 """
263 """
264 if self.subapp is not None:
264 if self.subapp is not None:
265 return self.subapp.start()
265 return self.subapp.start()
266
266
267 def print_alias_help(self):
267 def print_alias_help(self):
268 """Print the alias part of the help."""
268 """Print the alias part of the help."""
269 if not self.aliases:
269 if not self.aliases:
270 return
270 return
271
271
272 lines = []
272 lines = []
273 classdict = {}
273 classdict = {}
274 for cls in self.classes:
274 for cls in self.classes:
275 # include all parents (up to, but excluding Configurable) in available names
275 # include all parents (up to, but excluding Configurable) in available names
276 for c in cls.mro()[:-3]:
276 for c in cls.mro()[:-3]:
277 classdict[c.__name__] = c
277 classdict[c.__name__] = c
278
278
279 for alias, longname in iteritems(self.aliases):
279 for alias, longname in iteritems(self.aliases):
280 classname, traitname = longname.split('.',1)
280 classname, traitname = longname.split('.',1)
281 cls = classdict[classname]
281 cls = classdict[classname]
282
282
283 trait = cls.class_traits(config=True)[traitname]
283 trait = cls.class_traits(config=True)[traitname]
284 help = cls.class_get_trait_help(trait).splitlines()
284 help = cls.class_get_trait_help(trait).splitlines()
285 # reformat first line
285 # reformat first line
286 help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
286 help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
287 if len(alias) == 1:
287 if len(alias) == 1:
288 help[0] = help[0].replace('--%s='%alias, '-%s '%alias)
288 help[0] = help[0].replace('--%s='%alias, '-%s '%alias)
289 lines.extend(help)
289 lines.extend(help)
290 # lines.append('')
290 # lines.append('')
291 print(os.linesep.join(lines))
291 print(os.linesep.join(lines))
292
292
293 def print_flag_help(self):
293 def print_flag_help(self):
294 """Print the flag part of the help."""
294 """Print the flag part of the help."""
295 if not self.flags:
295 if not self.flags:
296 return
296 return
297
297
298 lines = []
298 lines = []
299 for m, (cfg,help) in iteritems(self.flags):
299 for m, (cfg,help) in iteritems(self.flags):
300 prefix = '--' if len(m) > 1 else '-'
300 prefix = '--' if len(m) > 1 else '-'
301 lines.append(prefix+m)
301 lines.append(prefix+m)
302 lines.append(indent(dedent(help.strip())))
302 lines.append(indent(dedent(help.strip())))
303 # lines.append('')
303 # lines.append('')
304 print(os.linesep.join(lines))
304 print(os.linesep.join(lines))
305
305
306 def print_options(self):
306 def print_options(self):
307 if not self.flags and not self.aliases:
307 if not self.flags and not self.aliases:
308 return
308 return
309 lines = ['Options']
309 lines = ['Options']
310 lines.append('-'*len(lines[0]))
310 lines.append('-'*len(lines[0]))
311 lines.append('')
311 lines.append('')
312 for p in wrap_paragraphs(self.option_description):
312 for p in wrap_paragraphs(self.option_description):
313 lines.append(p)
313 lines.append(p)
314 lines.append('')
314 lines.append('')
315 print(os.linesep.join(lines))
315 print(os.linesep.join(lines))
316 self.print_flag_help()
316 self.print_flag_help()
317 self.print_alias_help()
317 self.print_alias_help()
318 print()
318 print()
319
319
320 def print_subcommands(self):
320 def print_subcommands(self):
321 """Print the subcommand part of the help."""
321 """Print the subcommand part of the help."""
322 if not self.subcommands:
322 if not self.subcommands:
323 return
323 return
324
324
325 lines = ["Subcommands"]
325 lines = ["Subcommands"]
326 lines.append('-'*len(lines[0]))
326 lines.append('-'*len(lines[0]))
327 lines.append('')
327 lines.append('')
328 for p in wrap_paragraphs(self.subcommand_description):
328 for p in wrap_paragraphs(self.subcommand_description):
329 lines.append(p)
329 lines.append(p)
330 lines.append('')
330 lines.append('')
331 for subc, (cls, help) in iteritems(self.subcommands):
331 for subc, (cls, help) in iteritems(self.subcommands):
332 lines.append(subc)
332 lines.append(subc)
333 if help:
333 if help:
334 lines.append(indent(dedent(help.strip())))
334 lines.append(indent(dedent(help.strip())))
335 lines.append('')
335 lines.append('')
336 print(os.linesep.join(lines))
336 print(os.linesep.join(lines))
337
337
338 def print_help(self, classes=False):
338 def print_help(self, classes=False):
339 """Print the help for each Configurable class in self.classes.
339 """Print the help for each Configurable class in self.classes.
340
340
341 If classes=False (the default), only flags and aliases are printed.
341 If classes=False (the default), only flags and aliases are printed.
342 """
342 """
343 self.print_description()
343 self.print_description()
344 self.print_subcommands()
344 self.print_subcommands()
345 self.print_options()
345 self.print_options()
346
346
347 if classes:
347 if classes:
348 if self.classes:
348 if self.classes:
349 print("Class parameters")
349 print("Class parameters")
350 print("----------------")
350 print("----------------")
351 print()
351 print()
352 for p in wrap_paragraphs(self.keyvalue_description):
352 for p in wrap_paragraphs(self.keyvalue_description):
353 print(p)
353 print(p)
354 print()
354 print()
355
355
356 for cls in self.classes:
356 for cls in self.classes:
357 cls.class_print_help()
357 cls.class_print_help()
358 print()
358 print()
359 else:
359 else:
360 print("To see all available configurables, use `--help-all`")
360 print("To see all available configurables, use `--help-all`")
361 print()
361 print()
362
362
363 self.print_examples()
363 self.print_examples()
364
364
365
365
366 def print_description(self):
366 def print_description(self):
367 """Print the application description."""
367 """Print the application description."""
368 for p in wrap_paragraphs(self.description):
368 for p in wrap_paragraphs(self.description):
369 print(p)
369 print(p)
370 print()
370 print()
371
371
372 def print_examples(self):
372 def print_examples(self):
373 """Print usage and examples.
373 """Print usage and examples.
374
374
375 This usage string goes at the end of the command line help string
375 This usage string goes at the end of the command line help string
376 and should contain examples of the application's usage.
376 and should contain examples of the application's usage.
377 """
377 """
378 if self.examples:
378 if self.examples:
379 print("Examples")
379 print("Examples")
380 print("--------")
380 print("--------")
381 print()
381 print()
382 print(indent(dedent(self.examples.strip())))
382 print(indent(dedent(self.examples.strip())))
383 print()
383 print()
384
384
385 def print_version(self):
385 def print_version(self):
386 """Print the version string."""
386 """Print the version string."""
387 print(self.version)
387 print(self.version)
388
388
389 def update_config(self, config):
389 def update_config(self, config):
390 """Fire the traits events when the config is updated."""
390 """Fire the traits events when the config is updated."""
391 # Save a copy of the current config.
391 # Save a copy of the current config.
392 newconfig = deepcopy(self.config)
392 newconfig = deepcopy(self.config)
393 # Merge the new config into the current one.
393 # Merge the new config into the current one.
394 newconfig.merge(config)
394 newconfig.merge(config)
395 # Save the combined config as self.config, which triggers the traits
395 # Save the combined config as self.config, which triggers the traits
396 # events.
396 # events.
397 self.config = newconfig
397 self.config = newconfig
398
398
399 @catch_config_error
399 @catch_config_error
400 def initialize_subcommand(self, subc, argv=None):
400 def initialize_subcommand(self, subc, argv=None):
401 """Initialize a subcommand with argv."""
401 """Initialize a subcommand with argv."""
402 subapp,help = self.subcommands.get(subc)
402 subapp,help = self.subcommands.get(subc)
403
403
404 if isinstance(subapp, string_types):
404 if isinstance(subapp, string_types):
405 subapp = import_item(subapp)
405 subapp = import_item(subapp)
406
406
407 # clear existing instances
407 # clear existing instances
408 self.__class__.clear_instance()
408 self.__class__.clear_instance()
409 # instantiate
409 # instantiate
410 self.subapp = subapp.instance(config=self.config)
410 self.subapp = subapp.instance(config=self.config)
411 # and initialize subapp
411 # and initialize subapp
412 self.subapp.initialize(argv)
412 self.subapp.initialize(argv)
413
413
414 def flatten_flags(self):
414 def flatten_flags(self):
415 """flatten flags and aliases, so cl-args override as expected.
415 """flatten flags and aliases, so cl-args override as expected.
416
416
417 This prevents issues such as an alias pointing to InteractiveShell,
417 This prevents issues such as an alias pointing to InteractiveShell,
418 but a config file setting the same trait in TerminalInteraciveShell
418 but a config file setting the same trait in TerminalInteraciveShell
419 getting inappropriate priority over the command-line arg.
419 getting inappropriate priority over the command-line arg.
420
420
421 Only aliases with exactly one descendent in the class list
421 Only aliases with exactly one descendent in the class list
422 will be promoted.
422 will be promoted.
423
423
424 """
424 """
425 # build a tree of classes in our list that inherit from a particular
425 # build a tree of classes in our list that inherit from a particular
426 # it will be a dict by parent classname of classes in our list
426 # it will be a dict by parent classname of classes in our list
427 # that are descendents
427 # that are descendents
428 mro_tree = defaultdict(list)
428 mro_tree = defaultdict(list)
429 for cls in self.classes:
429 for cls in self.classes:
430 clsname = cls.__name__
430 clsname = cls.__name__
431 for parent in cls.mro()[1:-3]:
431 for parent in cls.mro()[1:-3]:
432 # exclude cls itself and Configurable,HasTraits,object
432 # exclude cls itself and Configurable,HasTraits,object
433 mro_tree[parent.__name__].append(clsname)
433 mro_tree[parent.__name__].append(clsname)
434 # flatten aliases, which have the form:
434 # flatten aliases, which have the form:
435 # { 'alias' : 'Class.trait' }
435 # { 'alias' : 'Class.trait' }
436 aliases = {}
436 aliases = {}
437 for alias, cls_trait in iteritems(self.aliases):
437 for alias, cls_trait in iteritems(self.aliases):
438 cls,trait = cls_trait.split('.',1)
438 cls,trait = cls_trait.split('.',1)
439 children = mro_tree[cls]
439 children = mro_tree[cls]
440 if len(children) == 1:
440 if len(children) == 1:
441 # exactly one descendent, promote alias
441 # exactly one descendent, promote alias
442 cls = children[0]
442 cls = children[0]
443 aliases[alias] = '.'.join([cls,trait])
443 aliases[alias] = '.'.join([cls,trait])
444
444
445 # flatten flags, which are of the form:
445 # flatten flags, which are of the form:
446 # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
446 # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
447 flags = {}
447 flags = {}
448 for key, (flagdict, help) in iteritems(self.flags):
448 for key, (flagdict, help) in iteritems(self.flags):
449 newflag = {}
449 newflag = {}
450 for cls, subdict in iteritems(flagdict):
450 for cls, subdict in iteritems(flagdict):
451 children = mro_tree[cls]
451 children = mro_tree[cls]
452 # exactly one descendent, promote flag section
452 # exactly one descendent, promote flag section
453 if len(children) == 1:
453 if len(children) == 1:
454 cls = children[0]
454 cls = children[0]
455 newflag[cls] = subdict
455 newflag[cls] = subdict
456 flags[key] = (newflag, help)
456 flags[key] = (newflag, help)
457 return flags, aliases
457 return flags, aliases
458
458
459 @catch_config_error
459 @catch_config_error
460 def parse_command_line(self, argv=None):
460 def parse_command_line(self, argv=None):
461 """Parse the command line arguments."""
461 """Parse the command line arguments."""
462 argv = sys.argv[1:] if argv is None else argv
462 argv = sys.argv[1:] if argv is None else argv
463 self.argv = [ py3compat.cast_unicode(arg) for arg in argv ]
463 self.argv = [ py3compat.cast_unicode(arg) for arg in argv ]
464
464
465 if argv and argv[0] == 'help':
465 if argv and argv[0] == 'help':
466 # turn `ipython help notebook` into `ipython notebook -h`
466 # turn `ipython help notebook` into `ipython notebook -h`
467 argv = argv[1:] + ['-h']
467 argv = argv[1:] + ['-h']
468
468
469 if self.subcommands and len(argv) > 0:
469 if self.subcommands and len(argv) > 0:
470 # we have subcommands, and one may have been specified
470 # we have subcommands, and one may have been specified
471 subc, subargv = argv[0], argv[1:]
471 subc, subargv = argv[0], argv[1:]
472 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
472 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
473 # it's a subcommand, and *not* a flag or class parameter
473 # it's a subcommand, and *not* a flag or class parameter
474 return self.initialize_subcommand(subc, subargv)
474 return self.initialize_subcommand(subc, subargv)
475
475
476 # Arguments after a '--' argument are for the script IPython may be
476 # Arguments after a '--' argument are for the script IPython may be
477 # about to run, not IPython iteslf. For arguments parsed here (help and
477 # about to run, not IPython iteslf. For arguments parsed here (help and
478 # version), we want to only search the arguments up to the first
478 # version), we want to only search the arguments up to the first
479 # occurrence of '--', which we're calling interpreted_argv.
479 # occurrence of '--', which we're calling interpreted_argv.
480 try:
480 try:
481 interpreted_argv = argv[:argv.index('--')]
481 interpreted_argv = argv[:argv.index('--')]
482 except ValueError:
482 except ValueError:
483 interpreted_argv = argv
483 interpreted_argv = argv
484
484
485 if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
485 if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
486 self.print_help('--help-all' in interpreted_argv)
486 self.print_help('--help-all' in interpreted_argv)
487 self.exit(0)
487 self.exit(0)
488
488
489 if '--version' in interpreted_argv or '-V' in interpreted_argv:
489 if '--version' in interpreted_argv or '-V' in interpreted_argv:
490 self.print_version()
490 self.print_version()
491 self.exit(0)
491 self.exit(0)
492
492
493 # flatten flags&aliases, so cl-args get appropriate priority:
493 # flatten flags&aliases, so cl-args get appropriate priority:
494 flags,aliases = self.flatten_flags()
494 flags,aliases = self.flatten_flags()
495 loader = KVArgParseConfigLoader(argv=argv, aliases=aliases,
495 loader = KVArgParseConfigLoader(argv=argv, aliases=aliases,
496 flags=flags, log=self.log)
496 flags=flags, log=self.log)
497 config = loader.load_config()
497 config = loader.load_config()
498 self.update_config(config)
498 self.update_config(config)
499 # store unparsed args in extra_args
499 # store unparsed args in extra_args
500 self.extra_args = loader.extra_args
500 self.extra_args = loader.extra_args
501
501
502 @classmethod
502 @classmethod
503 def _load_config_file(cls, basefilename, path=None, log=None):
503 def _load_config_files(cls, basefilename, path=None, log=None):
504 """Load config files (json/py) by filename and path."""
504 """Load config files (py,json) by filename and path.
505
506 yield each config object in turn.
507 """
505
508
506 pyloader = PyFileConfigLoader(basefilename+'.py', path=path, log=log)
509 pyloader = PyFileConfigLoader(basefilename+'.py', path=path, log=log)
507 jsonloader = JSONFileConfigLoader(basefilename+'.json', path=path, log=log)
510 jsonloader = JSONFileConfigLoader(basefilename+'.json', path=path, log=log)
508 config_found = False
511 config_found = False
509 config = None
512 config = None
510 for loader in [pyloader, jsonloader]:
513 for loader in [pyloader, jsonloader]:
511 try:
514 try:
512 config = loader.load_config()
515 config = loader.load_config()
513 config_found = True
516 config_found = True
514 except ConfigFileNotFound:
517 except ConfigFileNotFound:
515 pass
518 pass
516 except Exception:
519 except Exception:
517 # try to get the full filename, but it will be empty in the
520 # try to get the full filename, but it will be empty in the
518 # unlikely event that the error raised before filefind finished
521 # unlikely event that the error raised before filefind finished
519 filename = loader.full_filename or filename
522 filename = loader.full_filename or filename
520 # problem while running the file
523 # problem while running the file
521 log.error("Exception while loading config file %s",
524 log.error("Exception while loading config file %s",
522 filename, exc_info=True)
525 filename, exc_info=True)
523 else:
526 else:
524 log.debug("Loaded config file: %s", loader.full_filename)
527 log.debug("Loaded config file: %s", loader.full_filename)
525 if config :
528 if config :
526 yield config
529 yield config
527
530
528 if not config_found :
531 if not config_found :
529 raise ConfigFileNotFound('Neither .json, not .py file found.')
532 raise ConfigFileNotFound('Neither .json, not .py file found.')
530 raise StopIteration
533 raise StopIteration
531
534
532
535
533 @catch_config_error
536 @catch_config_error
534 def load_config_file(self, filename, path=None):
537 def load_config_file(self, filename, path=None):
535 """Load config files (json/py) by filename and path."""
538 """Load config files (json/py) by filename and path."""
536 filename, ext = os.path.splitext(filename)
539 filename, ext = os.path.splitext(filename)
537 for config in self._load_config_file(filename, path=path , log=self.log):
540 for config in self._load_config_files(filename, path=path , log=self.log):
538 self.update_config(config)
541 self.update_config(config)
539
542
540
543
541 def generate_config_file(self):
544 def generate_config_file(self):
542 """generate default config file from Configurables"""
545 """generate default config file from Configurables"""
543 lines = ["# Configuration file for %s."%self.name]
546 lines = ["# Configuration file for %s."%self.name]
544 lines.append('')
547 lines.append('')
545 lines.append('c = get_config()')
548 lines.append('c = get_config()')
546 lines.append('')
549 lines.append('')
547 for cls in self.classes:
550 for cls in self.classes:
548 lines.append(cls.class_config_section())
551 lines.append(cls.class_config_section())
549 return '\n'.join(lines)
552 return '\n'.join(lines)
550
553
551 def exit(self, exit_status=0):
554 def exit(self, exit_status=0):
552 self.log.debug("Exiting application: %s" % self.name)
555 self.log.debug("Exiting application: %s" % self.name)
553 sys.exit(exit_status)
556 sys.exit(exit_status)
554
557
555 @classmethod
558 @classmethod
556 def launch_instance(cls, argv=None, **kwargs):
559 def launch_instance(cls, argv=None, **kwargs):
557 """Launch a global instance of this Application
560 """Launch a global instance of this Application
558
561
559 If a global instance already exists, this reinitializes and starts it
562 If a global instance already exists, this reinitializes and starts it
560 """
563 """
561 app = cls.instance(**kwargs)
564 app = cls.instance(**kwargs)
562 app.initialize(argv)
565 app.initialize(argv)
563 app.start()
566 app.start()
564
567
565 #-----------------------------------------------------------------------------
568 #-----------------------------------------------------------------------------
566 # utility functions, for convenience
569 # utility functions, for convenience
567 #-----------------------------------------------------------------------------
570 #-----------------------------------------------------------------------------
568
571
569 def boolean_flag(name, configurable, set_help='', unset_help=''):
572 def boolean_flag(name, configurable, set_help='', unset_help=''):
570 """Helper for building basic --trait, --no-trait flags.
573 """Helper for building basic --trait, --no-trait flags.
571
574
572 Parameters
575 Parameters
573 ----------
576 ----------
574
577
575 name : str
578 name : str
576 The name of the flag.
579 The name of the flag.
577 configurable : str
580 configurable : str
578 The 'Class.trait' string of the trait to be set/unset with the flag
581 The 'Class.trait' string of the trait to be set/unset with the flag
579 set_help : unicode
582 set_help : unicode
580 help string for --name flag
583 help string for --name flag
581 unset_help : unicode
584 unset_help : unicode
582 help string for --no-name flag
585 help string for --no-name flag
583
586
584 Returns
587 Returns
585 -------
588 -------
586
589
587 cfg : dict
590 cfg : dict
588 A dict with two keys: 'name', and 'no-name', for setting and unsetting
591 A dict with two keys: 'name', and 'no-name', for setting and unsetting
589 the trait, respectively.
592 the trait, respectively.
590 """
593 """
591 # default helpstrings
594 # default helpstrings
592 set_help = set_help or "set %s=True"%configurable
595 set_help = set_help or "set %s=True"%configurable
593 unset_help = unset_help or "set %s=False"%configurable
596 unset_help = unset_help or "set %s=False"%configurable
594
597
595 cls,trait = configurable.split('.')
598 cls,trait = configurable.split('.')
596
599
597 setter = {cls : {trait : True}}
600 setter = {cls : {trait : True}}
598 unsetter = {cls : {trait : False}}
601 unsetter = {cls : {trait : False}}
599 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
602 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
600
603
@@ -1,387 +1,387 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 from __future__ import print_function
27 from __future__ import print_function
28
28
29 import logging
29 import logging
30 import os
30 import os
31 import sys
31 import sys
32
32
33 from IPython.config.loader import (
33 from IPython.config.loader import (
34 Config, PyFileConfigLoader, ConfigFileNotFound
34 Config, PyFileConfigLoader, ConfigFileNotFound
35 )
35 )
36 from IPython.config.application import boolean_flag, catch_config_error, Application
36 from IPython.config.application import boolean_flag, catch_config_error, Application
37 from IPython.core import release
37 from IPython.core import release
38 from IPython.core import usage
38 from IPython.core import usage
39 from IPython.core.completer import IPCompleter
39 from IPython.core.completer import IPCompleter
40 from IPython.core.crashhandler import CrashHandler
40 from IPython.core.crashhandler import CrashHandler
41 from IPython.core.formatters import PlainTextFormatter
41 from IPython.core.formatters import PlainTextFormatter
42 from IPython.core.history import HistoryManager
42 from IPython.core.history import HistoryManager
43 from IPython.core.prompts import PromptManager
43 from IPython.core.prompts import PromptManager
44 from IPython.core.application import (
44 from IPython.core.application import (
45 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
45 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
46 )
46 )
47 from IPython.core.magics import ScriptMagics
47 from IPython.core.magics import ScriptMagics
48 from IPython.core.shellapp import (
48 from IPython.core.shellapp import (
49 InteractiveShellApp, shell_flags, shell_aliases
49 InteractiveShellApp, shell_flags, shell_aliases
50 )
50 )
51 from IPython.extensions.storemagic import StoreMagics
51 from IPython.extensions.storemagic import StoreMagics
52 from IPython.terminal.interactiveshell import TerminalInteractiveShell
52 from IPython.terminal.interactiveshell import TerminalInteractiveShell
53 from IPython.utils import warn
53 from IPython.utils import warn
54 from IPython.utils.path import get_ipython_dir, check_for_old_config
54 from IPython.utils.path import get_ipython_dir, check_for_old_config
55 from IPython.utils.traitlets import (
55 from IPython.utils.traitlets import (
56 Bool, List, Dict,
56 Bool, List, Dict,
57 )
57 )
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Globals, utilities and helpers
60 # Globals, utilities and helpers
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62
62
63 _examples = """
63 _examples = """
64 ipython --matplotlib # enable matplotlib integration
64 ipython --matplotlib # enable matplotlib integration
65 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
65 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
66
66
67 ipython --log-level=DEBUG # set logging to DEBUG
67 ipython --log-level=DEBUG # set logging to DEBUG
68 ipython --profile=foo # start with profile foo
68 ipython --profile=foo # start with profile foo
69
69
70 ipython qtconsole # start the qtconsole GUI application
70 ipython qtconsole # start the qtconsole GUI application
71 ipython help qtconsole # show the help for the qtconsole subcmd
71 ipython help qtconsole # show the help for the qtconsole subcmd
72
72
73 ipython console # start the terminal-based console application
73 ipython console # start the terminal-based console application
74 ipython help console # show the help for the console subcmd
74 ipython help console # show the help for the console subcmd
75
75
76 ipython notebook # start the IPython notebook
76 ipython notebook # start the IPython notebook
77 ipython help notebook # show the help for the notebook subcmd
77 ipython help notebook # show the help for the notebook subcmd
78
78
79 ipython profile create foo # create profile foo w/ default config files
79 ipython profile create foo # create profile foo w/ default config files
80 ipython help profile # show the help for the profile subcmd
80 ipython help profile # show the help for the profile subcmd
81
81
82 ipython locate # print the path to the IPython directory
82 ipython locate # print the path to the IPython directory
83 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`
84
84
85 ipython nbconvert # convert notebooks to/from other formats
85 ipython nbconvert # convert notebooks to/from other formats
86 """
86 """
87
87
88 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
89 # Crash handler for this application
89 # Crash handler for this application
90 #-----------------------------------------------------------------------------
90 #-----------------------------------------------------------------------------
91
91
92 class IPAppCrashHandler(CrashHandler):
92 class IPAppCrashHandler(CrashHandler):
93 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
93 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
94
94
95 def __init__(self, app):
95 def __init__(self, app):
96 contact_name = release.author
96 contact_name = release.author
97 contact_email = release.author_email
97 contact_email = release.author_email
98 bug_tracker = 'https://github.com/ipython/ipython/issues'
98 bug_tracker = 'https://github.com/ipython/ipython/issues'
99 super(IPAppCrashHandler,self).__init__(
99 super(IPAppCrashHandler,self).__init__(
100 app, contact_name, contact_email, bug_tracker
100 app, contact_name, contact_email, bug_tracker
101 )
101 )
102
102
103 def make_report(self,traceback):
103 def make_report(self,traceback):
104 """Return a string containing a crash report."""
104 """Return a string containing a crash report."""
105
105
106 sec_sep = self.section_sep
106 sec_sep = self.section_sep
107 # Start with parent report
107 # Start with parent report
108 report = [super(IPAppCrashHandler, self).make_report(traceback)]
108 report = [super(IPAppCrashHandler, self).make_report(traceback)]
109 # Add interactive-specific info we may have
109 # Add interactive-specific info we may have
110 rpt_add = report.append
110 rpt_add = report.append
111 try:
111 try:
112 rpt_add(sec_sep+"History of session input:")
112 rpt_add(sec_sep+"History of session input:")
113 for line in self.app.shell.user_ns['_ih']:
113 for line in self.app.shell.user_ns['_ih']:
114 rpt_add(line)
114 rpt_add(line)
115 rpt_add('\n*** Last line of input (may not be in above history):\n')
115 rpt_add('\n*** Last line of input (may not be in above history):\n')
116 rpt_add(self.app.shell._last_input_line+'\n')
116 rpt_add(self.app.shell._last_input_line+'\n')
117 except:
117 except:
118 pass
118 pass
119
119
120 return ''.join(report)
120 return ''.join(report)
121
121
122 #-----------------------------------------------------------------------------
122 #-----------------------------------------------------------------------------
123 # Aliases and Flags
123 # Aliases and Flags
124 #-----------------------------------------------------------------------------
124 #-----------------------------------------------------------------------------
125 flags = dict(base_flags)
125 flags = dict(base_flags)
126 flags.update(shell_flags)
126 flags.update(shell_flags)
127 frontend_flags = {}
127 frontend_flags = {}
128 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
128 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
129 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
129 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
130 'Turn on auto editing of files with syntax errors.',
130 'Turn on auto editing of files with syntax errors.',
131 'Turn off auto editing of files with syntax errors.'
131 'Turn off auto editing of files with syntax errors.'
132 )
132 )
133 addflag('banner', 'TerminalIPythonApp.display_banner',
133 addflag('banner', 'TerminalIPythonApp.display_banner',
134 "Display a banner upon starting IPython.",
134 "Display a banner upon starting IPython.",
135 "Don't display a banner upon starting IPython."
135 "Don't display a banner upon starting IPython."
136 )
136 )
137 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
137 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
138 """Set to confirm when you try to exit IPython with an EOF (Control-D
138 """Set to confirm when you try to exit IPython with an EOF (Control-D
139 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
139 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
140 you can force a direct exit without any confirmation.""",
140 you can force a direct exit without any confirmation.""",
141 "Don't prompt the user when exiting."
141 "Don't prompt the user when exiting."
142 )
142 )
143 addflag('term-title', 'TerminalInteractiveShell.term_title',
143 addflag('term-title', 'TerminalInteractiveShell.term_title',
144 "Enable auto setting the terminal title.",
144 "Enable auto setting the terminal title.",
145 "Disable auto setting the terminal title."
145 "Disable auto setting the terminal title."
146 )
146 )
147 classic_config = Config()
147 classic_config = Config()
148 classic_config.InteractiveShell.cache_size = 0
148 classic_config.InteractiveShell.cache_size = 0
149 classic_config.PlainTextFormatter.pprint = False
149 classic_config.PlainTextFormatter.pprint = False
150 classic_config.PromptManager.in_template = '>>> '
150 classic_config.PromptManager.in_template = '>>> '
151 classic_config.PromptManager.in2_template = '... '
151 classic_config.PromptManager.in2_template = '... '
152 classic_config.PromptManager.out_template = ''
152 classic_config.PromptManager.out_template = ''
153 classic_config.InteractiveShell.separate_in = ''
153 classic_config.InteractiveShell.separate_in = ''
154 classic_config.InteractiveShell.separate_out = ''
154 classic_config.InteractiveShell.separate_out = ''
155 classic_config.InteractiveShell.separate_out2 = ''
155 classic_config.InteractiveShell.separate_out2 = ''
156 classic_config.InteractiveShell.colors = 'NoColor'
156 classic_config.InteractiveShell.colors = 'NoColor'
157 classic_config.InteractiveShell.xmode = 'Plain'
157 classic_config.InteractiveShell.xmode = 'Plain'
158
158
159 frontend_flags['classic']=(
159 frontend_flags['classic']=(
160 classic_config,
160 classic_config,
161 "Gives IPython a similar feel to the classic Python prompt."
161 "Gives IPython a similar feel to the classic Python prompt."
162 )
162 )
163 # # log doesn't make so much sense this way anymore
163 # # log doesn't make so much sense this way anymore
164 # paa('--log','-l',
164 # paa('--log','-l',
165 # action='store_true', dest='InteractiveShell.logstart',
165 # action='store_true', dest='InteractiveShell.logstart',
166 # help="Start logging to the default log file (./ipython_log.py).")
166 # help="Start logging to the default log file (./ipython_log.py).")
167 #
167 #
168 # # quick is harder to implement
168 # # quick is harder to implement
169 frontend_flags['quick']=(
169 frontend_flags['quick']=(
170 {'TerminalIPythonApp' : {'quick' : True}},
170 {'TerminalIPythonApp' : {'quick' : True}},
171 "Enable quick startup with no config files."
171 "Enable quick startup with no config files."
172 )
172 )
173
173
174 frontend_flags['i'] = (
174 frontend_flags['i'] = (
175 {'TerminalIPythonApp' : {'force_interact' : True}},
175 {'TerminalIPythonApp' : {'force_interact' : True}},
176 """If running code from the command line, become interactive afterwards.
176 """If running code from the command line, become interactive afterwards.
177 Note: can also be given simply as '-i.'"""
177 Note: can also be given simply as '-i.'"""
178 )
178 )
179 flags.update(frontend_flags)
179 flags.update(frontend_flags)
180
180
181 aliases = dict(base_aliases)
181 aliases = dict(base_aliases)
182 aliases.update(shell_aliases)
182 aliases.update(shell_aliases)
183
183
184 #-----------------------------------------------------------------------------
184 #-----------------------------------------------------------------------------
185 # Main classes and functions
185 # Main classes and functions
186 #-----------------------------------------------------------------------------
186 #-----------------------------------------------------------------------------
187
187
188
188
189 class LocateIPythonApp(BaseIPythonApplication):
189 class LocateIPythonApp(BaseIPythonApplication):
190 description = """print the path to the IPython dir"""
190 description = """print the path to the IPython dir"""
191 subcommands = Dict(dict(
191 subcommands = Dict(dict(
192 profile=('IPython.core.profileapp.ProfileLocate',
192 profile=('IPython.core.profileapp.ProfileLocate',
193 "print the path to an IPython profile directory",
193 "print the path to an IPython profile directory",
194 ),
194 ),
195 ))
195 ))
196 def start(self):
196 def start(self):
197 if self.subapp is not None:
197 if self.subapp is not None:
198 return self.subapp.start()
198 return self.subapp.start()
199 else:
199 else:
200 print(self.ipython_dir)
200 print(self.ipython_dir)
201
201
202
202
203 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
203 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
204 name = u'ipython'
204 name = u'ipython'
205 description = usage.cl_usage
205 description = usage.cl_usage
206 crash_handler_class = IPAppCrashHandler
206 crash_handler_class = IPAppCrashHandler
207 examples = _examples
207 examples = _examples
208
208
209 flags = Dict(flags)
209 flags = Dict(flags)
210 aliases = Dict(aliases)
210 aliases = Dict(aliases)
211 classes = List()
211 classes = List()
212 def _classes_default(self):
212 def _classes_default(self):
213 """This has to be in a method, for TerminalIPythonApp to be available."""
213 """This has to be in a method, for TerminalIPythonApp to be available."""
214 return [
214 return [
215 InteractiveShellApp, # ShellApp comes before TerminalApp, because
215 InteractiveShellApp, # ShellApp comes before TerminalApp, because
216 self.__class__, # it will also affect subclasses (e.g. QtConsole)
216 self.__class__, # it will also affect subclasses (e.g. QtConsole)
217 TerminalInteractiveShell,
217 TerminalInteractiveShell,
218 PromptManager,
218 PromptManager,
219 HistoryManager,
219 HistoryManager,
220 ProfileDir,
220 ProfileDir,
221 PlainTextFormatter,
221 PlainTextFormatter,
222 IPCompleter,
222 IPCompleter,
223 ScriptMagics,
223 ScriptMagics,
224 StoreMagics,
224 StoreMagics,
225 ]
225 ]
226
226
227 subcommands = Dict(dict(
227 subcommands = Dict(dict(
228 qtconsole=('IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
228 qtconsole=('IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
229 """Launch the IPython Qt Console."""
229 """Launch the IPython Qt Console."""
230 ),
230 ),
231 notebook=('IPython.html.notebookapp.NotebookApp',
231 notebook=('IPython.html.notebookapp.NotebookApp',
232 """Launch the IPython HTML Notebook Server."""
232 """Launch the IPython HTML Notebook Server."""
233 ),
233 ),
234 profile = ("IPython.core.profileapp.ProfileApp",
234 profile = ("IPython.core.profileapp.ProfileApp",
235 "Create and manage IPython profiles."
235 "Create and manage IPython profiles."
236 ),
236 ),
237 kernel = ("IPython.kernel.zmq.kernelapp.IPKernelApp",
237 kernel = ("IPython.kernel.zmq.kernelapp.IPKernelApp",
238 "Start a kernel without an attached frontend."
238 "Start a kernel without an attached frontend."
239 ),
239 ),
240 console=('IPython.terminal.console.app.ZMQTerminalIPythonApp',
240 console=('IPython.terminal.console.app.ZMQTerminalIPythonApp',
241 """Launch the IPython terminal-based Console."""
241 """Launch the IPython terminal-based Console."""
242 ),
242 ),
243 locate=('IPython.terminal.ipapp.LocateIPythonApp',
243 locate=('IPython.terminal.ipapp.LocateIPythonApp',
244 LocateIPythonApp.description
244 LocateIPythonApp.description
245 ),
245 ),
246 history=('IPython.core.historyapp.HistoryApp',
246 history=('IPython.core.historyapp.HistoryApp',
247 "Manage the IPython history database."
247 "Manage the IPython history database."
248 ),
248 ),
249 nbconvert=('IPython.nbconvert.nbconvertapp.NbConvertApp',
249 nbconvert=('IPython.nbconvert.nbconvertapp.NbConvertApp',
250 "Convert notebooks to/from other formats."
250 "Convert notebooks to/from other formats."
251 ),
251 ),
252 ))
252 ))
253
253
254 # *do* autocreate requested profile, but don't create the config file.
254 # *do* autocreate requested profile, but don't create the config file.
255 auto_create=Bool(True)
255 auto_create=Bool(True)
256 # configurables
256 # configurables
257 ignore_old_config=Bool(False, config=True,
257 ignore_old_config=Bool(False, config=True,
258 help="Suppress warning messages about legacy config files"
258 help="Suppress warning messages about legacy config files"
259 )
259 )
260 quick = Bool(False, config=True,
260 quick = Bool(False, config=True,
261 help="""Start IPython quickly by skipping the loading of config files."""
261 help="""Start IPython quickly by skipping the loading of config files."""
262 )
262 )
263 def _quick_changed(self, name, old, new):
263 def _quick_changed(self, name, old, new):
264 if new:
264 if new:
265 self.load_config_file = lambda *a, **kw: None
265 self.load_config_file = lambda *a, **kw: None
266 self.ignore_old_config=True
266 self.ignore_old_config=True
267
267
268 display_banner = Bool(True, config=True,
268 display_banner = Bool(True, config=True,
269 help="Whether to display a banner upon starting IPython."
269 help="Whether to display a banner upon starting IPython."
270 )
270 )
271
271
272 # if there is code of files to run from the cmd line, don't interact
272 # if there is code of files to run from the cmd line, don't interact
273 # unless the --i flag (App.force_interact) is true.
273 # unless the --i flag (App.force_interact) is true.
274 force_interact = Bool(False, config=True,
274 force_interact = Bool(False, config=True,
275 help="""If a command or file is given via the command-line,
275 help="""If a command or file is given via the command-line,
276 e.g. 'ipython foo.py"""
276 e.g. 'ipython foo.py"""
277 )
277 )
278 def _force_interact_changed(self, name, old, new):
278 def _force_interact_changed(self, name, old, new):
279 if new:
279 if new:
280 self.interact = True
280 self.interact = True
281
281
282 def _file_to_run_changed(self, name, old, new):
282 def _file_to_run_changed(self, name, old, new):
283 if new:
283 if new:
284 self.something_to_run = True
284 self.something_to_run = True
285 if new and not self.force_interact:
285 if new and not self.force_interact:
286 self.interact = False
286 self.interact = False
287 _code_to_run_changed = _file_to_run_changed
287 _code_to_run_changed = _file_to_run_changed
288 _module_to_run_changed = _file_to_run_changed
288 _module_to_run_changed = _file_to_run_changed
289
289
290 # internal, not-configurable
290 # internal, not-configurable
291 interact=Bool(True)
291 interact=Bool(True)
292 something_to_run=Bool(False)
292 something_to_run=Bool(False)
293
293
294 def parse_command_line(self, argv=None):
294 def parse_command_line(self, argv=None):
295 """override to allow old '-pylab' flag with deprecation warning"""
295 """override to allow old '-pylab' flag with deprecation warning"""
296
296
297 argv = sys.argv[1:] if argv is None else argv
297 argv = sys.argv[1:] if argv is None else argv
298
298
299 if '-pylab' in argv:
299 if '-pylab' in argv:
300 # deprecated `-pylab` given,
300 # deprecated `-pylab` given,
301 # warn and transform into current syntax
301 # warn and transform into current syntax
302 argv = argv[:] # copy, don't clobber
302 argv = argv[:] # copy, don't clobber
303 idx = argv.index('-pylab')
303 idx = argv.index('-pylab')
304 warn.warn("`-pylab` flag has been deprecated.\n"
304 warn.warn("`-pylab` flag has been deprecated.\n"
305 " Use `--matplotlib <backend>` and import pylab manually.")
305 " Use `--matplotlib <backend>` and import pylab manually.")
306 argv[idx] = '--pylab'
306 argv[idx] = '--pylab'
307
307
308 return super(TerminalIPythonApp, self).parse_command_line(argv)
308 return super(TerminalIPythonApp, self).parse_command_line(argv)
309
309
310 @catch_config_error
310 @catch_config_error
311 def initialize(self, argv=None):
311 def initialize(self, argv=None):
312 """Do actions after construct, but before starting the app."""
312 """Do actions after construct, but before starting the app."""
313 super(TerminalIPythonApp, self).initialize(argv)
313 super(TerminalIPythonApp, self).initialize(argv)
314 if self.subapp is not None:
314 if self.subapp is not None:
315 # don't bother initializing further, starting subapp
315 # don't bother initializing further, starting subapp
316 return
316 return
317 if not self.ignore_old_config:
317 if not self.ignore_old_config:
318 check_for_old_config(self.ipython_dir)
318 check_for_old_config(self.ipython_dir)
319 # print self.extra_args
319 # print self.extra_args
320 if self.extra_args and not self.something_to_run:
320 if self.extra_args and not self.something_to_run:
321 self.file_to_run = self.extra_args[0]
321 self.file_to_run = self.extra_args[0]
322 self.init_path()
322 self.init_path()
323 # create the shell
323 # create the shell
324 self.init_shell()
324 self.init_shell()
325 # and draw the banner
325 # and draw the banner
326 self.init_banner()
326 self.init_banner()
327 # Now a variety of things that happen after the banner is printed.
327 # Now a variety of things that happen after the banner is printed.
328 self.init_gui_pylab()
328 self.init_gui_pylab()
329 self.init_extensions()
329 self.init_extensions()
330 self.init_code()
330 self.init_code()
331
331
332 def init_shell(self):
332 def init_shell(self):
333 """initialize the InteractiveShell instance"""
333 """initialize the InteractiveShell instance"""
334 # Create an InteractiveShell instance.
334 # Create an InteractiveShell instance.
335 # shell.display_banner should always be False for the terminal
335 # shell.display_banner should always be False for the terminal
336 # based app, because we call shell.show_banner() by hand below
336 # based app, because we call shell.show_banner() by hand below
337 # so the banner shows *before* all extension loading stuff.
337 # so the banner shows *before* all extension loading stuff.
338 self.shell = TerminalInteractiveShell.instance(parent=self,
338 self.shell = TerminalInteractiveShell.instance(parent=self,
339 display_banner=False, profile_dir=self.profile_dir,
339 display_banner=False, profile_dir=self.profile_dir,
340 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
340 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
341 self.shell.configurables.append(self)
341 self.shell.configurables.append(self)
342
342
343 def init_banner(self):
343 def init_banner(self):
344 """optionally display the banner"""
344 """optionally display the banner"""
345 if self.display_banner and self.interact:
345 if self.display_banner and self.interact:
346 self.shell.show_banner()
346 self.shell.show_banner()
347 # Make sure there is a space below the banner.
347 # Make sure there is a space below the banner.
348 if self.log_level <= logging.INFO: print()
348 if self.log_level <= logging.INFO: print()
349
349
350 def _pylab_changed(self, name, old, new):
350 def _pylab_changed(self, name, old, new):
351 """Replace --pylab='inline' with --pylab='auto'"""
351 """Replace --pylab='inline' with --pylab='auto'"""
352 if new == 'inline':
352 if new == 'inline':
353 warn.warn("'inline' not available as pylab backend, "
353 warn.warn("'inline' not available as pylab backend, "
354 "using 'auto' instead.")
354 "using 'auto' instead.")
355 self.pylab = 'auto'
355 self.pylab = 'auto'
356
356
357 def start(self):
357 def start(self):
358 if self.subapp is not None:
358 if self.subapp is not None:
359 return self.subapp.start()
359 return self.subapp.start()
360 # perform any prexec steps:
360 # perform any prexec steps:
361 if self.interact:
361 if self.interact:
362 self.log.debug("Starting IPython's mainloop...")
362 self.log.debug("Starting IPython's mainloop...")
363 self.shell.mainloop()
363 self.shell.mainloop()
364 else:
364 else:
365 self.log.debug("IPython not interactive...")
365 self.log.debug("IPython not interactive...")
366
366
367 def load_default_config(ipython_dir=None):
367 def load_default_config(ipython_dir=None):
368 """Load the default config file from the default ipython_dir.
368 """Load the default config file from the default ipython_dir.
369
369
370 This is useful for embedded shells.
370 This is useful for embedded shells.
371 """
371 """
372 if ipython_dir is None:
372 if ipython_dir is None:
373 ipython_dir = get_ipython_dir()
373 ipython_dir = get_ipython_dir()
374
374
375 profile_dir = os.path.join(ipython_dir, 'profile_default')
375 profile_dir = os.path.join(ipython_dir, 'profile_default')
376
376
377 config = Config()
377 config = Config()
378 for cf in Application._load_config_file(filename[:-3], path=profile_dir, log=None):
378 for cf in Application._load_config_files("ipython_config", path=profile_dir):
379 config.update(cf)
379 config.update(cf)
380
380
381 return config
381 return config
382
382
383 launch_new_instance = TerminalIPythonApp.launch_instance
383 launch_new_instance = TerminalIPythonApp.launch_instance
384
384
385
385
386 if __name__ == '__main__':
386 if __name__ == '__main__':
387 launch_new_instance()
387 launch_new_instance()
General Comments 0
You need to be logged in to leave comments. Login now