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