ipapp.py
380 lines
| 13.9 KiB
| text/x-python
|
PythonLexer
|
r2202 | #!/usr/bin/env python | ||
# encoding: utf-8 | ||||
""" | ||||
|
r4023 | The :class:`~IPython.core.application.Application` object for the command | ||
|
r2301 | line :command:`ipython` program. | ||
|
r2202 | """ | ||
|
r20890 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
|
r2506 | |||
|
r2202 | |||
|
r2252 | import logging | ||
|
r2203 | import os | ||
import sys | ||||
|
r22340 | import warnings | ||
|
r2203 | |||
|
r21253 | from traitlets.config.loader import Config | ||
from traitlets.config.application import boolean_flag, catch_config_error, Application | ||||
|
r2506 | from IPython.core import release | ||
|
r3963 | from IPython.core import usage | ||
|
r5231 | from IPython.core.completer import IPCompleter | ||
|
r2506 | from IPython.core.crashhandler import CrashHandler | ||
|
r3963 | from IPython.core.formatters import PlainTextFormatter | ||
|
r6823 | from IPython.core.history import HistoryManager | ||
|
r4023 | from IPython.core.application import ( | ||
|
r3963 | ProfileDir, BaseIPythonApplication, base_flags, base_aliases | ||
|
r2245 | ) | ||
|
r23624 | from IPython.core.magics import ( | ||
ScriptMagics, LoggingMagics | ||||
) | ||||
|
r3968 | from IPython.core.shellapp import ( | ||
InteractiveShellApp, shell_flags, shell_aliases | ||||
) | ||||
|
r12331 | from IPython.extensions.storemagic import StoreMagics | ||
|
r22549 | from .interactiveshell import TerminalInteractiveShell | ||
|
r21253 | from IPython.paths import get_ipython_dir | ||
from traitlets import ( | ||||
|
r23505 | Bool, List, Dict, default, observe, Type | ||
|
r3963 | ) | ||
|
r2203 | |||
#----------------------------------------------------------------------------- | ||||
|
r2427 | # Globals, utilities and helpers | ||
|
r2203 | #----------------------------------------------------------------------------- | ||
|
r4216 | _examples = """ | ||
|
r12015 | ipython --matplotlib # enable matplotlib integration | ||
|
r12154 | ipython --matplotlib=qt # enable matplotlib integration with qt4 backend | ||
|
r12015 | |||
|
r4219 | ipython --log-level=DEBUG # set logging to DEBUG | ||
|
r4216 | ipython --profile=foo # start with profile foo | ||
|
r4218 | |||
|
r6169 | ipython profile create foo # create profile foo w/ default config files | ||
ipython help profile # show the help for the profile subcmd | ||||
|
r6901 | |||
ipython locate # print the path to the IPython directory | ||||
ipython locate profile foo # print the path to the directory for profile `foo` | ||||
|
r4216 | """ | ||
|
r2501 | |||
|
r2427 | #----------------------------------------------------------------------------- | ||
|
r2506 | # Crash handler for this application | ||
#----------------------------------------------------------------------------- | ||||
class IPAppCrashHandler(CrashHandler): | ||||
"""sys.excepthook for IPython itself, leaves a detailed report on disk.""" | ||||
def __init__(self, app): | ||||
|
r9188 | contact_name = release.author | ||
|
r5316 | contact_email = release.author_email | ||
bug_tracker = 'https://github.com/ipython/ipython/issues' | ||||
|
r2506 | super(IPAppCrashHandler,self).__init__( | ||
app, contact_name, contact_email, bug_tracker | ||||
) | ||||
def make_report(self,traceback): | ||||
"""Return a string containing a crash report.""" | ||||
sec_sep = self.section_sep | ||||
# Start with parent report | ||||
report = [super(IPAppCrashHandler, self).make_report(traceback)] | ||||
# Add interactive-specific info we may have | ||||
rpt_add = report.append | ||||
try: | ||||
rpt_add(sec_sep+"History of session input:") | ||||
for line in self.app.shell.user_ns['_ih']: | ||||
rpt_add(line) | ||||
rpt_add('\n*** Last line of input (may not be in above history):\n') | ||||
rpt_add(self.app.shell._last_input_line+'\n') | ||||
except: | ||||
pass | ||||
return ''.join(report) | ||||
|
r3963 | #----------------------------------------------------------------------------- | ||
# Aliases and Flags | ||||
#----------------------------------------------------------------------------- | ||||
flags = dict(base_flags) | ||||
|
r3968 | flags.update(shell_flags) | ||
|
r5610 | frontend_flags = {} | ||
addflag = lambda *args: frontend_flags.update(boolean_flag(*args)) | ||||
|
r3963 | addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax', | ||
'Turn on auto editing of files with syntax errors.', | ||||
'Turn off auto editing of files with syntax errors.' | ||||
) | ||||
|
r22416 | addflag('simple-prompt', 'TerminalInteractiveShell.simple_prompt', | ||
"Force simple minimal prompt using `raw_input`", | ||||
"Use a rich interactive prompt with prompt_toolkit", | ||||
) | ||||
|
r3968 | addflag('banner', 'TerminalIPythonApp.display_banner', | ||
|
r3963 | "Display a banner upon starting IPython.", | ||
"Don't display a banner upon starting IPython." | ||||
) | ||||
addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit', | ||||
"""Set to confirm when you try to exit IPython with an EOF (Control-D | ||||
|
r3967 | in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit', | ||
you can force a direct exit without any confirmation.""", | ||||
|
r3963 | "Don't prompt the user when exiting." | ||
) | ||||
addflag('term-title', 'TerminalInteractiveShell.term_title', | ||||
"Enable auto setting the terminal title.", | ||||
"Disable auto setting the terminal title." | ||||
) | ||||
classic_config = Config() | ||||
classic_config.InteractiveShell.cache_size = 0 | ||||
classic_config.PlainTextFormatter.pprint = False | ||||
|
r22447 | classic_config.TerminalInteractiveShell.prompts_class='IPython.terminal.prompts.ClassicPrompts' | ||
|
r3963 | classic_config.InteractiveShell.separate_in = '' | ||
classic_config.InteractiveShell.separate_out = '' | ||||
classic_config.InteractiveShell.separate_out2 = '' | ||||
classic_config.InteractiveShell.colors = 'NoColor' | ||||
classic_config.InteractiveShell.xmode = 'Plain' | ||||
|
r5610 | frontend_flags['classic']=( | ||
|
r3963 | classic_config, | ||
"Gives IPython a similar feel to the classic Python prompt." | ||||
) | ||||
# # log doesn't make so much sense this way anymore | ||||
# paa('--log','-l', | ||||
# action='store_true', dest='InteractiveShell.logstart', | ||||
# help="Start logging to the default log file (./ipython_log.py).") | ||||
# | ||||
# # quick is harder to implement | ||||
|
r5610 | frontend_flags['quick']=( | ||
|
r3968 | {'TerminalIPythonApp' : {'quick' : True}}, | ||
|
r3963 | "Enable quick startup with no config files." | ||
) | ||||
|
r5610 | frontend_flags['i'] = ( | ||
|
r3968 | {'TerminalIPythonApp' : {'force_interact' : True}}, | ||
|
r21556 | """If running code from the command line, become interactive afterwards. | ||
It is often useful to follow this with `--` to treat remaining flags as | ||||
script arguments. | ||||
""" | ||||
|
r3963 | ) | ||
|
r5610 | flags.update(frontend_flags) | ||
|
r3963 | |||
aliases = dict(base_aliases) | ||||
|
r3968 | aliases.update(shell_aliases) | ||
|
r3963 | |||
|
r2506 | #----------------------------------------------------------------------------- | ||
|
r2427 | # Main classes and functions | ||
#----------------------------------------------------------------------------- | ||||
|
r2245 | |||
|
r6901 | |||
class LocateIPythonApp(BaseIPythonApplication): | ||||
description = """print the path to the IPython dir""" | ||||
subcommands = Dict(dict( | ||||
profile=('IPython.core.profileapp.ProfileLocate', | ||||
"print the path to an IPython profile directory", | ||||
), | ||||
)) | ||||
def start(self): | ||||
if self.subapp is not None: | ||||
return self.subapp.start() | ||||
else: | ||||
|
r13348 | print(self.ipython_dir) | ||
|
r6901 | |||
|
r3968 | class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp): | ||
|
r2328 | name = u'ipython' | ||
|
r3963 | description = usage.cl_usage | ||
|
r2506 | crash_handler_class = IPAppCrashHandler | ||
|
r4216 | examples = _examples | ||
|
r4215 | |||
|
r3963 | flags = Dict(flags) | ||
aliases = Dict(aliases) | ||||
|
r4462 | classes = List() | ||
|
r23505 | |||
interactive_shell_class = Type( | ||||
klass=object, # use default_value otherwise which only allow subclasses. | ||||
default_value=TerminalInteractiveShell, | ||||
help="Class to use to instantiate the TerminalInteractiveShell object. Useful for custom Frontends" | ||||
).tag(config=True) | ||||
|
r22340 | @default('classes') | ||
|
r4462 | def _classes_default(self): | ||
"""This has to be in a method, for TerminalIPythonApp to be available.""" | ||||
return [ | ||||
InteractiveShellApp, # ShellApp comes before TerminalApp, because | ||||
self.__class__, # it will also affect subclasses (e.g. QtConsole) | ||||
TerminalInteractiveShell, | ||||
|
r6823 | HistoryManager, | ||
|
r4462 | ProfileDir, | ||
PlainTextFormatter, | ||||
|
r5231 | IPCompleter, | ||
|
r7417 | ScriptMagics, | ||
|
r23624 | LoggingMagics, | ||
|
r12331 | StoreMagics, | ||
|
r4462 | ] | ||
|
r4872 | |||
|
r21836 | deprecated_subcommands = dict( | ||
|
r21433 | qtconsole=('qtconsole.qtconsoleapp.JupyterQtConsoleApp', | ||
|
r21889 | """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter Qt Console.""" | ||
|
r4024 | ), | ||
|
r21360 | notebook=('notebook.notebookapp.NotebookApp', | ||
|
r21889 | """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter HTML Notebook Server.""" | ||
|
r4344 | ), | ||
|
r21253 | console=('jupyter_console.app.ZMQTerminalIPythonApp', | ||
|
r21889 | """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter terminal-based Console.""" | ||
|
r5600 | ), | ||
|
r21339 | nbconvert=('nbconvert.nbconvertapp.NbConvertApp', | ||
|
r21889 | "DEPRECATED, Will be removed in IPython 6.0 : Convert notebooks to/from other formats." | ||
|
r11091 | ), | ||
|
r21345 | trust=('nbformat.sign.TrustNotebookApp', | ||
|
r21889 | "DEPRECATED, Will be removed in IPython 6.0 : Sign notebooks to trust their potentially unsafe contents at load." | ||
|
r14861 | ), | ||
|
r21253 | kernelspec=('jupyter_client.kernelspecapp.KernelSpecApp', | ||
|
r21889 | "DEPRECATED, Will be removed in IPython 6.0 : Manage Jupyter kernel specifications." | ||
|
r16560 | ), | ||
|
r15224 | ) | ||
|
r21836 | subcommands = dict( | ||
profile = ("IPython.core.profileapp.ProfileApp", | ||||
"Create and manage IPython profiles." | ||||
), | ||||
kernel = ("ipykernel.kernelapp.IPKernelApp", | ||||
"Start a kernel without an attached frontend." | ||||
), | ||||
locate=('IPython.terminal.ipapp.LocateIPythonApp', | ||||
LocateIPythonApp.description | ||||
), | ||||
history=('IPython.core.historyapp.HistoryApp', | ||||
"Manage the IPython history database." | ||||
), | ||||
) | ||||
deprecated_subcommands['install-nbextension'] = ( | ||||
|
r21502 | "notebook.nbextensions.InstallNBExtensionApp", | ||
|
r21889 | "DEPRECATED, Will be removed in IPython 6.0 : Install Jupyter notebook extension files" | ||
|
r15221 | ) | ||
|
r21836 | subcommands.update(deprecated_subcommands) | ||
|
r4872 | |||
|
r4025 | # *do* autocreate requested profile, but don't create the config file. | ||
|
r3963 | auto_create=Bool(True) | ||
# configurables | ||||
|
r22340 | quick = Bool(False, | ||
|
r3963 | help="""Start IPython quickly by skipping the loading of config files.""" | ||
|
r22340 | ).tag(config=True) | ||
@observe('quick') | ||||
def _quick_changed(self, change): | ||||
if change['new']: | ||||
|
r3963 | self.load_config_file = lambda *a, **kw: None | ||
|
r22340 | display_banner = Bool(True, | ||
|
r3963 | help="Whether to display a banner upon starting IPython." | ||
|
r22340 | ).tag(config=True) | ||
|
r3963 | |||
# if there is code of files to run from the cmd line, don't interact | ||||
# unless the --i flag (App.force_interact) is true. | ||||
|
r22340 | force_interact = Bool(False, | ||
|
r3963 | help="""If a command or file is given via the command-line, | ||
|
r13929 | e.g. 'ipython foo.py', start an interactive shell after executing the | ||
file or command.""" | ||||
|
r22340 | ).tag(config=True) | ||
@observe('force_interact') | ||||
def _force_interact_changed(self, change): | ||||
if change['new']: | ||||
|
r3963 | self.interact = True | ||
|
r4872 | |||
|
r22340 | @observe('file_to_run', 'code_to_run', 'module_to_run') | ||
def _file_to_run_changed(self, change): | ||||
new = change['new'] | ||||
|
r6081 | if new: | ||
self.something_to_run = True | ||||
|
r3963 | if new and not self.force_interact: | ||
self.interact = False | ||||
# internal, not-configurable | ||||
|
r6081 | something_to_run=Bool(False) | ||
|
r3963 | |||
|
r4104 | def parse_command_line(self, argv=None): | ||
"""override to allow old '-pylab' flag with deprecation warning""" | ||||
|
r4245 | |||
|
r4104 | argv = sys.argv[1:] if argv is None else argv | ||
|
r4245 | |||
if '-pylab' in argv: | ||||
|
r4104 | # deprecated `-pylab` given, | ||
# warn and transform into current syntax | ||||
|
r4245 | argv = argv[:] # copy, don't clobber | ||
idx = argv.index('-pylab') | ||||
|
r22340 | warnings.warn("`-pylab` flag has been deprecated.\n" | ||
|
r11798 | " Use `--matplotlib <backend>` and import pylab manually.") | ||
argv[idx] = '--pylab' | ||||
|
r4872 | |||
|
r4104 | return super(TerminalIPythonApp, self).parse_command_line(argv) | ||
|
r5172 | |||
|
r5214 | @catch_config_error | ||
|
r3963 | def initialize(self, argv=None): | ||
"""Do actions after construct, but before starting the app.""" | ||||
|
r3968 | super(TerminalIPythonApp, self).initialize(argv) | ||
|
r3982 | if self.subapp is not None: | ||
# don't bother initializing further, starting subapp | ||||
return | ||||
|
r3963 | # print self.extra_args | ||
|
r6081 | if self.extra_args and not self.something_to_run: | ||
|
r3963 | self.file_to_run = self.extra_args[0] | ||
|
r6695 | self.init_path() | ||
|
r3963 | # create the shell | ||
self.init_shell() | ||||
# and draw the banner | ||||
self.init_banner() | ||||
# Now a variety of things that happen after the banner is printed. | ||||
self.init_gui_pylab() | ||||
self.init_extensions() | ||||
self.init_code() | ||||
def init_shell(self): | ||||
"""initialize the InteractiveShell instance""" | ||||
|
r2731 | # Create an InteractiveShell instance. | ||
|
r4872 | # shell.display_banner should always be False for the terminal | ||
|
r2252 | # based app, because we call shell.show_banner() by hand below | ||
# so the banner shows *before* all extension loading stuff. | ||||
|
r23505 | self.shell = self.interactive_shell_class.instance(parent=self, | ||
|
r22168 | profile_dir=self.profile_dir, | ||
|
r12159 | ipython_dir=self.ipython_dir, user_ns=self.user_ns) | ||
|
r5315 | self.shell.configurables.append(self) | ||
|
r2252 | |||
|
r3963 | def init_banner(self): | ||
"""optionally display the banner""" | ||||
if self.display_banner and self.interact: | ||||
self.shell.show_banner() | ||||
|
r2252 | # Make sure there is a space below the banner. | ||
|
r13348 | if self.log_level <= logging.INFO: print() | ||
|
r2252 | |||
|
r7096 | def _pylab_changed(self, name, old, new): | ||
"""Replace --pylab='inline' with --pylab='auto'""" | ||||
if new == 'inline': | ||||
|
r22340 | warnings.warn("'inline' not available as pylab backend, " | ||
|
r8223 | "using 'auto' instead.") | ||
|
r7096 | self.pylab = 'auto' | ||
|
r2252 | |||
|
r3963 | def start(self): | ||
|
r3982 | if self.subapp is not None: | ||
return self.subapp.start() | ||||
|
r3963 | # perform any prexec steps: | ||
if self.interact: | ||||
|
r2253 | self.log.debug("Starting IPython's mainloop...") | ||
self.shell.mainloop() | ||||
|
r2391 | else: | ||
|
r3963 | self.log.debug("IPython not interactive...") | ||
|
r2202 | |||
|
r2322 | def load_default_config(ipython_dir=None): | ||
"""Load the default config file from the default ipython_dir. | ||||
|
r2245 | |||
This is useful for embedded shells. | ||||
""" | ||||
|
r2322 | if ipython_dir is None: | ||
ipython_dir = get_ipython_dir() | ||||
|
r13771 | |||
|
r3963 | profile_dir = os.path.join(ipython_dir, 'profile_default') | ||
|
r2245 | |||
|
r13771 | config = Config() | ||
|
r13787 | for cf in Application._load_config_files("ipython_config", path=profile_dir): | ||
|
r13771 | config.update(cf) | ||
return config | ||||
|
r2245 | |||
|
r11176 | launch_new_instance = TerminalIPythonApp.launch_instance | ||
|
r2501 | |||
|
r2507 | |||
if __name__ == '__main__': | ||||
launch_new_instance() | ||||