diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 51e61ed..e660c16 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -315,10 +315,41 @@ class InteractiveShell(SingletonConfigurable, Magic): help="Save multi-line entries as one entry in readline history" ) - prompt_in1 = Unicode('In [\\#]: ', config=True) - prompt_in2 = Unicode(' .\\D.: ', config=True) - prompt_out = Unicode('Out[\\#]: ', config=True) - prompts_pad_left = CBool(True, config=True) + # deprecated prompt traits: + + prompt_in1 = Unicode('In [\\#]: ', config=True, + help="Deprecated, use PromptManager.in_template") + prompt_in2 = Unicode(' .\\D.: ', config=True, + help="Deprecated, use PromptManager.in2_template") + prompt_out = Unicode('Out[\\#]: ', config=True, + help="Deprecated, use PromptManager.out_template") + prompts_pad_left = CBool(True, config=True, + help="Deprecated, use PromptManager.justify") + + def _prompt_trait_changed(self, name, old, new): + table = { + 'prompt_in1' : 'in_template', + 'prompt_in2' : 'in2_template', + 'prompt_out' : 'out_template', + 'prompts_pad_left' : 'justify', + } + warn("InteractiveShell.{name} is deprecated, use PromptManager.{newname}\n".format( + name=name, newname=table[name]) + ) + # protect against weird cases where self.config may not exist: + if self.config is not None: + # propagate to corresponding PromptManager trait + setattr(self.config.PromptManager, table[name], new) + + _prompt_in1_changed = _prompt_trait_changed + _prompt_in2_changed = _prompt_trait_changed + _prompt_out_changed = _prompt_trait_changed + _prompt_pad_left_changed = _prompt_trait_changed + + show_rewritten_input = CBool(True, config=True, + help="Show rewritten input, e.g. for autocall." + ) + quiet = CBool(False, config=True) history_length = Integer(10000, config=True) @@ -2141,6 +2172,9 @@ class InteractiveShell(SingletonConfigurable, Magic): after the user's input prompt. This helps the user understand that the input line was transformed automatically by IPython. """ + if not self.show_rewritten_input: + return + rw = self.prompt_manager.render('rewrite') + cmd try: diff --git a/IPython/core/prompts.py b/IPython/core/prompts.py index d1572bf..4413ddf 100644 --- a/IPython/core/prompts.py +++ b/IPython/core/prompts.py @@ -254,10 +254,12 @@ class PromptManager(Configurable): """) def _lazy_evaluate_fields_default(self): return lazily_evaluate.copy() - in_template = Unicode('In [\\#]: ', config=True) - in2_template = Unicode(' .\\D.: ', config=True) - out_template = Unicode('Out[\\#]: ', config=True) - rewrite_template = Unicode("------> ", config=True) + in_template = Unicode('In [\\#]: ', config=True, + help="Input prompt. '\\#' will be transformed to the prompt number") + in2_template = Unicode(' .\\D.: ', config=True, + help="Continuation prompt.") + out_template = Unicode('Out[\\#]: ', config=True, + help="Output prompt. '\\#' will be transformed to the prompt number") justify = Bool(True, config=True, help=""" If True (default), each prompt will be right-aligned with the @@ -270,11 +272,12 @@ class PromptManager(Configurable): # The number of characters in the last prompt rendered, not including # colour characters. width = Int() + txtwidth = Int() # Not including right-justification # The number of characters in each prompt which don't contribute to width invisible_chars = Dict() def _invisible_chars_default(self): - return {'in': 0, 'in2': 0, 'out': 0, 'rewrite': 0} + return {'in': 0, 'in2': 0, 'out': 0, 'rewrite':0} def __init__(self, shell, config=None): super(PromptManager, self).__init__(shell=shell, config=config) @@ -283,13 +286,13 @@ class PromptManager(Configurable): self.color_scheme_table = coloransi.ColorSchemeTable([PColNoColors, PColLinux, PColLightBG], self.color_scheme) - # Prepare templates + # Prepare templates & numbers of invisible characters self.update_prompt('in', self.in_template) self.update_prompt('in2', self.in2_template) self.update_prompt('out', self.out_template) - self.update_prompt('rewrite', self.rewrite_template) + self.update_prompt('rewrite') self.on_trait_change(self._update_prompt_trait, ['in_template', - 'in2_template', 'out_template', 'rewrite_template']) + 'in2_template', 'out_template']) def update_prompt(self, name, new_template=None): """This is called when a prompt template is updated. It processes @@ -302,48 +305,26 @@ class PromptManager(Configurable): """ if new_template is not None: self.templates[name] = multiple_replace(prompt_abbreviations, new_template) - invis_chars = len(self.render(name, color=True, just=False)) - \ - len(self.render(name, color=False, just=False)) + invis_chars = len(self._render(name, color=True)) - \ + len(self._render(name, color=False)) self.invisible_chars[name] = invis_chars def _update_prompt_trait(self, traitname, new_template): name = traitname[:-9] # Cut off '_template' self.update_prompt(name, new_template) - def render(self, name, color=True, just=None, **kwargs): + def _render(self, name, color=True, **kwargs): + """Render but don't justify, or update the width or txtwidth attributes. """ - Render the selected prompt. + if name == 'rewrite': + return self._render_rewrite(color=color) - Parameters - ---------- - name : str - Which prompt to render. One of 'in', 'in2', 'out', 'rewrite' - color : bool - If True (default), include ANSI escape sequences for a coloured prompt. - just : bool - If True, justify the prompt to the width of the last prompt. The - default is stored in self.justify. - **kwargs : - Additional arguments will be passed to the string formatting operation, - so they can override the values that would otherwise fill in the - template. - - Returns - ------- - A string containing the rendered prompt. - """ if color: scheme = self.color_scheme_table.active_colors if name=='out': colors = color_lists['normal'] colors.number, colors.prompt, colors.normal = \ scheme.out_number, scheme.out_prompt, scheme.normal - elif name=='rewrite': - colors = color_lists['normal'] - # We need a non-input version of these escapes - colors.number = scheme.in_number.replace("\001","").replace("\002","") - colors.prompt = scheme.in_prompt.replace("\001","").replace("\002","") - colors.normal = scheme.normal else: colors = color_lists['inp'] colors.number, colors.prompt, colors.normal = \ @@ -358,7 +339,8 @@ class PromptManager(Configurable): count = self.shell.execution_count # Shorthand # Build the dictionary to be passed to string formatting fmtargs = dict(color=colors, count=count, - dots="."*len(str(count)) ) + dots="."*len(str(count)), + width=self.width, txtwidth=self.txtwidth ) fmtargs.update(self.lazy_evaluate_fields) fmtargs.update(kwargs) @@ -366,13 +348,49 @@ class PromptManager(Configurable): prompt = colors.prompt + self.templates[name] + colors.normal # Fill in required fields - res = prompt.format(**fmtargs) + return prompt.format(**fmtargs) + + def _render_rewrite(self, color=True): + """Render the ---> rewrite prompt.""" + if color: + scheme = self.color_scheme_table.active_colors + # We need a non-input version of these escapes + color_prompt = scheme.in_prompt.replace("\001","").replace("\002","") + color_normal = scheme.normal + else: + color_prompt, color_normal = '', '' + + return color_prompt + "-> ".rjust(self.txtwidth, "-") + color_normal + + def render(self, name, color=True, just=None, **kwargs): + """ + Render the selected prompt. + + Parameters + ---------- + name : str + Which prompt to render. One of 'in', 'in2', 'out', 'rewrite' + color : bool + If True (default), include ANSI escape sequences for a coloured prompt. + just : bool + If True, justify the prompt to the width of the last prompt. The + default is stored in self.justify. + **kwargs : + Additional arguments will be passed to the string formatting operation, + so they can override the values that would otherwise fill in the + template. + + Returns + ------- + A string containing the rendered prompt. + """ + res = self._render(name, color=color, **kwargs) # Handle justification of prompt invis_chars = self.invisible_chars[name] if color else 0 + self.txtwidth = len(res) - invis_chars just = self.justify if (just is None) else just if just: res = res.rjust(self.width + invis_chars) self.width = len(res) - invis_chars return res - diff --git a/IPython/frontend/terminal/ipapp.py b/IPython/frontend/terminal/ipapp.py index 3d9ced3..1ab9435 100755 --- a/IPython/frontend/terminal/ipapp.py +++ b/IPython/frontend/terminal/ipapp.py @@ -38,6 +38,7 @@ from IPython.core import usage from IPython.core.completer import IPCompleter from IPython.core.crashhandler import CrashHandler from IPython.core.formatters import PlainTextFormatter +from IPython.core.prompts import PromptManager from IPython.core.application import ( ProfileDir, BaseIPythonApplication, base_flags, base_aliases ) @@ -133,9 +134,9 @@ addflag('term-title', 'TerminalInteractiveShell.term_title', classic_config = Config() classic_config.InteractiveShell.cache_size = 0 classic_config.PlainTextFormatter.pprint = False -classic_config.InteractiveShell.prompt_in1 = '>>> ' -classic_config.InteractiveShell.prompt_in2 = '... ' -classic_config.InteractiveShell.prompt_out = '' +classic_config.PromptManager.in_template = '>>> ' +classic_config.PromptManager.in2_template = '... ' +classic_config.PromptManager.out_template = '' classic_config.InteractiveShell.separate_in = '' classic_config.InteractiveShell.separate_out = '' classic_config.InteractiveShell.separate_out2 = '' @@ -197,6 +198,7 @@ class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp): InteractiveShellApp, # ShellApp comes before TerminalApp, because self.__class__, # it will also affect subclasses (e.g. QtConsole) TerminalInteractiveShell, + PromptManager, ProfileDir, PlainTextFormatter, IPCompleter, diff --git a/IPython/testing/tools.py b/IPython/testing/tools.py index d67c441..6a289cb 100644 --- a/IPython/testing/tools.py +++ b/IPython/testing/tools.py @@ -202,9 +202,9 @@ def ipexec(fname, options=None): # For these subprocess calls, eliminate all prompt printing so we only see # output from script execution - prompt_opts = [ '--InteractiveShell.prompt_in1=""', - '--InteractiveShell.prompt_in2=""', - '--InteractiveShell.prompt_out=""' + prompt_opts = [ '--PromptManager.in_template=""', + '--PromptManager.in2_template=""', + '--PromptManager.out_template=""' ] cmdargs = ' '.join(default_argv() + prompt_opts + options) diff --git a/docs/examples/core/example-embed.py b/docs/examples/core/example-embed.py index a384e5b..9a0fe22 100755 --- a/docs/examples/core/example-embed.py +++ b/docs/examples/core/example-embed.py @@ -23,10 +23,10 @@ try: except NameError: nested = 0 cfg = Config() - shell_config = cfg.InteractiveShellEmbed - shell_config.prompt_in1 = 'In <\\#>: ' - shell_config.prompt_in2 = ' .\\D.: ' - shell_config.prompt_out = 'Out<\\#>: ' + prompt_config = cfg.PromptManager + prompt_config.in_template = 'In <\\#>: ' + prompt_config.in2_template = ' .\\D.: ' + prompt_config.out_template = 'Out<\\#>: ' else: print "Running nested copies of IPython." print "The prompts for the nested copy have been modified" @@ -46,12 +46,12 @@ ipshell = InteractiveShellEmbed(config=cfg, # Make a second instance, you can have as many as you want. cfg2 = cfg.copy() -shell_config = cfg2.InteractiveShellEmbed -shell_config.prompt_in1 = 'In2<\\#>: ' +prompt_config = cfg2.PromptManager +prompt_config.in_template = 'In2<\\#>: ' if not nested: - shell_config.prompt_in1 = 'In2<\\#>: ' - shell_config.prompt_in2 = ' .\\D.: ' - shell_config.prompt_out = 'Out<\\#>: ' + prompt_config.in_template = 'In2<\\#>: ' + prompt_config.in2_template = ' .\\D.: ' + prompt_config.out_template = 'Out<\\#>: ' ipshell2 = InteractiveShellEmbed(config=cfg, banner1 = 'Second IPython instance.') diff --git a/docs/source/interactive/reference.txt b/docs/source/interactive/reference.txt index 59c4fec..4719370 100644 --- a/docs/source/interactive/reference.txt +++ b/docs/source/interactive/reference.txt @@ -227,7 +227,7 @@ All options with a [no] prepended can be specified in negated form circular file inclusions, IPython will stop if it reaches 15 recursive inclusions. - ``InteractiveShell.prompt_in1=`` + ``PromptManager.in_template=`` Specify the string used for input prompts. Note that if you are using numbered prompts, the number is represented with a '\#' in the @@ -236,7 +236,7 @@ All options with a [no] prepended can be specified in negated form discusses in detail all the available escapes to customize your prompts. - ``InteractiveShell.prompt_in2=`` + ``PromptManager.in2_template=`` Similar to the previous option, but used for the continuation prompts. The special sequence '\D' is similar to '\#', but with all digits replaced dots (so you can have your @@ -244,9 +244,9 @@ All options with a [no] prepended can be specified in negated form ' .\D.:' (note three spaces at the start for alignment with 'In [\#]'). - ``InteractiveShell.prompt_out=`` + ``PromptManager.out_template=`` String used for output prompts, also uses numbers like - prompt_in1. Default: 'Out[\#]:' + in_template. Default: 'Out[\#]:' ``--quick`` start in bare bones mode (no config file loaded). diff --git a/docs/source/interactive/shell.txt b/docs/source/interactive/shell.txt index 46d3bee..306db9a 100644 --- a/docs/source/interactive/shell.txt +++ b/docs/source/interactive/shell.txt @@ -155,8 +155,9 @@ Prompt customization The sh profile uses the following prompt configurations:: - o.prompt_in1= r'\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#>' - o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green>' + c.PromptManager.in_template = r'{color.LightGreen}\u@\h{color.LightBlue}[{color.LightCyan}\Y1{color.LightBlue}]{color.Green}|\#> ' + c.PromptManager.in2_template = r'{color.Green}|{color.LightGreen}\D{color.Green}> ' + c.PromptManager.out_template = r'<\#> ' You can change the prompt configuration to your liking by editing ipython_config.py.