##// END OF EJS Templates
Command line examples added for non-parallel apps.
Brian Granger -
Show More
@@ -1,404 +1,421 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
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 logging
22 import logging
23 import os
23 import os
24 import re
24 import re
25 import sys
25 import sys
26 from copy import deepcopy
26 from copy import deepcopy
27
27
28 from IPython.config.configurable import SingletonConfigurable
28 from IPython.config.configurable import SingletonConfigurable
29 from IPython.config.loader import (
29 from IPython.config.loader import (
30 KeyValueConfigLoader, PyFileConfigLoader, Config, ArgumentError
30 KeyValueConfigLoader, PyFileConfigLoader, Config, ArgumentError
31 )
31 )
32
32
33 from IPython.utils.traitlets import (
33 from IPython.utils.traitlets import (
34 Unicode, List, Int, Enum, Dict, Instance, TraitError
34 Unicode, List, Int, Enum, Dict, Instance, TraitError
35 )
35 )
36 from IPython.utils.importstring import import_item
36 from IPython.utils.importstring import import_item
37 from IPython.utils.text import indent, wrap_paragraphs, dedent
37 from IPython.utils.text import indent, wrap_paragraphs, dedent
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # function for re-wrapping a helpstring
40 # function for re-wrapping a helpstring
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Descriptions for the various sections
44 # Descriptions for the various sections
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 # merge flags&aliases into options
47 # merge flags&aliases into options
48 option_description = """
48 option_description = """
49 IPython command-line arguments are passed as '--<flag>', or '--<name>=<value>'.
49 IPython command-line arguments are passed as '--<flag>', or '--<name>=<value>'.
50
50
51 Arguments that take values are actually aliases to full Configurables, whose
51 Arguments that take values are actually aliases to full Configurables, whose
52 aliases are listed on the help line. For more information on full
52 aliases are listed on the help line. For more information on full
53 configurables, see '--help-all'.
53 configurables, see '--help-all'.
54 """.strip() # trim newlines of front and back
54 """.strip() # trim newlines of front and back
55
55
56 keyvalue_description = """
56 keyvalue_description = """
57 Parameters are set from command-line arguments of the form:
57 Parameters are set from command-line arguments of the form:
58 `--Class.trait=value`.
58 `--Class.trait=value`.
59 This line is evaluated in Python, so simple expressions are allowed, e.g.::
59 This line is evaluated in Python, so simple expressions are allowed, e.g.::
60 `--C.a='range(3)'` For setting C.a=[0,1,2].
60 `--C.a='range(3)'` For setting C.a=[0,1,2].
61 """.strip() # trim newlines of front and back
61 """.strip() # trim newlines of front and back
62
62
63 subcommand_description = """
63 subcommand_description = """
64 Subcommands are launched as `{app} cmd [args]`. For information on using
64 Subcommands are launched as `{app} cmd [args]`. For information on using
65 subcommand 'cmd', do: `{app} cmd -h`.
65 subcommand 'cmd', do: `{app} cmd -h`.
66 """.strip().format(app=os.path.basename(sys.argv[0]))
66 """.strip().format(app=os.path.basename(sys.argv[0]))
67 # get running program name
67 # get running program name
68
68
69 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
70 # Application class
70 # Application class
71 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
72
72
73
73
74 class ApplicationError(Exception):
74 class ApplicationError(Exception):
75 pass
75 pass
76
76
77
77
78 class Application(SingletonConfigurable):
78 class Application(SingletonConfigurable):
79 """A singleton application with full configuration support."""
79 """A singleton application with full configuration support."""
80
80
81 # The name of the application, will usually match the name of the command
81 # The name of the application, will usually match the name of the command
82 # line application
82 # line application
83 name = Unicode(u'application')
83 name = Unicode(u'application')
84
84
85 # The description of the application that is printed at the beginning
85 # The description of the application that is printed at the beginning
86 # of the help.
86 # of the help.
87 description = Unicode(u'This is an application.')
87 description = Unicode(u'This is an application.')
88 # default section descriptions
88 # default section descriptions
89 option_description = Unicode(option_description)
89 option_description = Unicode(option_description)
90 keyvalue_description = Unicode(keyvalue_description)
90 keyvalue_description = Unicode(keyvalue_description)
91 subcommand_description = Unicode(subcommand_description)
91 subcommand_description = Unicode(subcommand_description)
92
92
93 # The usage and example string that goes at the end of the help string.
94 examples = Unicode()
93
95
94 # A sequence of Configurable subclasses whose config=True attributes will
96 # A sequence of Configurable subclasses whose config=True attributes will
95 # be exposed at the command line.
97 # be exposed at the command line.
96 classes = List([])
98 classes = List([])
97
99
98 # The version string of this application.
100 # The version string of this application.
99 version = Unicode(u'0.0')
101 version = Unicode(u'0.0')
100
102
101 # The log level for the application
103 # The log level for the application
102 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
104 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
103 default_value=logging.WARN,
105 default_value=logging.WARN,
104 config=True,
106 config=True,
105 help="Set the log level by value or name.")
107 help="Set the log level by value or name.")
106 def _log_level_changed(self, name, old, new):
108 def _log_level_changed(self, name, old, new):
107 """Adjust the log level when log_level is set."""
109 """Adjust the log level when log_level is set."""
108 if isinstance(new, basestring):
110 if isinstance(new, basestring):
109 new = getattr(logging, new)
111 new = getattr(logging, new)
110 self.log_level = new
112 self.log_level = new
111 self.log.setLevel(new)
113 self.log.setLevel(new)
112
114
113 # the alias map for configurables
115 # the alias map for configurables
114 aliases = Dict({'log-level' : 'Application.log_level'})
116 aliases = Dict({'log-level' : 'Application.log_level'})
115
117
116 # flags for loading Configurables or store_const style flags
118 # flags for loading Configurables or store_const style flags
117 # flags are loaded from this dict by '--key' flags
119 # flags are loaded from this dict by '--key' flags
118 # this must be a dict of two-tuples, the first element being the Config/dict
120 # this must be a dict of two-tuples, the first element being the Config/dict
119 # and the second being the help string for the flag
121 # and the second being the help string for the flag
120 flags = Dict()
122 flags = Dict()
121 def _flags_changed(self, name, old, new):
123 def _flags_changed(self, name, old, new):
122 """ensure flags dict is valid"""
124 """ensure flags dict is valid"""
123 for key,value in new.iteritems():
125 for key,value in new.iteritems():
124 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
126 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
125 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
127 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
126 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
128 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
127
129
128
130
129 # subcommands for launching other applications
131 # subcommands for launching other applications
130 # if this is not empty, this will be a parent Application
132 # if this is not empty, this will be a parent Application
131 # this must be a dict of two-tuples,
133 # this must be a dict of two-tuples,
132 # the first element being the application class/import string
134 # the first element being the application class/import string
133 # and the second being the help string for the subcommand
135 # and the second being the help string for the subcommand
134 subcommands = Dict()
136 subcommands = Dict()
135 # parse_command_line will initialize a subapp, if requested
137 # parse_command_line will initialize a subapp, if requested
136 subapp = Instance('IPython.config.application.Application', allow_none=True)
138 subapp = Instance('IPython.config.application.Application', allow_none=True)
137
139
138 # extra command-line arguments that don't set config values
140 # extra command-line arguments that don't set config values
139 extra_args = List(Unicode)
141 extra_args = List(Unicode)
140
142
141
143
142 def __init__(self, **kwargs):
144 def __init__(self, **kwargs):
143 SingletonConfigurable.__init__(self, **kwargs)
145 SingletonConfigurable.__init__(self, **kwargs)
144 # Add my class to self.classes so my attributes appear in command line
146 # Add my class to self.classes so my attributes appear in command line
145 # options.
147 # options.
146 self.classes.insert(0, self.__class__)
148 self.classes.insert(0, self.__class__)
147
149
148 self.init_logging()
150 self.init_logging()
149
151
150 def _config_changed(self, name, old, new):
152 def _config_changed(self, name, old, new):
151 SingletonConfigurable._config_changed(self, name, old, new)
153 SingletonConfigurable._config_changed(self, name, old, new)
152 self.log.debug('Config changed:')
154 self.log.debug('Config changed:')
153 self.log.debug(repr(new))
155 self.log.debug(repr(new))
154
156
155 def init_logging(self):
157 def init_logging(self):
156 """Start logging for this application.
158 """Start logging for this application.
157
159
158 The default is to log to stdout using a StreaHandler. The log level
160 The default is to log to stdout using a StreaHandler. The log level
159 starts at loggin.WARN, but this can be adjusted by setting the
161 starts at loggin.WARN, but this can be adjusted by setting the
160 ``log_level`` attribute.
162 ``log_level`` attribute.
161 """
163 """
162 self.log = logging.getLogger(self.__class__.__name__)
164 self.log = logging.getLogger(self.__class__.__name__)
163 self.log.setLevel(self.log_level)
165 self.log.setLevel(self.log_level)
164 if sys.executable.endswith('pythonw.exe'):
166 if sys.executable.endswith('pythonw.exe'):
165 # this should really go to a file, but file-logging is only
167 # this should really go to a file, but file-logging is only
166 # hooked up in parallel applications
168 # hooked up in parallel applications
167 self._log_handler = logging.StreamHandler(open(os.devnull, 'w'))
169 self._log_handler = logging.StreamHandler(open(os.devnull, 'w'))
168 else:
170 else:
169 self._log_handler = logging.StreamHandler()
171 self._log_handler = logging.StreamHandler()
170 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
172 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
171 self._log_handler.setFormatter(self._log_formatter)
173 self._log_handler.setFormatter(self._log_formatter)
172 self.log.addHandler(self._log_handler)
174 self.log.addHandler(self._log_handler)
173
175
174 def initialize(self, argv=None):
176 def initialize(self, argv=None):
175 """Do the basic steps to configure me.
177 """Do the basic steps to configure me.
176
178
177 Override in subclasses.
179 Override in subclasses.
178 """
180 """
179 self.parse_command_line(argv)
181 self.parse_command_line(argv)
180
182
181
183
182 def start(self):
184 def start(self):
183 """Start the app mainloop.
185 """Start the app mainloop.
184
186
185 Override in subclasses.
187 Override in subclasses.
186 """
188 """
187 if self.subapp is not None:
189 if self.subapp is not None:
188 return self.subapp.start()
190 return self.subapp.start()
189
191
190 def print_alias_help(self):
192 def print_alias_help(self):
191 """Print the alias part of the help."""
193 """Print the alias part of the help."""
192 if not self.aliases:
194 if not self.aliases:
193 return
195 return
194
196
195 lines = []
197 lines = []
196 classdict = {}
198 classdict = {}
197 for cls in self.classes:
199 for cls in self.classes:
198 # include all parents (up to, but excluding Configurable) in available names
200 # include all parents (up to, but excluding Configurable) in available names
199 for c in cls.mro()[:-3]:
201 for c in cls.mro()[:-3]:
200 classdict[c.__name__] = c
202 classdict[c.__name__] = c
201
203
202 for alias, longname in self.aliases.iteritems():
204 for alias, longname in self.aliases.iteritems():
203 classname, traitname = longname.split('.',1)
205 classname, traitname = longname.split('.',1)
204 cls = classdict[classname]
206 cls = classdict[classname]
205
207
206 trait = cls.class_traits(config=True)[traitname]
208 trait = cls.class_traits(config=True)[traitname]
207 help = cls.class_get_trait_help(trait).splitlines()
209 help = cls.class_get_trait_help(trait).splitlines()
208 # reformat first line
210 # reformat first line
209 help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
211 help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
210 lines.extend(help)
212 lines.extend(help)
211 # lines.append('')
213 # lines.append('')
212 print os.linesep.join(lines)
214 print os.linesep.join(lines)
213
215
214 def print_flag_help(self):
216 def print_flag_help(self):
215 """Print the flag part of the help."""
217 """Print the flag part of the help."""
216 if not self.flags:
218 if not self.flags:
217 return
219 return
218
220
219 lines = []
221 lines = []
220 for m, (cfg,help) in self.flags.iteritems():
222 for m, (cfg,help) in self.flags.iteritems():
221 lines.append('--'+m)
223 lines.append('--'+m)
222 lines.append(indent(dedent(help.strip())))
224 lines.append(indent(dedent(help.strip())))
223 # lines.append('')
225 # lines.append('')
224 print os.linesep.join(lines)
226 print os.linesep.join(lines)
225
227
226 def print_options(self):
228 def print_options(self):
227 if not self.flags and not self.aliases:
229 if not self.flags and not self.aliases:
228 return
230 return
229 lines = ['Options']
231 lines = ['Options']
230 lines.append('-'*len(lines[0]))
232 lines.append('-'*len(lines[0]))
231 lines.append('')
233 lines.append('')
232 for p in wrap_paragraphs(self.option_description):
234 for p in wrap_paragraphs(self.option_description):
233 lines.append(p)
235 lines.append(p)
234 lines.append('')
236 lines.append('')
235 print os.linesep.join(lines)
237 print os.linesep.join(lines)
236 self.print_flag_help()
238 self.print_flag_help()
237 self.print_alias_help()
239 self.print_alias_help()
238 print
240 print
239
241
240 def print_subcommands(self):
242 def print_subcommands(self):
241 """Print the subcommand part of the help."""
243 """Print the subcommand part of the help."""
242 if not self.subcommands:
244 if not self.subcommands:
243 return
245 return
244
246
245 lines = ["Subcommands"]
247 lines = ["Subcommands"]
246 lines.append('-'*len(lines[0]))
248 lines.append('-'*len(lines[0]))
247 lines.append('')
249 lines.append('')
248 for p in wrap_paragraphs(self.subcommand_description):
250 for p in wrap_paragraphs(self.subcommand_description):
249 lines.append(p)
251 lines.append(p)
250 lines.append('')
252 lines.append('')
251 for subc, (cls,help) in self.subcommands.iteritems():
253 for subc, (cls,help) in self.subcommands.iteritems():
252 lines.append("%s : %s"%(subc, cls))
254 lines.append("%s : %s"%(subc, cls))
253 if help:
255 if help:
254 lines.append(indent(dedent(help.strip())))
256 lines.append(indent(dedent(help.strip())))
255 lines.append('')
257 lines.append('')
256 print os.linesep.join(lines)
258 print os.linesep.join(lines)
257
259
258 def print_help(self, classes=False):
260 def print_help(self, classes=False):
259 """Print the help for each Configurable class in self.classes.
261 """Print the help for each Configurable class in self.classes.
260
262
261 If classes=False (the default), only flags and aliases are printed.
263 If classes=False (the default), only flags and aliases are printed.
262 """
264 """
263 self.print_subcommands()
265 self.print_subcommands()
264 self.print_options()
266 self.print_options()
265
267
266 if classes:
268 if classes:
267 if self.classes:
269 if self.classes:
268 print "Class parameters"
270 print "Class parameters"
269 print "----------------"
271 print "----------------"
270 print
272 print
271 for p in wrap_paragraphs(self.keyvalue_description):
273 for p in wrap_paragraphs(self.keyvalue_description):
272 print p
274 print p
273 print
275 print
274
276
275 for cls in self.classes:
277 for cls in self.classes:
276 cls.class_print_help()
278 cls.class_print_help()
277 print
279 print
278 else:
280 else:
279 print "To see all available configurables, use `--help-all`"
281 print "To see all available configurables, use `--help-all`"
280 print
282 print
281
283
282 def print_description(self):
284 def print_description(self):
283 """Print the application description."""
285 """Print the application description."""
284 for p in wrap_paragraphs(self.description):
286 for p in wrap_paragraphs(self.description):
285 print p
287 print p
286 print
288 print
287
289
290 def print_examples(self):
291 """Print usage and examples.
292
293 This usage string goes at the end of the command line help string
294 and should contain examples of the application's usage.
295 """
296 if self.examples:
297 print "Examples"
298 print "--------"
299 print
300 print indent(dedent(self.examples.strip()))
301 print
302
288 def print_version(self):
303 def print_version(self):
289 """Print the version string."""
304 """Print the version string."""
290 print self.version
305 print self.version
291
306
292 def update_config(self, config):
307 def update_config(self, config):
293 """Fire the traits events when the config is updated."""
308 """Fire the traits events when the config is updated."""
294 # Save a copy of the current config.
309 # Save a copy of the current config.
295 newconfig = deepcopy(self.config)
310 newconfig = deepcopy(self.config)
296 # Merge the new config into the current one.
311 # Merge the new config into the current one.
297 newconfig._merge(config)
312 newconfig._merge(config)
298 # Save the combined config as self.config, which triggers the traits
313 # Save the combined config as self.config, which triggers the traits
299 # events.
314 # events.
300 self.config = newconfig
315 self.config = newconfig
301
316
302 def initialize_subcommand(self, subc, argv=None):
317 def initialize_subcommand(self, subc, argv=None):
303 """Initialize a subcommand with argv."""
318 """Initialize a subcommand with argv."""
304 subapp,help = self.subcommands.get(subc)
319 subapp,help = self.subcommands.get(subc)
305
320
306 if isinstance(subapp, basestring):
321 if isinstance(subapp, basestring):
307 subapp = import_item(subapp)
322 subapp = import_item(subapp)
308
323
309 # clear existing instances
324 # clear existing instances
310 self.__class__.clear_instance()
325 self.__class__.clear_instance()
311 # instantiate
326 # instantiate
312 self.subapp = subapp.instance()
327 self.subapp = subapp.instance()
313 # and initialize subapp
328 # and initialize subapp
314 self.subapp.initialize(argv)
329 self.subapp.initialize(argv)
315
330
316 def parse_command_line(self, argv=None):
331 def parse_command_line(self, argv=None):
317 """Parse the command line arguments."""
332 """Parse the command line arguments."""
318 argv = sys.argv[1:] if argv is None else argv
333 argv = sys.argv[1:] if argv is None else argv
319
334
320 if self.subcommands and len(argv) > 0:
335 if self.subcommands and len(argv) > 0:
321 # we have subcommands, and one may have been specified
336 # we have subcommands, and one may have been specified
322 subc, subargv = argv[0], argv[1:]
337 subc, subargv = argv[0], argv[1:]
323 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
338 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
324 # it's a subcommand, and *not* a flag or class parameter
339 # it's a subcommand, and *not* a flag or class parameter
325 return self.initialize_subcommand(subc, subargv)
340 return self.initialize_subcommand(subc, subargv)
326
341
327 if '-h' in argv or '--help' in argv or '--help-all' in argv:
342 if '-h' in argv or '--help' in argv or '--help-all' in argv:
328 self.print_description()
343 self.print_description()
329 self.print_help('--help-all' in argv)
344 self.print_help('--help-all' in argv)
345 self.print_examples()
330 self.exit(0)
346 self.exit(0)
331
347
332 if '--version' in argv:
348 if '--version' in argv:
333 self.print_version()
349 self.print_version()
334 self.exit(0)
350 self.exit(0)
335
351
336 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
352 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
337 flags=self.flags)
353 flags=self.flags)
338 try:
354 try:
339 config = loader.load_config()
355 config = loader.load_config()
340 self.update_config(config)
356 self.update_config(config)
341 except (TraitError, ArgumentError) as e:
357 except (TraitError, ArgumentError) as e:
342 self.print_description()
358 self.print_description()
343 self.print_help()
359 self.print_help()
360 self.print_examples()
344 self.log.fatal(str(e))
361 self.log.fatal(str(e))
345 self.exit(1)
362 self.exit(1)
346 # store unparsed args in extra_args
363 # store unparsed args in extra_args
347 self.extra_args = loader.extra_args
364 self.extra_args = loader.extra_args
348
365
349 def load_config_file(self, filename, path=None):
366 def load_config_file(self, filename, path=None):
350 """Load a .py based config file by filename and path."""
367 """Load a .py based config file by filename and path."""
351 loader = PyFileConfigLoader(filename, path=path)
368 loader = PyFileConfigLoader(filename, path=path)
352 config = loader.load_config()
369 config = loader.load_config()
353 self.update_config(config)
370 self.update_config(config)
354
371
355 def generate_config_file(self):
372 def generate_config_file(self):
356 """generate default config file from Configurables"""
373 """generate default config file from Configurables"""
357 lines = ["# Configuration file for %s."%self.name]
374 lines = ["# Configuration file for %s."%self.name]
358 lines.append('')
375 lines.append('')
359 lines.append('c = get_config()')
376 lines.append('c = get_config()')
360 lines.append('')
377 lines.append('')
361 for cls in self.classes:
378 for cls in self.classes:
362 lines.append(cls.class_config_section())
379 lines.append(cls.class_config_section())
363 return '\n'.join(lines)
380 return '\n'.join(lines)
364
381
365 def exit(self, exit_status=0):
382 def exit(self, exit_status=0):
366 self.log.debug("Exiting application: %s" % self.name)
383 self.log.debug("Exiting application: %s" % self.name)
367 sys.exit(exit_status)
384 sys.exit(exit_status)
368
385
369 #-----------------------------------------------------------------------------
386 #-----------------------------------------------------------------------------
370 # utility functions, for convenience
387 # utility functions, for convenience
371 #-----------------------------------------------------------------------------
388 #-----------------------------------------------------------------------------
372
389
373 def boolean_flag(name, configurable, set_help='', unset_help=''):
390 def boolean_flag(name, configurable, set_help='', unset_help=''):
374 """Helper for building basic --trait, --no-trait flags.
391 """Helper for building basic --trait, --no-trait flags.
375
392
376 Parameters
393 Parameters
377 ----------
394 ----------
378
395
379 name : str
396 name : str
380 The name of the flag.
397 The name of the flag.
381 configurable : str
398 configurable : str
382 The 'Class.trait' string of the trait to be set/unset with the flag
399 The 'Class.trait' string of the trait to be set/unset with the flag
383 set_help : unicode
400 set_help : unicode
384 help string for --name flag
401 help string for --name flag
385 unset_help : unicode
402 unset_help : unicode
386 help string for --no-name flag
403 help string for --no-name flag
387
404
388 Returns
405 Returns
389 -------
406 -------
390
407
391 cfg : dict
408 cfg : dict
392 A dict with two keys: 'name', and 'no-name', for setting and unsetting
409 A dict with two keys: 'name', and 'no-name', for setting and unsetting
393 the trait, respectively.
410 the trait, respectively.
394 """
411 """
395 # default helpstrings
412 # default helpstrings
396 set_help = set_help or "set %s=True"%configurable
413 set_help = set_help or "set %s=True"%configurable
397 unset_help = unset_help or "set %s=False"%configurable
414 unset_help = unset_help or "set %s=False"%configurable
398
415
399 cls,trait = configurable.split('.')
416 cls,trait = configurable.split('.')
400
417
401 setter = {cls : {trait : True}}
418 setter = {cls : {trait : True}}
402 unsetter = {cls : {trait : False}}
419 unsetter = {cls : {trait : False}}
403 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
420 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
404
421
@@ -1,220 +1,231 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 An application for managing IPython profiles.
3 An application for managing IPython profiles.
4
4
5 To be invoked as the `ipython profile` subcommand.
5 To be invoked as the `ipython profile` subcommand.
6
6
7 Authors:
7 Authors:
8
8
9 * Min RK
9 * Min RK
10
10
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008-2011 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 import logging
24 import logging
25 import os
25 import os
26
26
27 from IPython.config.application import Application, boolean_flag
27 from IPython.config.application import Application, boolean_flag
28 from IPython.core.application import (
28 from IPython.core.application import (
29 BaseIPythonApplication, base_flags, base_aliases
29 BaseIPythonApplication, base_flags, base_aliases
30 )
30 )
31 from IPython.core.profiledir import ProfileDir
31 from IPython.core.profiledir import ProfileDir
32 from IPython.utils.path import get_ipython_dir
32 from IPython.utils.path import get_ipython_dir
33 from IPython.utils.traitlets import Unicode, Bool, Dict
33 from IPython.utils.traitlets import Unicode, Bool, Dict
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Constants
36 # Constants
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39 create_help = """Create an IPython profile by name
39 create_help = """Create an IPython profile by name
40
40
41 Create an ipython profile directory by its name or
41 Create an ipython profile directory by its name or
42 profile directory path. Profile directories contain
42 profile directory path. Profile directories contain
43 configuration, log and security related files and are named
43 configuration, log and security related files and are named
44 using the convention 'profile_<name>'. By default they are
44 using the convention 'profile_<name>'. By default they are
45 located in your ipython directory. Once created, you will
45 located in your ipython directory. Once created, you will
46 can edit the configuration files in the profile
46 can edit the configuration files in the profile
47 directory to configure IPython. Most users will create a
47 directory to configure IPython. Most users will create a
48 profile directory by name,
48 profile directory by name,
49 `ipython profile create myprofile`, which will put the directory
49 `ipython profile create myprofile`, which will put the directory
50 in `<ipython_dir>/profile_myprofile`.
50 in `<ipython_dir>/profile_myprofile`.
51 """
51 """
52 list_help = """List available IPython profiles
52 list_help = """List available IPython profiles
53
53
54 List all available profiles, by profile location, that can
54 List all available profiles, by profile location, that can
55 be found in the current working directly or in the ipython
55 be found in the current working directly or in the ipython
56 directory. Profile directories are named using the convention
56 directory. Profile directories are named using the convention
57 'profile_<profile>'.
57 'profile_<profile>'.
58 """
58 """
59 profile_help = """Manage IPython profiles
59 profile_help = """Manage IPython profiles
60
60
61 Profile directories contain
61 Profile directories contain
62 configuration, log and security related files and are named
62 configuration, log and security related files and are named
63 using the convention 'profile_<name>'. By default they are
63 using the convention 'profile_<name>'. By default they are
64 located in your ipython directory. You can create profiles
64 located in your ipython directory. You can create profiles
65 with `ipython profile create <name>`, or see the profiles you
65 with `ipython profile create <name>`, or see the profiles you
66 already have with `ipython profile list`
66 already have with `ipython profile list`
67
67
68 To get started configuring IPython, simply do:
68 To get started configuring IPython, simply do:
69
69
70 $> ipython profile create
70 $> ipython profile create
71
71
72 and IPython will create the default profile in <ipython_dir>/profile_default,
72 and IPython will create the default profile in <ipython_dir>/profile_default,
73 where you can edit ipython_config.py to start configuring IPython.
73 where you can edit ipython_config.py to start configuring IPython.
74
74
75 """
75 """
76
76
77 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
78 # Profile Application Class (for `ipython profile` subcommand)
78 # Profile Application Class (for `ipython profile` subcommand)
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80
80
81
81
82
83 class ProfileList(Application):
82 class ProfileList(Application):
84 name = u'ipython-profile'
83 name = u'ipython-profile'
85 description = list_help
84 description = list_help
86
85
87 aliases = Dict({
86 aliases = Dict({
88 'ipython-dir' : 'ProfileList.ipython_dir',
87 'ipython-dir' : 'ProfileList.ipython_dir',
89 'log-level' : 'Application.log_level',
88 'log-level' : 'Application.log_level',
90 })
89 })
91 flags = Dict(dict(
90 flags = Dict(dict(
92 debug = ({'Application' : {'log_level' : 0}},
91 debug = ({'Application' : {'log_level' : 0}},
93 "Set Application.log_level to 0, maximizing log output."
92 "Set Application.log_level to 0, maximizing log output."
94 )
93 )
95 ))
94 ))
96 ipython_dir = Unicode(get_ipython_dir(), config=True,
95 ipython_dir = Unicode(get_ipython_dir(), config=True,
97 help="""
96 help="""
98 The name of the IPython directory. This directory is used for logging
97 The name of the IPython directory. This directory is used for logging
99 configuration (through profiles), history storage, etc. The default
98 configuration (through profiles), history storage, etc. The default
100 is usually $HOME/.ipython. This options can also be specified through
99 is usually $HOME/.ipython. This options can also be specified through
101 the environment variable IPYTHON_DIR.
100 the environment variable IPYTHON_DIR.
102 """
101 """
103 )
102 )
104
103
105 def list_profile_dirs(self):
104 def list_profile_dirs(self):
106 # Find the search paths
105 # Find the search paths
107 paths = [os.getcwdu(), self.ipython_dir]
106 paths = [os.getcwdu(), self.ipython_dir]
108
107
109 self.log.warn('Searching for IPython profiles in paths: %r' % paths)
108 self.log.warn('Searching for IPython profiles in paths: %r' % paths)
110 for path in paths:
109 for path in paths:
111 files = os.listdir(path)
110 files = os.listdir(path)
112 for f in files:
111 for f in files:
113 full_path = os.path.join(path, f)
112 full_path = os.path.join(path, f)
114 if os.path.isdir(full_path) and f.startswith('profile_'):
113 if os.path.isdir(full_path) and f.startswith('profile_'):
115 profile = f.split('_',1)[-1]
114 profile = f.split('_',1)[-1]
116 start_cmd = 'ipython profile=%s' % profile
115 start_cmd = 'ipython profile=%s' % profile
117 print start_cmd + " ==> " + full_path
116 print start_cmd + " ==> " + full_path
118
117
119 def start(self):
118 def start(self):
120 self.list_profile_dirs()
119 self.list_profile_dirs()
121
120
122
121
123 create_flags = {}
122 create_flags = {}
124 create_flags.update(base_flags)
123 create_flags.update(base_flags)
125 create_flags.update(boolean_flag('reset', 'ProfileCreate.overwrite',
124 create_flags.update(boolean_flag('reset', 'ProfileCreate.overwrite',
126 "reset config files to defaults", "leave existing config files"))
125 "reset config files to defaults", "leave existing config files"))
127 create_flags.update(boolean_flag('parallel', 'ProfileCreate.parallel',
126 create_flags.update(boolean_flag('parallel', 'ProfileCreate.parallel',
128 "Include parallel computing config files",
127 "Include parallel computing config files",
129 "Don't include parallel computing config files"))
128 "Don't include parallel computing config files"))
130
129
130 create_examples = """
131 ipython profile create foo # create profile foo
132 ipython profile create foo --init # create with default config files
133 """
134
131 class ProfileCreate(BaseIPythonApplication):
135 class ProfileCreate(BaseIPythonApplication):
132 name = u'ipython-profile'
136 name = u'ipython-profile'
133 description = create_help
137 description = create_help
138 examples = create_examples
134 auto_create = Bool(True, config=False)
139 auto_create = Bool(True, config=False)
135
140
136 def _copy_config_files_default(self):
141 def _copy_config_files_default(self):
137 return True
142 return True
138
143
139 parallel = Bool(False, config=True,
144 parallel = Bool(False, config=True,
140 help="whether to include parallel computing config files")
145 help="whether to include parallel computing config files")
141 def _parallel_changed(self, name, old, new):
146 def _parallel_changed(self, name, old, new):
142 parallel_files = [ 'ipcontroller_config.py',
147 parallel_files = [ 'ipcontroller_config.py',
143 'ipengine_config.py',
148 'ipengine_config.py',
144 'ipcluster_config.py'
149 'ipcluster_config.py'
145 ]
150 ]
146 if new:
151 if new:
147 for cf in parallel_files:
152 for cf in parallel_files:
148 self.config_files.append(cf)
153 self.config_files.append(cf)
149 else:
154 else:
150 for cf in parallel_files:
155 for cf in parallel_files:
151 if cf in self.config_files:
156 if cf in self.config_files:
152 self.config_files.remove(cf)
157 self.config_files.remove(cf)
153
158
154 def parse_command_line(self, argv):
159 def parse_command_line(self, argv):
155 super(ProfileCreate, self).parse_command_line(argv)
160 super(ProfileCreate, self).parse_command_line(argv)
156 # accept positional arg as profile name
161 # accept positional arg as profile name
157 if self.extra_args:
162 if self.extra_args:
158 self.profile = self.extra_args[0]
163 self.profile = self.extra_args[0]
159
164
160 flags = Dict(create_flags)
165 flags = Dict(create_flags)
161
166
162 classes = [ProfileDir]
167 classes = [ProfileDir]
163
168
164 def init_config_files(self):
169 def init_config_files(self):
165 super(ProfileCreate, self).init_config_files()
170 super(ProfileCreate, self).init_config_files()
166 # use local imports, since these classes may import from here
171 # use local imports, since these classes may import from here
167 from IPython.frontend.terminal.ipapp import TerminalIPythonApp
172 from IPython.frontend.terminal.ipapp import TerminalIPythonApp
168 apps = [TerminalIPythonApp]
173 apps = [TerminalIPythonApp]
169 try:
174 try:
170 from IPython.frontend.qt.console.qtconsoleapp import IPythonQtConsoleApp
175 from IPython.frontend.qt.console.qtconsoleapp import IPythonQtConsoleApp
171 except Exception:
176 except Exception:
172 # this should be ImportError, but under weird circumstances
177 # this should be ImportError, but under weird circumstances
173 # this might be an AttributeError, or possibly others
178 # this might be an AttributeError, or possibly others
174 # in any case, nothing should cause the profile creation to crash.
179 # in any case, nothing should cause the profile creation to crash.
175 pass
180 pass
176 else:
181 else:
177 apps.append(IPythonQtConsoleApp)
182 apps.append(IPythonQtConsoleApp)
178 if self.parallel:
183 if self.parallel:
179 from IPython.parallel.apps.ipcontrollerapp import IPControllerApp
184 from IPython.parallel.apps.ipcontrollerapp import IPControllerApp
180 from IPython.parallel.apps.ipengineapp import IPEngineApp
185 from IPython.parallel.apps.ipengineapp import IPEngineApp
181 from IPython.parallel.apps.ipclusterapp import IPClusterStart
186 from IPython.parallel.apps.ipclusterapp import IPClusterStart
182 from IPython.parallel.apps.iploggerapp import IPLoggerApp
187 from IPython.parallel.apps.iploggerapp import IPLoggerApp
183 apps.extend([
188 apps.extend([
184 IPControllerApp,
189 IPControllerApp,
185 IPEngineApp,
190 IPEngineApp,
186 IPClusterStart,
191 IPClusterStart,
187 IPLoggerApp,
192 IPLoggerApp,
188 ])
193 ])
189 for App in apps:
194 for App in apps:
190 app = App()
195 app = App()
191 app.config.update(self.config)
196 app.config.update(self.config)
192 app.log = self.log
197 app.log = self.log
193 app.overwrite = self.overwrite
198 app.overwrite = self.overwrite
194 app.copy_config_files=True
199 app.copy_config_files=True
195 app.profile = self.profile
200 app.profile = self.profile
196 app.init_profile_dir()
201 app.init_profile_dir()
197 app.init_config_files()
202 app.init_config_files()
198
203
199 def stage_default_config_file(self):
204 def stage_default_config_file(self):
200 pass
205 pass
201
206
207 main_examples = """
208 ipython profile create -h # show the help string for the create subcommand
209 ipython profile list -h # show the help string for the list subcommand
210 """
211
202 class ProfileApp(Application):
212 class ProfileApp(Application):
203 name = u'ipython-profile'
213 name = u'ipython-profile'
204 description = profile_help
214 description = profile_help
205
215 examples = main_examples
216
206 subcommands = Dict(dict(
217 subcommands = Dict(dict(
207 create = (ProfileCreate, "Create a new profile dir with default config files"),
218 create = (ProfileCreate, "Create a new profile dir with default config files"),
208 list = (ProfileList, "List existing profiles")
219 list = (ProfileList, "List existing profiles")
209 ))
220 ))
210
221
211 def start(self):
222 def start(self):
212 if self.subapp is None:
223 if self.subapp is None:
213 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
224 print "No subcommand specified. Must specify one of: %s"%(self.subcommands.keys())
214 print
225 print
215 self.print_description()
226 self.print_description()
216 self.print_subcommands()
227 self.print_subcommands()
217 self.exit(1)
228 self.exit(1)
218 else:
229 else:
219 return self.subapp.start()
230 return self.subapp.start()
220
231
@@ -1,422 +1,429 b''
1 """ A minimal application using the Qt console-style IPython frontend.
1 """ A minimal application using the Qt console-style IPython frontend.
2
2
3 This is not a complete console app, as subprocess will not be able to receive
3 This is not a complete console app, as subprocess will not be able to receive
4 input, there is no real readline support, among other limitations.
4 input, there is no real readline support, among other limitations.
5
5
6 Authors:
6 Authors:
7
7
8 * Evan Patterson
8 * Evan Patterson
9 * Min RK
9 * Min RK
10 * Erik Tollerud
10 * Erik Tollerud
11 * Fernando Perez
11 * Fernando Perez
12
12
13 """
13 """
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # stdlib imports
19 # stdlib imports
20 import os
20 import os
21 import signal
21 import signal
22 import sys
22 import sys
23
23
24 # System library imports
24 # System library imports
25 from IPython.external.qt import QtGui
25 from IPython.external.qt import QtGui
26 from pygments.styles import get_all_styles
26 from pygments.styles import get_all_styles
27
27
28 # Local imports
28 # Local imports
29 from IPython.config.application import boolean_flag
29 from IPython.config.application import boolean_flag
30 from IPython.core.application import BaseIPythonApplication
30 from IPython.core.application import BaseIPythonApplication
31 from IPython.core.profiledir import ProfileDir
31 from IPython.core.profiledir import ProfileDir
32 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
32 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
33 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
33 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
34 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
34 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
35 from IPython.frontend.qt.console import styles
35 from IPython.frontend.qt.console import styles
36 from IPython.frontend.qt.kernelmanager import QtKernelManager
36 from IPython.frontend.qt.kernelmanager import QtKernelManager
37 from IPython.utils.traitlets import (
37 from IPython.utils.traitlets import (
38 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
38 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
39 )
39 )
40 from IPython.zmq.ipkernel import (
40 from IPython.zmq.ipkernel import (
41 flags as ipkernel_flags,
41 flags as ipkernel_flags,
42 aliases as ipkernel_aliases,
42 aliases as ipkernel_aliases,
43 IPKernelApp
43 IPKernelApp
44 )
44 )
45 from IPython.zmq.session import Session
45 from IPython.zmq.session import Session
46 from IPython.zmq.zmqshell import ZMQInteractiveShell
46 from IPython.zmq.zmqshell import ZMQInteractiveShell
47
47
48
48
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50 # Network Constants
50 # Network Constants
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52
52
53 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
53 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
54
54
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56 # Classes
56 # Classes
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58
58
59 class MainWindow(QtGui.QMainWindow):
59 class MainWindow(QtGui.QMainWindow):
60
60
61 #---------------------------------------------------------------------------
61 #---------------------------------------------------------------------------
62 # 'object' interface
62 # 'object' interface
63 #---------------------------------------------------------------------------
63 #---------------------------------------------------------------------------
64
64
65 def __init__(self, app, frontend, existing=False, may_close=True,
65 def __init__(self, app, frontend, existing=False, may_close=True,
66 confirm_exit=True):
66 confirm_exit=True):
67 """ Create a MainWindow for the specified FrontendWidget.
67 """ Create a MainWindow for the specified FrontendWidget.
68
68
69 The app is passed as an argument to allow for different
69 The app is passed as an argument to allow for different
70 closing behavior depending on whether we are the Kernel's parent.
70 closing behavior depending on whether we are the Kernel's parent.
71
71
72 If existing is True, then this Console does not own the Kernel.
72 If existing is True, then this Console does not own the Kernel.
73
73
74 If may_close is True, then this Console is permitted to close the kernel
74 If may_close is True, then this Console is permitted to close the kernel
75 """
75 """
76 super(MainWindow, self).__init__()
76 super(MainWindow, self).__init__()
77 self._app = app
77 self._app = app
78 self._frontend = frontend
78 self._frontend = frontend
79 self._existing = existing
79 self._existing = existing
80 if existing:
80 if existing:
81 self._may_close = may_close
81 self._may_close = may_close
82 else:
82 else:
83 self._may_close = True
83 self._may_close = True
84 self._frontend.exit_requested.connect(self.close)
84 self._frontend.exit_requested.connect(self.close)
85 self._confirm_exit = confirm_exit
85 self._confirm_exit = confirm_exit
86 self.setCentralWidget(frontend)
86 self.setCentralWidget(frontend)
87
87
88 #---------------------------------------------------------------------------
88 #---------------------------------------------------------------------------
89 # QWidget interface
89 # QWidget interface
90 #---------------------------------------------------------------------------
90 #---------------------------------------------------------------------------
91
91
92 def closeEvent(self, event):
92 def closeEvent(self, event):
93 """ Close the window and the kernel (if necessary).
93 """ Close the window and the kernel (if necessary).
94
94
95 This will prompt the user if they are finished with the kernel, and if
95 This will prompt the user if they are finished with the kernel, and if
96 so, closes the kernel cleanly. Alternatively, if the exit magic is used,
96 so, closes the kernel cleanly. Alternatively, if the exit magic is used,
97 it closes without prompt.
97 it closes without prompt.
98 """
98 """
99 keepkernel = None #Use the prompt by default
99 keepkernel = None #Use the prompt by default
100 if hasattr(self._frontend,'_keep_kernel_on_exit'): #set by exit magic
100 if hasattr(self._frontend,'_keep_kernel_on_exit'): #set by exit magic
101 keepkernel = self._frontend._keep_kernel_on_exit
101 keepkernel = self._frontend._keep_kernel_on_exit
102
102
103 kernel_manager = self._frontend.kernel_manager
103 kernel_manager = self._frontend.kernel_manager
104
104
105 if keepkernel is None and not self._confirm_exit:
105 if keepkernel is None and not self._confirm_exit:
106 # don't prompt, just terminate the kernel if we own it
106 # don't prompt, just terminate the kernel if we own it
107 # or leave it alone if we don't
107 # or leave it alone if we don't
108 keepkernel = not self._existing
108 keepkernel = not self._existing
109
109
110 if keepkernel is None: #show prompt
110 if keepkernel is None: #show prompt
111 if kernel_manager and kernel_manager.channels_running:
111 if kernel_manager and kernel_manager.channels_running:
112 title = self.window().windowTitle()
112 title = self.window().windowTitle()
113 cancel = QtGui.QMessageBox.Cancel
113 cancel = QtGui.QMessageBox.Cancel
114 okay = QtGui.QMessageBox.Ok
114 okay = QtGui.QMessageBox.Ok
115 if self._may_close:
115 if self._may_close:
116 msg = "You are closing this Console window."
116 msg = "You are closing this Console window."
117 info = "Would you like to quit the Kernel and all attached Consoles as well?"
117 info = "Would you like to quit the Kernel and all attached Consoles as well?"
118 justthis = QtGui.QPushButton("&No, just this Console", self)
118 justthis = QtGui.QPushButton("&No, just this Console", self)
119 justthis.setShortcut('N')
119 justthis.setShortcut('N')
120 closeall = QtGui.QPushButton("&Yes, quit everything", self)
120 closeall = QtGui.QPushButton("&Yes, quit everything", self)
121 closeall.setShortcut('Y')
121 closeall.setShortcut('Y')
122 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
122 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
123 title, msg)
123 title, msg)
124 box.setInformativeText(info)
124 box.setInformativeText(info)
125 box.addButton(cancel)
125 box.addButton(cancel)
126 box.addButton(justthis, QtGui.QMessageBox.NoRole)
126 box.addButton(justthis, QtGui.QMessageBox.NoRole)
127 box.addButton(closeall, QtGui.QMessageBox.YesRole)
127 box.addButton(closeall, QtGui.QMessageBox.YesRole)
128 box.setDefaultButton(closeall)
128 box.setDefaultButton(closeall)
129 box.setEscapeButton(cancel)
129 box.setEscapeButton(cancel)
130 reply = box.exec_()
130 reply = box.exec_()
131 if reply == 1: # close All
131 if reply == 1: # close All
132 kernel_manager.shutdown_kernel()
132 kernel_manager.shutdown_kernel()
133 #kernel_manager.stop_channels()
133 #kernel_manager.stop_channels()
134 event.accept()
134 event.accept()
135 elif reply == 0: # close Console
135 elif reply == 0: # close Console
136 if not self._existing:
136 if not self._existing:
137 # Have kernel: don't quit, just close the window
137 # Have kernel: don't quit, just close the window
138 self._app.setQuitOnLastWindowClosed(False)
138 self._app.setQuitOnLastWindowClosed(False)
139 self.deleteLater()
139 self.deleteLater()
140 event.accept()
140 event.accept()
141 else:
141 else:
142 event.ignore()
142 event.ignore()
143 else:
143 else:
144 reply = QtGui.QMessageBox.question(self, title,
144 reply = QtGui.QMessageBox.question(self, title,
145 "Are you sure you want to close this Console?"+
145 "Are you sure you want to close this Console?"+
146 "\nThe Kernel and other Consoles will remain active.",
146 "\nThe Kernel and other Consoles will remain active.",
147 okay|cancel,
147 okay|cancel,
148 defaultButton=okay
148 defaultButton=okay
149 )
149 )
150 if reply == okay:
150 if reply == okay:
151 event.accept()
151 event.accept()
152 else:
152 else:
153 event.ignore()
153 event.ignore()
154 elif keepkernel: #close console but leave kernel running (no prompt)
154 elif keepkernel: #close console but leave kernel running (no prompt)
155 if kernel_manager and kernel_manager.channels_running:
155 if kernel_manager and kernel_manager.channels_running:
156 if not self._existing:
156 if not self._existing:
157 # I have the kernel: don't quit, just close the window
157 # I have the kernel: don't quit, just close the window
158 self._app.setQuitOnLastWindowClosed(False)
158 self._app.setQuitOnLastWindowClosed(False)
159 event.accept()
159 event.accept()
160 else: #close console and kernel (no prompt)
160 else: #close console and kernel (no prompt)
161 if kernel_manager and kernel_manager.channels_running:
161 if kernel_manager and kernel_manager.channels_running:
162 kernel_manager.shutdown_kernel()
162 kernel_manager.shutdown_kernel()
163 event.accept()
163 event.accept()
164
164
165 #-----------------------------------------------------------------------------
165 #-----------------------------------------------------------------------------
166 # Aliases and Flags
166 # Aliases and Flags
167 #-----------------------------------------------------------------------------
167 #-----------------------------------------------------------------------------
168
168
169 flags = dict(ipkernel_flags)
169 flags = dict(ipkernel_flags)
170
170
171 flags.update({
171 flags.update({
172 'existing' : ({'IPythonQtConsoleApp' : {'existing' : True}},
172 'existing' : ({'IPythonQtConsoleApp' : {'existing' : True}},
173 "Connect to an existing kernel."),
173 "Connect to an existing kernel."),
174 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
174 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
175 "Use a pure Python kernel instead of an IPython kernel."),
175 "Use a pure Python kernel instead of an IPython kernel."),
176 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
176 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
177 "Disable rich text support."),
177 "Disable rich text support."),
178 })
178 })
179 flags.update(boolean_flag(
179 flags.update(boolean_flag(
180 'gui-completion', 'ConsoleWidget.gui_completion',
180 'gui-completion', 'ConsoleWidget.gui_completion',
181 "use a GUI widget for tab completion",
181 "use a GUI widget for tab completion",
182 "use plaintext output for completion"
182 "use plaintext output for completion"
183 ))
183 ))
184 flags.update(boolean_flag(
184 flags.update(boolean_flag(
185 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
185 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
186 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
186 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
187 to force a direct exit without any confirmation.
187 to force a direct exit without any confirmation.
188 """,
188 """,
189 """Don't prompt the user when exiting. This will terminate the kernel
189 """Don't prompt the user when exiting. This will terminate the kernel
190 if it is owned by the frontend, and leave it alive if it is external.
190 if it is owned by the frontend, and leave it alive if it is external.
191 """
191 """
192 ))
192 ))
193 # the flags that are specific to the frontend
193 # the flags that are specific to the frontend
194 # these must be scrubbed before being passed to the kernel,
194 # these must be scrubbed before being passed to the kernel,
195 # or it will raise an error on unrecognized flags
195 # or it will raise an error on unrecognized flags
196 qt_flags = ['existing', 'pure', 'plain', 'gui-completion', 'no-gui-completion',
196 qt_flags = ['existing', 'pure', 'plain', 'gui-completion', 'no-gui-completion',
197 'confirm-exit', 'no-confirm-exit']
197 'confirm-exit', 'no-confirm-exit']
198
198
199 aliases = dict(ipkernel_aliases)
199 aliases = dict(ipkernel_aliases)
200
200
201 aliases.update(dict(
201 aliases.update(dict(
202 hb = 'IPythonQtConsoleApp.hb_port',
202 hb = 'IPythonQtConsoleApp.hb_port',
203 shell = 'IPythonQtConsoleApp.shell_port',
203 shell = 'IPythonQtConsoleApp.shell_port',
204 iopub = 'IPythonQtConsoleApp.iopub_port',
204 iopub = 'IPythonQtConsoleApp.iopub_port',
205 stdin = 'IPythonQtConsoleApp.stdin_port',
205 stdin = 'IPythonQtConsoleApp.stdin_port',
206 ip = 'IPythonQtConsoleApp.ip',
206 ip = 'IPythonQtConsoleApp.ip',
207
207
208 plain = 'IPythonQtConsoleApp.plain',
208 plain = 'IPythonQtConsoleApp.plain',
209 pure = 'IPythonQtConsoleApp.pure',
209 pure = 'IPythonQtConsoleApp.pure',
210 gui_completion = 'ConsoleWidget.gui_completion',
210 gui_completion = 'ConsoleWidget.gui_completion',
211 style = 'IPythonWidget.syntax_style',
211 style = 'IPythonWidget.syntax_style',
212 stylesheet = 'IPythonQtConsoleApp.stylesheet',
212 stylesheet = 'IPythonQtConsoleApp.stylesheet',
213 colors = 'ZMQInteractiveShell.colors',
213 colors = 'ZMQInteractiveShell.colors',
214
214
215 editor = 'IPythonWidget.editor',
215 editor = 'IPythonWidget.editor',
216 ))
216 ))
217
217
218 #-----------------------------------------------------------------------------
218 #-----------------------------------------------------------------------------
219 # IPythonQtConsole
219 # IPythonQtConsole
220 #-----------------------------------------------------------------------------
220 #-----------------------------------------------------------------------------
221
222 qt_examples = """
223 ipython qtconsole # start the qtconsole
224 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
225 """
226
221 class IPythonQtConsoleApp(BaseIPythonApplication):
227 class IPythonQtConsoleApp(BaseIPythonApplication):
222 name = 'ipython-qtconsole'
228 name = 'ipython-qtconsole'
223 default_config_file_name='ipython_config.py'
229 default_config_file_name='ipython_config.py'
224
230
225 description = """
231 description = """
226 The IPython QtConsole.
232 The IPython QtConsole.
227
233
228 This launches a Console-style application using Qt. It is not a full
234 This launches a Console-style application using Qt. It is not a full
229 console, in that launched terminal subprocesses will not.
235 console, in that launched terminal subprocesses will not.
230
236
231 The QtConsole supports various extra features beyond the
237 The QtConsole supports various extra features beyond the
232
238
233 """
239 """
234
240 examples = qt_examples
241
235 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
242 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
236 flags = Dict(flags)
243 flags = Dict(flags)
237 aliases = Dict(aliases)
244 aliases = Dict(aliases)
238
245
239 kernel_argv = List(Unicode)
246 kernel_argv = List(Unicode)
240
247
241 # connection info:
248 # connection info:
242 ip = Unicode(LOCALHOST, config=True,
249 ip = Unicode(LOCALHOST, config=True,
243 help="""Set the kernel\'s IP address [default localhost].
250 help="""Set the kernel\'s IP address [default localhost].
244 If the IP address is something other than localhost, then
251 If the IP address is something other than localhost, then
245 Consoles on other machines will be able to connect
252 Consoles on other machines will be able to connect
246 to the Kernel, so be careful!"""
253 to the Kernel, so be careful!"""
247 )
254 )
248 hb_port = Int(0, config=True,
255 hb_port = Int(0, config=True,
249 help="set the heartbeat port [default: random]")
256 help="set the heartbeat port [default: random]")
250 shell_port = Int(0, config=True,
257 shell_port = Int(0, config=True,
251 help="set the shell (XREP) port [default: random]")
258 help="set the shell (XREP) port [default: random]")
252 iopub_port = Int(0, config=True,
259 iopub_port = Int(0, config=True,
253 help="set the iopub (PUB) port [default: random]")
260 help="set the iopub (PUB) port [default: random]")
254 stdin_port = Int(0, config=True,
261 stdin_port = Int(0, config=True,
255 help="set the stdin (XREQ) port [default: random]")
262 help="set the stdin (XREQ) port [default: random]")
256
263
257 existing = CBool(False, config=True,
264 existing = CBool(False, config=True,
258 help="Whether to connect to an already running Kernel.")
265 help="Whether to connect to an already running Kernel.")
259
266
260 stylesheet = Unicode('', config=True,
267 stylesheet = Unicode('', config=True,
261 help="path to a custom CSS stylesheet")
268 help="path to a custom CSS stylesheet")
262
269
263 pure = CBool(False, config=True,
270 pure = CBool(False, config=True,
264 help="Use a pure Python kernel instead of an IPython kernel.")
271 help="Use a pure Python kernel instead of an IPython kernel.")
265 plain = CBool(False, config=True,
272 plain = CBool(False, config=True,
266 help="Use a plaintext widget instead of rich text (plain can't print/save).")
273 help="Use a plaintext widget instead of rich text (plain can't print/save).")
267
274
268 def _pure_changed(self, name, old, new):
275 def _pure_changed(self, name, old, new):
269 kind = 'plain' if self.plain else 'rich'
276 kind = 'plain' if self.plain else 'rich'
270 self.config.ConsoleWidget.kind = kind
277 self.config.ConsoleWidget.kind = kind
271 if self.pure:
278 if self.pure:
272 self.widget_factory = FrontendWidget
279 self.widget_factory = FrontendWidget
273 elif self.plain:
280 elif self.plain:
274 self.widget_factory = IPythonWidget
281 self.widget_factory = IPythonWidget
275 else:
282 else:
276 self.widget_factory = RichIPythonWidget
283 self.widget_factory = RichIPythonWidget
277
284
278 _plain_changed = _pure_changed
285 _plain_changed = _pure_changed
279
286
280 confirm_exit = CBool(True, config=True,
287 confirm_exit = CBool(True, config=True,
281 help="""
288 help="""
282 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
289 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
283 to force a direct exit without any confirmation.""",
290 to force a direct exit without any confirmation.""",
284 )
291 )
285
292
286 # the factory for creating a widget
293 # the factory for creating a widget
287 widget_factory = Any(RichIPythonWidget)
294 widget_factory = Any(RichIPythonWidget)
288
295
289 def parse_command_line(self, argv=None):
296 def parse_command_line(self, argv=None):
290 super(IPythonQtConsoleApp, self).parse_command_line(argv)
297 super(IPythonQtConsoleApp, self).parse_command_line(argv)
291 if argv is None:
298 if argv is None:
292 argv = sys.argv[1:]
299 argv = sys.argv[1:]
293
300
294 self.kernel_argv = list(argv) # copy
301 self.kernel_argv = list(argv) # copy
295 # kernel should inherit default config file from frontend
302 # kernel should inherit default config file from frontend
296 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
303 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
297 # scrub frontend-specific flags
304 # scrub frontend-specific flags
298 for a in argv:
305 for a in argv:
299 if a.startswith('-') and a.lstrip('-') in qt_flags:
306 if a.startswith('-') and a.lstrip('-') in qt_flags:
300 self.kernel_argv.remove(a)
307 self.kernel_argv.remove(a)
301
308
302 def init_kernel_manager(self):
309 def init_kernel_manager(self):
303 # Don't let Qt or ZMQ swallow KeyboardInterupts.
310 # Don't let Qt or ZMQ swallow KeyboardInterupts.
304 signal.signal(signal.SIGINT, signal.SIG_DFL)
311 signal.signal(signal.SIGINT, signal.SIG_DFL)
305
312
306 # Create a KernelManager and start a kernel.
313 # Create a KernelManager and start a kernel.
307 self.kernel_manager = QtKernelManager(
314 self.kernel_manager = QtKernelManager(
308 shell_address=(self.ip, self.shell_port),
315 shell_address=(self.ip, self.shell_port),
309 sub_address=(self.ip, self.iopub_port),
316 sub_address=(self.ip, self.iopub_port),
310 stdin_address=(self.ip, self.stdin_port),
317 stdin_address=(self.ip, self.stdin_port),
311 hb_address=(self.ip, self.hb_port),
318 hb_address=(self.ip, self.hb_port),
312 config=self.config
319 config=self.config
313 )
320 )
314 # start the kernel
321 # start the kernel
315 if not self.existing:
322 if not self.existing:
316 kwargs = dict(ip=self.ip, ipython=not self.pure)
323 kwargs = dict(ip=self.ip, ipython=not self.pure)
317 kwargs['extra_arguments'] = self.kernel_argv
324 kwargs['extra_arguments'] = self.kernel_argv
318 self.kernel_manager.start_kernel(**kwargs)
325 self.kernel_manager.start_kernel(**kwargs)
319 self.kernel_manager.start_channels()
326 self.kernel_manager.start_channels()
320
327
321
328
322 def init_qt_elements(self):
329 def init_qt_elements(self):
323 # Create the widget.
330 # Create the widget.
324 self.app = QtGui.QApplication([])
331 self.app = QtGui.QApplication([])
325 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
332 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
326 self.widget = self.widget_factory(config=self.config,
333 self.widget = self.widget_factory(config=self.config,
327 local_kernel=local_kernel)
334 local_kernel=local_kernel)
328 self.widget.kernel_manager = self.kernel_manager
335 self.widget.kernel_manager = self.kernel_manager
329 self.window = MainWindow(self.app, self.widget, self.existing,
336 self.window = MainWindow(self.app, self.widget, self.existing,
330 may_close=local_kernel,
337 may_close=local_kernel,
331 confirm_exit=self.confirm_exit)
338 confirm_exit=self.confirm_exit)
332 self.window.setWindowTitle('Python' if self.pure else 'IPython')
339 self.window.setWindowTitle('Python' if self.pure else 'IPython')
333
340
334 def init_colors(self):
341 def init_colors(self):
335 """Configure the coloring of the widget"""
342 """Configure the coloring of the widget"""
336 # Note: This will be dramatically simplified when colors
343 # Note: This will be dramatically simplified when colors
337 # are removed from the backend.
344 # are removed from the backend.
338
345
339 if self.pure:
346 if self.pure:
340 # only IPythonWidget supports styling
347 # only IPythonWidget supports styling
341 return
348 return
342
349
343 # parse the colors arg down to current known labels
350 # parse the colors arg down to current known labels
344 try:
351 try:
345 colors = self.config.ZMQInteractiveShell.colors
352 colors = self.config.ZMQInteractiveShell.colors
346 except AttributeError:
353 except AttributeError:
347 colors = None
354 colors = None
348 try:
355 try:
349 style = self.config.IPythonWidget.colors
356 style = self.config.IPythonWidget.colors
350 except AttributeError:
357 except AttributeError:
351 style = None
358 style = None
352
359
353 # find the value for colors:
360 # find the value for colors:
354 if colors:
361 if colors:
355 colors=colors.lower()
362 colors=colors.lower()
356 if colors in ('lightbg', 'light'):
363 if colors in ('lightbg', 'light'):
357 colors='lightbg'
364 colors='lightbg'
358 elif colors in ('dark', 'linux'):
365 elif colors in ('dark', 'linux'):
359 colors='linux'
366 colors='linux'
360 else:
367 else:
361 colors='nocolor'
368 colors='nocolor'
362 elif style:
369 elif style:
363 if style=='bw':
370 if style=='bw':
364 colors='nocolor'
371 colors='nocolor'
365 elif styles.dark_style(style):
372 elif styles.dark_style(style):
366 colors='linux'
373 colors='linux'
367 else:
374 else:
368 colors='lightbg'
375 colors='lightbg'
369 else:
376 else:
370 colors=None
377 colors=None
371
378
372 # Configure the style.
379 # Configure the style.
373 widget = self.widget
380 widget = self.widget
374 if style:
381 if style:
375 widget.style_sheet = styles.sheet_from_template(style, colors)
382 widget.style_sheet = styles.sheet_from_template(style, colors)
376 widget.syntax_style = style
383 widget.syntax_style = style
377 widget._syntax_style_changed()
384 widget._syntax_style_changed()
378 widget._style_sheet_changed()
385 widget._style_sheet_changed()
379 elif colors:
386 elif colors:
380 # use a default style
387 # use a default style
381 widget.set_default_style(colors=colors)
388 widget.set_default_style(colors=colors)
382 else:
389 else:
383 # this is redundant for now, but allows the widget's
390 # this is redundant for now, but allows the widget's
384 # defaults to change
391 # defaults to change
385 widget.set_default_style()
392 widget.set_default_style()
386
393
387 if self.stylesheet:
394 if self.stylesheet:
388 # we got an expicit stylesheet
395 # we got an expicit stylesheet
389 if os.path.isfile(self.stylesheet):
396 if os.path.isfile(self.stylesheet):
390 with open(self.stylesheet) as f:
397 with open(self.stylesheet) as f:
391 sheet = f.read()
398 sheet = f.read()
392 widget.style_sheet = sheet
399 widget.style_sheet = sheet
393 widget._style_sheet_changed()
400 widget._style_sheet_changed()
394 else:
401 else:
395 raise IOError("Stylesheet %r not found."%self.stylesheet)
402 raise IOError("Stylesheet %r not found."%self.stylesheet)
396
403
397 def initialize(self, argv=None):
404 def initialize(self, argv=None):
398 super(IPythonQtConsoleApp, self).initialize(argv)
405 super(IPythonQtConsoleApp, self).initialize(argv)
399 self.init_kernel_manager()
406 self.init_kernel_manager()
400 self.init_qt_elements()
407 self.init_qt_elements()
401 self.init_colors()
408 self.init_colors()
402
409
403 def start(self):
410 def start(self):
404
411
405 # draw the window
412 # draw the window
406 self.window.show()
413 self.window.show()
407
414
408 # Start the application main loop.
415 # Start the application main loop.
409 self.app.exec_()
416 self.app.exec_()
410
417
411 #-----------------------------------------------------------------------------
418 #-----------------------------------------------------------------------------
412 # Main entry point
419 # Main entry point
413 #-----------------------------------------------------------------------------
420 #-----------------------------------------------------------------------------
414
421
415 def main():
422 def main():
416 app = IPythonQtConsoleApp()
423 app = IPythonQtConsoleApp()
417 app.initialize()
424 app.initialize()
418 app.start()
425 app.start()
419
426
420
427
421 if __name__ == '__main__':
428 if __name__ == '__main__':
422 main()
429 main()
@@ -1,364 +1,375 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-2010 The IPython Development Team
16 # Copyright (C) 2008-2010 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
33 Config, PyFileConfigLoader
34 )
34 )
35 from IPython.config.application import boolean_flag
35 from IPython.config.application import boolean_flag
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.crashhandler import CrashHandler
38 from IPython.core.crashhandler import CrashHandler
39 from IPython.core.formatters import PlainTextFormatter
39 from IPython.core.formatters import PlainTextFormatter
40 from IPython.core.application import (
40 from IPython.core.application import (
41 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
41 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
42 )
42 )
43 from IPython.core.shellapp import (
43 from IPython.core.shellapp import (
44 InteractiveShellApp, shell_flags, shell_aliases
44 InteractiveShellApp, shell_flags, shell_aliases
45 )
45 )
46 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
46 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
47 from IPython.lib import inputhook
47 from IPython.lib import inputhook
48 from IPython.utils import warn
48 from IPython.utils import warn
49 from IPython.utils.path import get_ipython_dir, check_for_old_config
49 from IPython.utils.path import get_ipython_dir, check_for_old_config
50 from IPython.utils.traitlets import (
50 from IPython.utils.traitlets import (
51 Bool, Dict, CaselessStrEnum
51 Bool, Dict, CaselessStrEnum
52 )
52 )
53
53
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55 # Globals, utilities and helpers
55 # Globals, utilities and helpers
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57
57
58 #: The default config file name for this application.
58 #: The default config file name for this application.
59 default_config_file_name = u'ipython_config.py'
59 default_config_file_name = u'ipython_config.py'
60
60
61
61
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63 # Crash handler for this application
63 # Crash handler for this application
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65
65
66 class IPAppCrashHandler(CrashHandler):
66 class IPAppCrashHandler(CrashHandler):
67 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
67 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
68
68
69 def __init__(self, app):
69 def __init__(self, app):
70 contact_name = release.authors['Fernando'][0]
70 contact_name = release.authors['Fernando'][0]
71 contact_email = release.authors['Fernando'][1]
71 contact_email = release.authors['Fernando'][1]
72 bug_tracker = 'http://github.com/ipython/ipython/issues'
72 bug_tracker = 'http://github.com/ipython/ipython/issues'
73 super(IPAppCrashHandler,self).__init__(
73 super(IPAppCrashHandler,self).__init__(
74 app, contact_name, contact_email, bug_tracker
74 app, contact_name, contact_email, bug_tracker
75 )
75 )
76
76
77 def make_report(self,traceback):
77 def make_report(self,traceback):
78 """Return a string containing a crash report."""
78 """Return a string containing a crash report."""
79
79
80 sec_sep = self.section_sep
80 sec_sep = self.section_sep
81 # Start with parent report
81 # Start with parent report
82 report = [super(IPAppCrashHandler, self).make_report(traceback)]
82 report = [super(IPAppCrashHandler, self).make_report(traceback)]
83 # Add interactive-specific info we may have
83 # Add interactive-specific info we may have
84 rpt_add = report.append
84 rpt_add = report.append
85 try:
85 try:
86 rpt_add(sec_sep+"History of session input:")
86 rpt_add(sec_sep+"History of session input:")
87 for line in self.app.shell.user_ns['_ih']:
87 for line in self.app.shell.user_ns['_ih']:
88 rpt_add(line)
88 rpt_add(line)
89 rpt_add('\n*** Last line of input (may not be in above history):\n')
89 rpt_add('\n*** Last line of input (may not be in above history):\n')
90 rpt_add(self.app.shell._last_input_line+'\n')
90 rpt_add(self.app.shell._last_input_line+'\n')
91 except:
91 except:
92 pass
92 pass
93
93
94 return ''.join(report)
94 return ''.join(report)
95
95
96 #-----------------------------------------------------------------------------
96 #-----------------------------------------------------------------------------
97 # Aliases and Flags
97 # Aliases and Flags
98 #-----------------------------------------------------------------------------
98 #-----------------------------------------------------------------------------
99 flags = dict(base_flags)
99 flags = dict(base_flags)
100 flags.update(shell_flags)
100 flags.update(shell_flags)
101 addflag = lambda *args: flags.update(boolean_flag(*args))
101 addflag = lambda *args: flags.update(boolean_flag(*args))
102 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
102 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
103 'Turn on auto editing of files with syntax errors.',
103 'Turn on auto editing of files with syntax errors.',
104 'Turn off auto editing of files with syntax errors.'
104 'Turn off auto editing of files with syntax errors.'
105 )
105 )
106 addflag('banner', 'TerminalIPythonApp.display_banner',
106 addflag('banner', 'TerminalIPythonApp.display_banner',
107 "Display a banner upon starting IPython.",
107 "Display a banner upon starting IPython.",
108 "Don't display a banner upon starting IPython."
108 "Don't display a banner upon starting IPython."
109 )
109 )
110 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
110 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
111 """Set to confirm when you try to exit IPython with an EOF (Control-D
111 """Set to confirm when you try to exit IPython with an EOF (Control-D
112 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
112 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
113 you can force a direct exit without any confirmation.""",
113 you can force a direct exit without any confirmation.""",
114 "Don't prompt the user when exiting."
114 "Don't prompt the user when exiting."
115 )
115 )
116 addflag('term-title', 'TerminalInteractiveShell.term_title',
116 addflag('term-title', 'TerminalInteractiveShell.term_title',
117 "Enable auto setting the terminal title.",
117 "Enable auto setting the terminal title.",
118 "Disable auto setting the terminal title."
118 "Disable auto setting the terminal title."
119 )
119 )
120 classic_config = Config()
120 classic_config = Config()
121 classic_config.InteractiveShell.cache_size = 0
121 classic_config.InteractiveShell.cache_size = 0
122 classic_config.PlainTextFormatter.pprint = False
122 classic_config.PlainTextFormatter.pprint = False
123 classic_config.InteractiveShell.prompt_in1 = '>>> '
123 classic_config.InteractiveShell.prompt_in1 = '>>> '
124 classic_config.InteractiveShell.prompt_in2 = '... '
124 classic_config.InteractiveShell.prompt_in2 = '... '
125 classic_config.InteractiveShell.prompt_out = ''
125 classic_config.InteractiveShell.prompt_out = ''
126 classic_config.InteractiveShell.separate_in = ''
126 classic_config.InteractiveShell.separate_in = ''
127 classic_config.InteractiveShell.separate_out = ''
127 classic_config.InteractiveShell.separate_out = ''
128 classic_config.InteractiveShell.separate_out2 = ''
128 classic_config.InteractiveShell.separate_out2 = ''
129 classic_config.InteractiveShell.colors = 'NoColor'
129 classic_config.InteractiveShell.colors = 'NoColor'
130 classic_config.InteractiveShell.xmode = 'Plain'
130 classic_config.InteractiveShell.xmode = 'Plain'
131
131
132 flags['classic']=(
132 flags['classic']=(
133 classic_config,
133 classic_config,
134 "Gives IPython a similar feel to the classic Python prompt."
134 "Gives IPython a similar feel to the classic Python prompt."
135 )
135 )
136 # # log doesn't make so much sense this way anymore
136 # # log doesn't make so much sense this way anymore
137 # paa('--log','-l',
137 # paa('--log','-l',
138 # action='store_true', dest='InteractiveShell.logstart',
138 # action='store_true', dest='InteractiveShell.logstart',
139 # help="Start logging to the default log file (./ipython_log.py).")
139 # help="Start logging to the default log file (./ipython_log.py).")
140 #
140 #
141 # # quick is harder to implement
141 # # quick is harder to implement
142 flags['quick']=(
142 flags['quick']=(
143 {'TerminalIPythonApp' : {'quick' : True}},
143 {'TerminalIPythonApp' : {'quick' : True}},
144 "Enable quick startup with no config files."
144 "Enable quick startup with no config files."
145 )
145 )
146
146
147 flags['i'] = (
147 flags['i'] = (
148 {'TerminalIPythonApp' : {'force_interact' : True}},
148 {'TerminalIPythonApp' : {'force_interact' : True}},
149 """also works as '-i'
149 """also works as '-i'
150 If running code from the command line, become interactive afterwards."""
150 If running code from the command line, become interactive afterwards."""
151 )
151 )
152 flags['pylab'] = (
152 flags['pylab'] = (
153 {'TerminalIPythonApp' : {'pylab' : 'auto'}},
153 {'TerminalIPythonApp' : {'pylab' : 'auto'}},
154 """Pre-load matplotlib and numpy for interactive use with
154 """Pre-load matplotlib and numpy for interactive use with
155 the default matplotlib backend."""
155 the default matplotlib backend."""
156 )
156 )
157
157
158 aliases = dict(base_aliases)
158 aliases = dict(base_aliases)
159 aliases.update(shell_aliases)
159 aliases.update(shell_aliases)
160
160
161 # it's possible we don't want short aliases for *all* of these:
161 # it's possible we don't want short aliases for *all* of these:
162 aliases.update(dict(
162 aliases.update(dict(
163 gui='TerminalIPythonApp.gui',
163 gui='TerminalIPythonApp.gui',
164 pylab='TerminalIPythonApp.pylab',
164 pylab='TerminalIPythonApp.pylab',
165 ))
165 ))
166
166
167 #-----------------------------------------------------------------------------
167 #-----------------------------------------------------------------------------
168 # Main classes and functions
168 # Main classes and functions
169 #-----------------------------------------------------------------------------
169 #-----------------------------------------------------------------------------
170
170
171 examples = """
172 ipython --pylab # start in pylab mode
173 ipython --pylab=qt # start in pylab mode with the qt4 backend
174 ipython --log_level=DEBUG # set logging to DEBUG
175 ipython --profile=foo # start with profile foo
176 ipython qtconsole # start the qtconsole GUI application
177 ipython profile -h # show the help string for the profile subcmd
178 ipython qtconsole -h # show the help string for the qtconsole subcmd
179 """
180
171 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
181 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
172 name = u'ipython'
182 name = u'ipython'
173 description = usage.cl_usage
183 description = usage.cl_usage
174 default_config_file_name = default_config_file_name
184 default_config_file_name = default_config_file_name
175 crash_handler_class = IPAppCrashHandler
185 crash_handler_class = IPAppCrashHandler
176
186 examples = examples
187
177 flags = Dict(flags)
188 flags = Dict(flags)
178 aliases = Dict(aliases)
189 aliases = Dict(aliases)
179 classes = [InteractiveShellApp, TerminalInteractiveShell, ProfileDir, PlainTextFormatter]
190 classes = [InteractiveShellApp, TerminalInteractiveShell, ProfileDir, PlainTextFormatter]
180 subcommands = Dict(dict(
191 subcommands = Dict(dict(
181 qtconsole=('IPython.frontend.qt.console.qtconsoleapp.IPythonQtConsoleApp',
192 qtconsole=('IPython.frontend.qt.console.qtconsoleapp.IPythonQtConsoleApp',
182 """Launch the IPython Qt Console."""
193 """Launch the IPython Qt Console."""
183 ),
194 ),
184 profile = ("IPython.core.profileapp.ProfileApp",
195 profile = ("IPython.core.profileapp.ProfileApp",
185 "Create and manage IPython profiles.")
196 "Create and manage IPython profiles.")
186 ))
197 ))
187
198
188 # *do* autocreate requested profile, but don't create the config file.
199 # *do* autocreate requested profile, but don't create the config file.
189 auto_create=Bool(True)
200 auto_create=Bool(True)
190 # configurables
201 # configurables
191 ignore_old_config=Bool(False, config=True,
202 ignore_old_config=Bool(False, config=True,
192 help="Suppress warning messages about legacy config files"
203 help="Suppress warning messages about legacy config files"
193 )
204 )
194 quick = Bool(False, config=True,
205 quick = Bool(False, config=True,
195 help="""Start IPython quickly by skipping the loading of config files."""
206 help="""Start IPython quickly by skipping the loading of config files."""
196 )
207 )
197 def _quick_changed(self, name, old, new):
208 def _quick_changed(self, name, old, new):
198 if new:
209 if new:
199 self.load_config_file = lambda *a, **kw: None
210 self.load_config_file = lambda *a, **kw: None
200 self.ignore_old_config=True
211 self.ignore_old_config=True
201
212
202 gui = CaselessStrEnum(('qt','wx','gtk'), config=True,
213 gui = CaselessStrEnum(('qt','wx','gtk'), config=True,
203 help="Enable GUI event loop integration ('qt', 'wx', 'gtk')."
214 help="Enable GUI event loop integration ('qt', 'wx', 'gtk')."
204 )
215 )
205 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'],
216 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'],
206 config=True,
217 config=True,
207 help="""Pre-load matplotlib and numpy for interactive use,
218 help="""Pre-load matplotlib and numpy for interactive use,
208 selecting a particular matplotlib backend and loop integration.
219 selecting a particular matplotlib backend and loop integration.
209 """
220 """
210 )
221 )
211 display_banner = Bool(True, config=True,
222 display_banner = Bool(True, config=True,
212 help="Whether to display a banner upon starting IPython."
223 help="Whether to display a banner upon starting IPython."
213 )
224 )
214
225
215 # if there is code of files to run from the cmd line, don't interact
226 # if there is code of files to run from the cmd line, don't interact
216 # unless the --i flag (App.force_interact) is true.
227 # unless the --i flag (App.force_interact) is true.
217 force_interact = Bool(False, config=True,
228 force_interact = Bool(False, config=True,
218 help="""If a command or file is given via the command-line,
229 help="""If a command or file is given via the command-line,
219 e.g. 'ipython foo.py"""
230 e.g. 'ipython foo.py"""
220 )
231 )
221 def _force_interact_changed(self, name, old, new):
232 def _force_interact_changed(self, name, old, new):
222 if new:
233 if new:
223 self.interact = True
234 self.interact = True
224
235
225 def _file_to_run_changed(self, name, old, new):
236 def _file_to_run_changed(self, name, old, new):
226 if new and not self.force_interact:
237 if new and not self.force_interact:
227 self.interact = False
238 self.interact = False
228 _code_to_run_changed = _file_to_run_changed
239 _code_to_run_changed = _file_to_run_changed
229
240
230 # internal, not-configurable
241 # internal, not-configurable
231 interact=Bool(True)
242 interact=Bool(True)
232
243
233
244
234 def parse_command_line(self, argv=None):
245 def parse_command_line(self, argv=None):
235 """override to allow old '-pylab' flag with deprecation warning"""
246 """override to allow old '-pylab' flag with deprecation warning"""
236 argv = sys.argv[1:] if argv is None else argv
247 argv = sys.argv[1:] if argv is None else argv
237
248
238 try:
249 try:
239 idx = argv.index('-pylab')
250 idx = argv.index('-pylab')
240 except ValueError:
251 except ValueError:
241 # `-pylab` not given, proceed as normal
252 # `-pylab` not given, proceed as normal
242 pass
253 pass
243 else:
254 else:
244 # deprecated `-pylab` given,
255 # deprecated `-pylab` given,
245 # warn and transform into current syntax
256 # warn and transform into current syntax
246 argv = list(argv) # copy, don't clobber
257 argv = list(argv) # copy, don't clobber
247 warn.warn("`-pylab` flag has been deprecated.\n"
258 warn.warn("`-pylab` flag has been deprecated.\n"
248 " Use `--pylab` instead, or `--pylab=foo` to specify a backend.")
259 " Use `--pylab` instead, or `--pylab=foo` to specify a backend.")
249 sub = '--pylab'
260 sub = '--pylab'
250 if len(argv) > idx+1:
261 if len(argv) > idx+1:
251 # check for gui arg, as in '-pylab qt'
262 # check for gui arg, as in '-pylab qt'
252 gui = argv[idx+1]
263 gui = argv[idx+1]
253 if gui in ('wx', 'qt', 'qt4', 'gtk', 'auto'):
264 if gui in ('wx', 'qt', 'qt4', 'gtk', 'auto'):
254 sub = '--pylab='+gui
265 sub = '--pylab='+gui
255 argv.pop(idx+1)
266 argv.pop(idx+1)
256 argv[idx] = sub
267 argv[idx] = sub
257
268
258 return super(TerminalIPythonApp, self).parse_command_line(argv)
269 return super(TerminalIPythonApp, self).parse_command_line(argv)
259
270
260 def initialize(self, argv=None):
271 def initialize(self, argv=None):
261 """Do actions after construct, but before starting the app."""
272 """Do actions after construct, but before starting the app."""
262 super(TerminalIPythonApp, self).initialize(argv)
273 super(TerminalIPythonApp, self).initialize(argv)
263 if self.subapp is not None:
274 if self.subapp is not None:
264 # don't bother initializing further, starting subapp
275 # don't bother initializing further, starting subapp
265 return
276 return
266 if not self.ignore_old_config:
277 if not self.ignore_old_config:
267 check_for_old_config(self.ipython_dir)
278 check_for_old_config(self.ipython_dir)
268 # print self.extra_args
279 # print self.extra_args
269 if self.extra_args:
280 if self.extra_args:
270 self.file_to_run = self.extra_args[0]
281 self.file_to_run = self.extra_args[0]
271 # create the shell
282 # create the shell
272 self.init_shell()
283 self.init_shell()
273 # and draw the banner
284 # and draw the banner
274 self.init_banner()
285 self.init_banner()
275 # Now a variety of things that happen after the banner is printed.
286 # Now a variety of things that happen after the banner is printed.
276 self.init_gui_pylab()
287 self.init_gui_pylab()
277 self.init_extensions()
288 self.init_extensions()
278 self.init_code()
289 self.init_code()
279
290
280 def init_shell(self):
291 def init_shell(self):
281 """initialize the InteractiveShell instance"""
292 """initialize the InteractiveShell instance"""
282 # I am a little hesitant to put these into InteractiveShell itself.
293 # I am a little hesitant to put these into InteractiveShell itself.
283 # But that might be the place for them
294 # But that might be the place for them
284 sys.path.insert(0, '')
295 sys.path.insert(0, '')
285
296
286 # Create an InteractiveShell instance.
297 # Create an InteractiveShell instance.
287 # shell.display_banner should always be False for the terminal
298 # shell.display_banner should always be False for the terminal
288 # based app, because we call shell.show_banner() by hand below
299 # based app, because we call shell.show_banner() by hand below
289 # so the banner shows *before* all extension loading stuff.
300 # so the banner shows *before* all extension loading stuff.
290 self.shell = TerminalInteractiveShell.instance(config=self.config,
301 self.shell = TerminalInteractiveShell.instance(config=self.config,
291 display_banner=False, profile_dir=self.profile_dir,
302 display_banner=False, profile_dir=self.profile_dir,
292 ipython_dir=self.ipython_dir)
303 ipython_dir=self.ipython_dir)
293
304
294 def init_banner(self):
305 def init_banner(self):
295 """optionally display the banner"""
306 """optionally display the banner"""
296 if self.display_banner and self.interact:
307 if self.display_banner and self.interact:
297 self.shell.show_banner()
308 self.shell.show_banner()
298 # Make sure there is a space below the banner.
309 # Make sure there is a space below the banner.
299 if self.log_level <= logging.INFO: print
310 if self.log_level <= logging.INFO: print
300
311
301
312
302 def init_gui_pylab(self):
313 def init_gui_pylab(self):
303 """Enable GUI event loop integration, taking pylab into account."""
314 """Enable GUI event loop integration, taking pylab into account."""
304 gui = self.gui
315 gui = self.gui
305
316
306 # Using `pylab` will also require gui activation, though which toolkit
317 # Using `pylab` will also require gui activation, though which toolkit
307 # to use may be chosen automatically based on mpl configuration.
318 # to use may be chosen automatically based on mpl configuration.
308 if self.pylab:
319 if self.pylab:
309 activate = self.shell.enable_pylab
320 activate = self.shell.enable_pylab
310 if self.pylab == 'auto':
321 if self.pylab == 'auto':
311 gui = None
322 gui = None
312 else:
323 else:
313 gui = self.pylab
324 gui = self.pylab
314 else:
325 else:
315 # Enable only GUI integration, no pylab
326 # Enable only GUI integration, no pylab
316 activate = inputhook.enable_gui
327 activate = inputhook.enable_gui
317
328
318 if gui or self.pylab:
329 if gui or self.pylab:
319 try:
330 try:
320 self.log.info("Enabling GUI event loop integration, "
331 self.log.info("Enabling GUI event loop integration, "
321 "toolkit=%s, pylab=%s" % (gui, self.pylab) )
332 "toolkit=%s, pylab=%s" % (gui, self.pylab) )
322 activate(gui)
333 activate(gui)
323 except:
334 except:
324 self.log.warn("Error in enabling GUI event loop integration:")
335 self.log.warn("Error in enabling GUI event loop integration:")
325 self.shell.showtraceback()
336 self.shell.showtraceback()
326
337
327 def start(self):
338 def start(self):
328 if self.subapp is not None:
339 if self.subapp is not None:
329 return self.subapp.start()
340 return self.subapp.start()
330 # perform any prexec steps:
341 # perform any prexec steps:
331 if self.interact:
342 if self.interact:
332 self.log.debug("Starting IPython's mainloop...")
343 self.log.debug("Starting IPython's mainloop...")
333 self.shell.mainloop()
344 self.shell.mainloop()
334 else:
345 else:
335 self.log.debug("IPython not interactive...")
346 self.log.debug("IPython not interactive...")
336
347
337
348
338 def load_default_config(ipython_dir=None):
349 def load_default_config(ipython_dir=None):
339 """Load the default config file from the default ipython_dir.
350 """Load the default config file from the default ipython_dir.
340
351
341 This is useful for embedded shells.
352 This is useful for embedded shells.
342 """
353 """
343 if ipython_dir is None:
354 if ipython_dir is None:
344 ipython_dir = get_ipython_dir()
355 ipython_dir = get_ipython_dir()
345 profile_dir = os.path.join(ipython_dir, 'profile_default')
356 profile_dir = os.path.join(ipython_dir, 'profile_default')
346 cl = PyFileConfigLoader(default_config_file_name, profile_dir)
357 cl = PyFileConfigLoader(default_config_file_name, profile_dir)
347 try:
358 try:
348 config = cl.load_config()
359 config = cl.load_config()
349 except IOError:
360 except IOError:
350 # no config found
361 # no config found
351 config = Config()
362 config = Config()
352 return config
363 return config
353
364
354
365
355 def launch_new_instance():
366 def launch_new_instance():
356 """Create and run a full blown IPython instance"""
367 """Create and run a full blown IPython instance"""
357 app = TerminalIPythonApp.instance()
368 app = TerminalIPythonApp.instance()
358 app.initialize()
369 app.initialize()
359 app.start()
370 app.start()
360
371
361
372
362 if __name__ == '__main__':
373 if __name__ == '__main__':
363 launch_new_instance()
374 launch_new_instance()
364
375
General Comments 0
You need to be logged in to leave comments. Login now