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