Show More
@@ -0,0 +1,73 b'' | |||||
|
1 | """Terminal input and output prompts.""" | |||
|
2 | from __future__ import print_function | |||
|
3 | ||||
|
4 | from pygments.token import Token | |||
|
5 | import sys | |||
|
6 | ||||
|
7 | from IPython.core.displayhook import DisplayHook | |||
|
8 | ||||
|
9 | class Prompts(object): | |||
|
10 | def __init__(self, shell): | |||
|
11 | self.shell = shell | |||
|
12 | ||||
|
13 | def in_prompt_tokens(self, cli=None): | |||
|
14 | return [ | |||
|
15 | (Token.Prompt, 'In ['), | |||
|
16 | (Token.PromptNum, str(self.shell.execution_count)), | |||
|
17 | (Token.Prompt, ']: '), | |||
|
18 | ] | |||
|
19 | ||||
|
20 | def _width(self): | |||
|
21 | in_tokens = self.in_prompt_tokens() | |||
|
22 | return sum(len(s) for (t, s) in in_tokens) | |||
|
23 | ||||
|
24 | def continuation_prompt_tokens(self, cli=None, width=None): | |||
|
25 | if width is None: | |||
|
26 | width = self._width() | |||
|
27 | return [ | |||
|
28 | (Token.Prompt, (' ' * (width - 5)) + '...: '), | |||
|
29 | ] | |||
|
30 | ||||
|
31 | def rewrite_prompt_tokens(self): | |||
|
32 | width = self._width() | |||
|
33 | return [ | |||
|
34 | (Token.Prompt, ('-' * (width - 2)) + '> '), | |||
|
35 | ] | |||
|
36 | ||||
|
37 | def out_prompt_tokens(self): | |||
|
38 | return [ | |||
|
39 | (Token.OutPrompt, 'Out['), | |||
|
40 | (Token.OutPromptNum, str(self.shell.execution_count)), | |||
|
41 | (Token.OutPrompt, ']: '), | |||
|
42 | ] | |||
|
43 | ||||
|
44 | class ClassicPrompts(Prompts): | |||
|
45 | def in_prompt_tokens(self, cli=None): | |||
|
46 | return [ | |||
|
47 | (Token.Prompt, '>>> '), | |||
|
48 | ] | |||
|
49 | ||||
|
50 | def continuation_prompt_tokens(self, cli=None, width=None): | |||
|
51 | return [ | |||
|
52 | (Token.Prompt, '... ') | |||
|
53 | ] | |||
|
54 | ||||
|
55 | def rewrite_prompt_tokens(self): | |||
|
56 | return [] | |||
|
57 | ||||
|
58 | def out_prompt_tokens(self): | |||
|
59 | return [] | |||
|
60 | ||||
|
61 | class RichPromptDisplayHook(DisplayHook): | |||
|
62 | """Subclass of base display hook using coloured prompt""" | |||
|
63 | def write_output_prompt(self): | |||
|
64 | sys.stdout.write(self.shell.separate_out) | |||
|
65 | self.prompt_end_newline = False | |||
|
66 | if self.do_full_cache: | |||
|
67 | tokens = self.shell.prompts.out_prompt_tokens() | |||
|
68 | if tokens and tokens[-1][1].endswith('\n'): | |||
|
69 | self.prompt_end_newline = True | |||
|
70 | if self.shell.pt_cli: | |||
|
71 | self.shell.pt_cli.print_tokens(tokens) | |||
|
72 | else: | |||
|
73 | print(*(s for t, s in tokens), sep='') |
@@ -0,0 +1,26 b'' | |||||
|
1 | """This is an example that shows how to create new prompts for IPython | |||
|
2 | """ | |||
|
3 | ||||
|
4 | from IPython.terminal.prompts import Prompts, Token | |||
|
5 | import os | |||
|
6 | ||||
|
7 | class MyPrompt(Prompts): | |||
|
8 | ||||
|
9 | def in_prompt_tokens(self, cli=None): | |||
|
10 | return [(Token, os.getcwd()), | |||
|
11 | (Token.Prompt, '>>>')] | |||
|
12 | ||||
|
13 | def load_ipython_extension(shell): | |||
|
14 | new_prompts = MyPrompt(shell) | |||
|
15 | new_prompts.old_prompts = shell.prompts | |||
|
16 | shell.prompts = new_prompts | |||
|
17 | ||||
|
18 | def unload_ipython_extension(shell): | |||
|
19 | if not hasattr(shell.prompts, 'old_prompts'): | |||
|
20 | print("cannot unload") | |||
|
21 | else: | |||
|
22 | shell.prompts = shell.prompts.old_prompts | |||
|
23 | ||||
|
24 | ||||
|
25 | ||||
|
26 |
@@ -113,7 +113,7 b' class DisplayHook(Configurable):' | |||||
113 | """ |
|
113 | """ | |
114 | # Use write, not print which adds an extra space. |
|
114 | # Use write, not print which adds an extra space. | |
115 | sys.stdout.write(self.shell.separate_out) |
|
115 | sys.stdout.write(self.shell.separate_out) | |
116 | outprompt = self.shell.prompt_manager.render('out') |
|
116 | outprompt = 'Out[{}]: '.format(self.shell.execution_count) | |
117 | if self.do_full_cache: |
|
117 | if self.do_full_cache: | |
118 | sys.stdout.write(outprompt) |
|
118 | sys.stdout.write(outprompt) | |
119 |
|
119 | |||
@@ -149,6 +149,9 b' class DisplayHook(Configurable):' | |||||
149 | """ |
|
149 | """ | |
150 | return self.shell.display_formatter.format(result) |
|
150 | return self.shell.display_formatter.format(result) | |
151 |
|
151 | |||
|
152 | # This can be set to True by the write_output_prompt method in a subclass | |||
|
153 | prompt_end_newline = False | |||
|
154 | ||||
152 | def write_format_data(self, format_dict, md_dict=None): |
|
155 | def write_format_data(self, format_dict, md_dict=None): | |
153 | """Write the format data dict to the frontend. |
|
156 | """Write the format data dict to the frontend. | |
154 |
|
157 | |||
@@ -179,8 +182,7 b' class DisplayHook(Configurable):' | |||||
179 | # because the expansion may add ANSI escapes that will interfere |
|
182 | # because the expansion may add ANSI escapes that will interfere | |
180 | # with our ability to determine whether or not we should add |
|
183 | # with our ability to determine whether or not we should add | |
181 | # a newline. |
|
184 | # a newline. | |
182 | prompt_template = self.shell.prompt_manager.out_template |
|
185 | if not self.prompt_end_newline: | |
183 | if prompt_template and not prompt_template.endswith('\n'): |
|
|||
184 | # But avoid extraneous empty lines. |
|
186 | # But avoid extraneous empty lines. | |
185 | result_repr = '\n' + result_repr |
|
187 | result_repr = '\n' + result_repr | |
186 |
|
188 |
@@ -56,7 +56,6 b' from IPython.core.macro import Macro' | |||||
56 | from IPython.core.payload import PayloadManager |
|
56 | from IPython.core.payload import PayloadManager | |
57 | from IPython.core.prefilter import PrefilterManager |
|
57 | from IPython.core.prefilter import PrefilterManager | |
58 | from IPython.core.profiledir import ProfileDir |
|
58 | from IPython.core.profiledir import ProfileDir | |
59 | from IPython.core.prompts import PromptManager |
|
|||
60 | from IPython.core.usage import default_banner |
|
59 | from IPython.core.usage import default_banner | |
61 | from IPython.testing.skipdoctest import skip_doctest_py2, skip_doctest |
|
60 | from IPython.testing.skipdoctest import skip_doctest_py2, skip_doctest | |
62 | from IPython.utils import PyColorize |
|
61 | from IPython.utils import PyColorize | |
@@ -664,8 +663,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
664 | io.stderr = io.IOStream(sys.stderr) |
|
663 | io.stderr = io.IOStream(sys.stderr) | |
665 |
|
664 | |||
666 | def init_prompts(self): |
|
665 | def init_prompts(self): | |
667 | self.prompt_manager = PromptManager(shell=self, parent=self) |
|
|||
668 | self.configurables.append(self.prompt_manager) |
|
|||
669 | # Set system prompts, so that scripts can decide if they are running |
|
666 | # Set system prompts, so that scripts can decide if they are running | |
670 | # interactively. |
|
667 | # interactively. | |
671 | sys.ps1 = 'In : ' |
|
668 | sys.ps1 = 'In : ' | |
@@ -2339,16 +2336,9 b' class InteractiveShell(SingletonConfigurable):' | |||||
2339 | """ |
|
2336 | """ | |
2340 | if not self.show_rewritten_input: |
|
2337 | if not self.show_rewritten_input: | |
2341 | return |
|
2338 | return | |
2342 |
|
||||
2343 | rw = self.prompt_manager.render('rewrite') + cmd |
|
|||
2344 |
|
2339 | |||
2345 | try: |
|
2340 | # This is overridden in TerminalInteractiveShell to use fancy prompts | |
2346 | # plain ascii works better w/ pyreadline, on some machines, so |
|
2341 | print("------> " + cmd) | |
2347 | # we use it and only print uncolored rewrite if we have unicode |
|
|||
2348 | rw = str(rw) |
|
|||
2349 | print(rw) |
|
|||
2350 | except UnicodeEncodeError: |
|
|||
2351 | print("------> " + cmd) |
|
|||
2352 |
|
2342 | |||
2353 | #------------------------------------------------------------------------- |
|
2343 | #------------------------------------------------------------------------- | |
2354 | # Things related to extracting values/expressions from kernel and user_ns |
|
2344 | # Things related to extracting values/expressions from kernel and user_ns | |
@@ -3233,6 +3223,11 b' class InteractiveShell(SingletonConfigurable):' | |||||
3233 | self.restore_sys_module_state() |
|
3223 | self.restore_sys_module_state() | |
3234 |
|
3224 | |||
3235 |
|
3225 | |||
|
3226 | # Overridden in terminal subclass to change prompts | |||
|
3227 | def switch_doctest_mode(self, mode): | |||
|
3228 | pass | |||
|
3229 | ||||
|
3230 | ||||
3236 | class InteractiveShellABC(with_metaclass(abc.ABCMeta, object)): |
|
3231 | class InteractiveShellABC(with_metaclass(abc.ABCMeta, object)): | |
3237 | """An abstract base class for InteractiveShell.""" |
|
3232 | """An abstract base class for InteractiveShell.""" | |
3238 |
|
3233 |
@@ -356,14 +356,6 b' Defaulting color scheme to \'NoColor\'"""' | |||||
356 | # Will remove this check after switching to prompt_toolkit |
|
356 | # Will remove this check after switching to prompt_toolkit | |
357 | new_scheme = 'NoColor' |
|
357 | new_scheme = 'NoColor' | |
358 |
|
358 | |||
359 | # Set prompt colors |
|
|||
360 | try: |
|
|||
361 | shell.prompt_manager.color_scheme = new_scheme |
|
|||
362 | except: |
|
|||
363 | color_switch_err('prompt') |
|
|||
364 | else: |
|
|||
365 | shell.colors = \ |
|
|||
366 | shell.prompt_manager.color_scheme_table.active_scheme_name |
|
|||
367 | # Set exception colors |
|
359 | # Set exception colors | |
368 | try: |
|
360 | try: | |
369 | shell.InteractiveTB.set_colors(scheme = new_scheme) |
|
361 | shell.InteractiveTB.set_colors(scheme = new_scheme) | |
@@ -435,7 +427,6 b' Defaulting color scheme to \'NoColor\'"""' | |||||
435 |
|
427 | |||
436 | # Shorthands |
|
428 | # Shorthands | |
437 | shell = self.shell |
|
429 | shell = self.shell | |
438 | pm = shell.prompt_manager |
|
|||
439 | meta = shell.meta |
|
430 | meta = shell.meta | |
440 | disp_formatter = self.shell.display_formatter |
|
431 | disp_formatter = self.shell.display_formatter | |
441 | ptformatter = disp_formatter.formatters['text/plain'] |
|
432 | ptformatter = disp_formatter.formatters['text/plain'] | |
@@ -450,23 +441,17 b' Defaulting color scheme to \'NoColor\'"""' | |||||
450 | save_dstore('xmode',shell.InteractiveTB.mode) |
|
441 | save_dstore('xmode',shell.InteractiveTB.mode) | |
451 | save_dstore('rc_separate_out',shell.separate_out) |
|
442 | save_dstore('rc_separate_out',shell.separate_out) | |
452 | save_dstore('rc_separate_out2',shell.separate_out2) |
|
443 | save_dstore('rc_separate_out2',shell.separate_out2) | |
453 | save_dstore('rc_prompts_pad_left',pm.justify) |
|
|||
454 | save_dstore('rc_separate_in',shell.separate_in) |
|
444 | save_dstore('rc_separate_in',shell.separate_in) | |
455 | save_dstore('rc_active_types',disp_formatter.active_types) |
|
445 | save_dstore('rc_active_types',disp_formatter.active_types) | |
456 | save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template)) |
|
|||
457 |
|
446 | |||
458 | if not mode: |
|
447 | if not mode: | |
459 | # turn on |
|
448 | # turn on | |
460 | pm.in_template = '>>> ' |
|
|||
461 | pm.in2_template = '... ' |
|
|||
462 | pm.out_template = '' |
|
|||
463 |
|
449 | |||
464 | # Prompt separators like plain python |
|
450 | # Prompt separators like plain python | |
465 | shell.separate_in = '' |
|
451 | shell.separate_in = '' | |
466 | shell.separate_out = '' |
|
452 | shell.separate_out = '' | |
467 | shell.separate_out2 = '' |
|
453 | shell.separate_out2 = '' | |
468 |
|
454 | |||
469 | pm.justify = False |
|
|||
470 |
|
455 | |||
471 | ptformatter.pprint = False |
|
456 | ptformatter.pprint = False | |
472 | disp_formatter.active_types = ['text/plain'] |
|
457 | disp_formatter.active_types = ['text/plain'] | |
@@ -474,22 +459,22 b' Defaulting color scheme to \'NoColor\'"""' | |||||
474 | shell.magic('xmode Plain') |
|
459 | shell.magic('xmode Plain') | |
475 | else: |
|
460 | else: | |
476 | # turn off |
|
461 | # turn off | |
477 | pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates |
|
|||
478 |
|
||||
479 | shell.separate_in = dstore.rc_separate_in |
|
462 | shell.separate_in = dstore.rc_separate_in | |
480 |
|
463 | |||
481 | shell.separate_out = dstore.rc_separate_out |
|
464 | shell.separate_out = dstore.rc_separate_out | |
482 | shell.separate_out2 = dstore.rc_separate_out2 |
|
465 | shell.separate_out2 = dstore.rc_separate_out2 | |
483 |
|
466 | |||
484 | pm.justify = dstore.rc_prompts_pad_left |
|
|||
485 |
|
||||
486 | ptformatter.pprint = dstore.rc_pprint |
|
467 | ptformatter.pprint = dstore.rc_pprint | |
487 | disp_formatter.active_types = dstore.rc_active_types |
|
468 | disp_formatter.active_types = dstore.rc_active_types | |
488 |
|
469 | |||
489 | shell.magic('xmode ' + dstore.xmode) |
|
470 | shell.magic('xmode ' + dstore.xmode) | |
490 |
|
471 | |||
|
472 | # mode here is the state before we switch; switch_doctest_mode takes | |||
|
473 | # the mode we're switching to. | |||
|
474 | shell.switch_doctest_mode(not mode) | |||
|
475 | ||||
491 | # Store new mode and inform |
|
476 | # Store new mode and inform | |
492 |
dstore.mode = bool( |
|
477 | dstore.mode = bool(not mode) | |
493 | mode_label = ['OFF','ON'][dstore.mode] |
|
478 | mode_label = ['OFF','ON'][dstore.mode] | |
494 | print('Doctest mode is:', mode_label) |
|
479 | print('Doctest mode is:', mode_label) | |
495 |
|
480 |
@@ -1,37 +1,8 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Classes for handling input/output prompts.""" |
|
2 | """Being removed | |
|
3 | """ | |||
3 |
|
4 | |||
4 | # Copyright (c) 2001-2007 Fernando Perez <fperez@colorado.edu> |
|
5 | from IPython.utils import py3compat | |
5 | # Copyright (c) IPython Development Team. |
|
|||
6 | # Distributed under the terms of the Modified BSD License. |
|
|||
7 |
|
||||
8 | import os |
|
|||
9 | import re |
|
|||
10 | import socket |
|
|||
11 | import sys |
|
|||
12 | import time |
|
|||
13 |
|
||||
14 | from string import Formatter |
|
|||
15 |
|
||||
16 | from traitlets.config.configurable import Configurable |
|
|||
17 | from IPython.core import release |
|
|||
18 | from IPython.utils import coloransi, py3compat |
|
|||
19 | from traitlets import Unicode, Instance, Dict, Bool, Int, observe, default |
|
|||
20 |
|
||||
21 | from IPython.utils.PyColorize import LightBGColors, LinuxColors, NoColor |
|
|||
22 |
|
||||
23 | #----------------------------------------------------------------------------- |
|
|||
24 | # Color schemes for prompts |
|
|||
25 | #----------------------------------------------------------------------------- |
|
|||
26 |
|
||||
27 | InputColors = coloransi.InputTermColors # just a shorthand |
|
|||
28 | Colors = coloransi.TermColors # just a shorthand |
|
|||
29 |
|
||||
30 | color_lists = dict(normal=Colors(), inp=InputColors(), nocolor=coloransi.NoColors()) |
|
|||
31 |
|
||||
32 | #----------------------------------------------------------------------------- |
|
|||
33 | # Utilities |
|
|||
34 | #----------------------------------------------------------------------------- |
|
|||
35 |
|
6 | |||
36 | class LazyEvaluate(object): |
|
7 | class LazyEvaluate(object): | |
37 | """This is used for formatting strings with values that need to be updated |
|
8 | """This is used for formatting strings with values that need to be updated | |
@@ -53,360 +24,3 b' class LazyEvaluate(object):' | |||||
53 |
|
24 | |||
54 | def __format__(self, format_spec): |
|
25 | def __format__(self, format_spec): | |
55 | return format(self(), format_spec) |
|
26 | return format(self(), format_spec) | |
56 |
|
||||
57 | def multiple_replace(dict, text): |
|
|||
58 | """ Replace in 'text' all occurrences of any key in the given |
|
|||
59 | dictionary by its corresponding value. Returns the new string.""" |
|
|||
60 |
|
||||
61 | # Function by Xavier Defrang, originally found at: |
|
|||
62 | # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81330 |
|
|||
63 |
|
||||
64 | # Create a regular expression from the dictionary keys |
|
|||
65 | regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys()))) |
|
|||
66 | # For each match, look-up corresponding value in dictionary |
|
|||
67 | return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text) |
|
|||
68 |
|
||||
69 | #----------------------------------------------------------------------------- |
|
|||
70 | # Special characters that can be used in prompt templates, mainly bash-like |
|
|||
71 | #----------------------------------------------------------------------------- |
|
|||
72 |
|
||||
73 | # If $HOME isn't defined (Windows), make it an absurd string so that it can |
|
|||
74 | # never be expanded out into '~'. Basically anything which can never be a |
|
|||
75 | # reasonable directory name will do, we just want the $HOME -> '~' operation |
|
|||
76 | # to become a no-op. We pre-compute $HOME here so it's not done on every |
|
|||
77 | # prompt call. |
|
|||
78 |
|
||||
79 | # FIXME: |
|
|||
80 |
|
||||
81 | # - This should be turned into a class which does proper namespace management, |
|
|||
82 | # since the prompt specials need to be evaluated in a certain namespace. |
|
|||
83 | # Currently it's just globals, which need to be managed manually by code |
|
|||
84 | # below. |
|
|||
85 |
|
||||
86 | # - I also need to split up the color schemes from the prompt specials |
|
|||
87 | # somehow. I don't have a clean design for that quite yet. |
|
|||
88 |
|
||||
89 | HOME = py3compat.str_to_unicode(os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")) |
|
|||
90 |
|
||||
91 | # This is needed on FreeBSD, and maybe other systems which symlink /home to |
|
|||
92 | # /usr/home, but retain the $HOME variable as pointing to /home |
|
|||
93 | HOME = os.path.realpath(HOME) |
|
|||
94 |
|
||||
95 | # We precompute a few more strings here for the prompt_specials, which are |
|
|||
96 | # fixed once ipython starts. This reduces the runtime overhead of computing |
|
|||
97 | # prompt strings. |
|
|||
98 | USER = py3compat.str_to_unicode(os.environ.get("USER",'')) |
|
|||
99 | HOSTNAME = py3compat.str_to_unicode(socket.gethostname()) |
|
|||
100 | HOSTNAME_SHORT = HOSTNAME.split(".")[0] |
|
|||
101 |
|
||||
102 | # IronPython doesn't currently have os.getuid() even if |
|
|||
103 | # os.name == 'posix'; 2/8/2014 |
|
|||
104 | ROOT_SYMBOL = "#" if (os.name=='nt' or sys.platform=='cli' or os.getuid()==0) else "$" |
|
|||
105 |
|
||||
106 | prompt_abbreviations = { |
|
|||
107 | # Prompt/history count |
|
|||
108 | '%n' : '{color.number}' '{count}' '{color.prompt}', |
|
|||
109 | r'\#': '{color.number}' '{count}' '{color.prompt}', |
|
|||
110 | # Just the prompt counter number, WITHOUT any coloring wrappers, so users |
|
|||
111 | # can get numbers displayed in whatever color they want. |
|
|||
112 | r'\N': '{count}', |
|
|||
113 |
|
||||
114 | # Prompt/history count, with the actual digits replaced by dots or |
|
|||
115 | # spaces. Used mainly in continuation prompts (prompt_in2). |
|
|||
116 | r'\D': '{dots}', |
|
|||
117 | r'\S': '{spaces}', |
|
|||
118 |
|
||||
119 | # Current time |
|
|||
120 | r'\T' : '{time}', |
|
|||
121 | # Current working directory |
|
|||
122 | r'\w': '{cwd}', |
|
|||
123 | # Basename of current working directory. |
|
|||
124 | # (use os.sep to make this portable across OSes) |
|
|||
125 | r'\W' : '{cwd_last}', |
|
|||
126 | # These X<N> are an extension to the normal bash prompts. They return |
|
|||
127 | # N terms of the path, after replacing $HOME with '~' |
|
|||
128 | r'\X0': '{cwd_x[0]}', |
|
|||
129 | r'\X1': '{cwd_x[1]}', |
|
|||
130 | r'\X2': '{cwd_x[2]}', |
|
|||
131 | r'\X3': '{cwd_x[3]}', |
|
|||
132 | r'\X4': '{cwd_x[4]}', |
|
|||
133 | r'\X5': '{cwd_x[5]}', |
|
|||
134 | # Y<N> are similar to X<N>, but they show '~' if it's the directory |
|
|||
135 | # N+1 in the list. Somewhat like %cN in tcsh. |
|
|||
136 | r'\Y0': '{cwd_y[0]}', |
|
|||
137 | r'\Y1': '{cwd_y[1]}', |
|
|||
138 | r'\Y2': '{cwd_y[2]}', |
|
|||
139 | r'\Y3': '{cwd_y[3]}', |
|
|||
140 | r'\Y4': '{cwd_y[4]}', |
|
|||
141 | r'\Y5': '{cwd_y[5]}', |
|
|||
142 | # Hostname up to first . |
|
|||
143 | r'\h': HOSTNAME_SHORT, |
|
|||
144 | # Full hostname |
|
|||
145 | r'\H': HOSTNAME, |
|
|||
146 | # Username of current user |
|
|||
147 | r'\u': USER, |
|
|||
148 | # Escaped '\' |
|
|||
149 | '\\\\': '\\', |
|
|||
150 | # Newline |
|
|||
151 | r'\n': '\n', |
|
|||
152 | # Carriage return |
|
|||
153 | r'\r': '\r', |
|
|||
154 | # Release version |
|
|||
155 | r'\v': release.version, |
|
|||
156 | # Root symbol ($ or #) |
|
|||
157 | r'\$': ROOT_SYMBOL, |
|
|||
158 | } |
|
|||
159 |
|
||||
160 | #----------------------------------------------------------------------------- |
|
|||
161 | # More utilities |
|
|||
162 | #----------------------------------------------------------------------------- |
|
|||
163 |
|
||||
164 | def cwd_filt(depth): |
|
|||
165 | """Return the last depth elements of the current working directory. |
|
|||
166 |
|
||||
167 | $HOME is always replaced with '~'. |
|
|||
168 | If depth==0, the full path is returned.""" |
|
|||
169 |
|
||||
170 | cwd = py3compat.getcwd().replace(HOME,"~") |
|
|||
171 | out = os.sep.join(cwd.split(os.sep)[-depth:]) |
|
|||
172 | return out or os.sep |
|
|||
173 |
|
||||
174 | def cwd_filt2(depth): |
|
|||
175 | """Return the last depth elements of the current working directory. |
|
|||
176 |
|
||||
177 | $HOME is always replaced with '~'. |
|
|||
178 | If depth==0, the full path is returned.""" |
|
|||
179 |
|
||||
180 | full_cwd = py3compat.getcwd() |
|
|||
181 | cwd = full_cwd.replace(HOME,"~").split(os.sep) |
|
|||
182 | if '~' in cwd and len(cwd) == depth+1: |
|
|||
183 | depth += 1 |
|
|||
184 | drivepart = '' |
|
|||
185 | if sys.platform == 'win32' and len(cwd) > depth: |
|
|||
186 | drivepart = os.path.splitdrive(full_cwd)[0] |
|
|||
187 | out = drivepart + '/'.join(cwd[-depth:]) |
|
|||
188 |
|
||||
189 | return out or os.sep |
|
|||
190 |
|
||||
191 | #----------------------------------------------------------------------------- |
|
|||
192 | # Prompt classes |
|
|||
193 | #----------------------------------------------------------------------------- |
|
|||
194 |
|
||||
195 | lazily_evaluate = {'time': LazyEvaluate(time.strftime, "%H:%M:%S"), |
|
|||
196 | 'cwd': LazyEvaluate(py3compat.getcwd), |
|
|||
197 | 'cwd_last': LazyEvaluate(lambda: py3compat.getcwd().split(os.sep)[-1]), |
|
|||
198 | 'cwd_x': [LazyEvaluate(lambda: py3compat.getcwd().replace(HOME,"~"))] +\ |
|
|||
199 | [LazyEvaluate(cwd_filt, x) for x in range(1,6)], |
|
|||
200 | 'cwd_y': [LazyEvaluate(cwd_filt2, x) for x in range(6)] |
|
|||
201 | } |
|
|||
202 |
|
||||
203 | def _lenlastline(s): |
|
|||
204 | """Get the length of the last line. More intelligent than |
|
|||
205 | len(s.splitlines()[-1]). |
|
|||
206 | """ |
|
|||
207 | if not s or s.endswith(('\n', '\r')): |
|
|||
208 | return 0 |
|
|||
209 | return len(s.splitlines()[-1]) |
|
|||
210 |
|
||||
211 |
|
||||
212 | invisible_chars_re = re.compile('\001[^\001\002]*\002') |
|
|||
213 | def _invisible_characters(s): |
|
|||
214 | """ |
|
|||
215 | Get the number of invisible ANSI characters in s. Invisible characters |
|
|||
216 | must be delimited by \001 and \002. |
|
|||
217 | """ |
|
|||
218 | return _lenlastline(s) - _lenlastline(invisible_chars_re.sub('', s)) |
|
|||
219 |
|
||||
220 | class UserNSFormatter(Formatter): |
|
|||
221 | """A Formatter that falls back on a shell's user_ns and __builtins__ for name resolution""" |
|
|||
222 | def __init__(self, shell): |
|
|||
223 | self.shell = shell |
|
|||
224 |
|
||||
225 | def get_value(self, key, args, kwargs): |
|
|||
226 | # try regular formatting first: |
|
|||
227 | try: |
|
|||
228 | return Formatter.get_value(self, key, args, kwargs) |
|
|||
229 | except Exception: |
|
|||
230 | pass |
|
|||
231 | # next, look in user_ns and builtins: |
|
|||
232 | for container in (self.shell.user_ns, __builtins__): |
|
|||
233 | if key in container: |
|
|||
234 | return container[key] |
|
|||
235 | # nothing found, put error message in its place |
|
|||
236 | return "<ERROR: '%s' not found>" % key |
|
|||
237 |
|
||||
238 |
|
||||
239 | class PromptManager(Configurable): |
|
|||
240 | """This is the primary interface for producing IPython's prompts.""" |
|
|||
241 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) |
|
|||
242 |
|
||||
243 | color_scheme_table = Instance(coloransi.ColorSchemeTable, allow_none=True) |
|
|||
244 | color_scheme = Unicode('Linux').tag(config=True) |
|
|||
245 |
|
||||
246 | @observe('color_scheme') |
|
|||
247 | def _color_scheme_changed(self, change): |
|
|||
248 | self.color_scheme_table.set_active_scheme(change['new']) |
|
|||
249 | for pname in ['in', 'in2', 'out', 'rewrite']: |
|
|||
250 | # We need to recalculate the number of invisible characters |
|
|||
251 | self.update_prompt(pname) |
|
|||
252 |
|
||||
253 | lazy_evaluate_fields = Dict(help=""" |
|
|||
254 | This maps field names used in the prompt templates to functions which |
|
|||
255 | will be called when the prompt is rendered. This allows us to include |
|
|||
256 | things like the current time in the prompts. Functions are only called |
|
|||
257 | if they are used in the prompt. |
|
|||
258 | """) |
|
|||
259 |
|
||||
260 | in_template = Unicode('In [\\#]: ', |
|
|||
261 | help="Input prompt. '\\#' will be transformed to the prompt number" |
|
|||
262 | ).tag(config=True) |
|
|||
263 | in2_template = Unicode(' .\\D.: ', |
|
|||
264 | help="Continuation prompt.").tag(config=True) |
|
|||
265 | out_template = Unicode('Out[\\#]: ', |
|
|||
266 | help="Output prompt. '\\#' will be transformed to the prompt number" |
|
|||
267 | ).tag(config=True) |
|
|||
268 |
|
||||
269 | @default('lazy_evaluate_fields') |
|
|||
270 | def _lazy_evaluate_fields_default(self): |
|
|||
271 | return lazily_evaluate.copy() |
|
|||
272 |
|
||||
273 | justify = Bool(True, help=""" |
|
|||
274 | If True (default), each prompt will be right-aligned with the |
|
|||
275 | preceding one. |
|
|||
276 | """).tag(config=True) |
|
|||
277 |
|
||||
278 | # We actually store the expanded templates here: |
|
|||
279 | templates = Dict() |
|
|||
280 |
|
||||
281 | # The number of characters in the last prompt rendered, not including |
|
|||
282 | # colour characters. |
|
|||
283 | width = Int() |
|
|||
284 | txtwidth = Int() # Not including right-justification |
|
|||
285 |
|
||||
286 | # The number of characters in each prompt which don't contribute to width |
|
|||
287 | invisible_chars = Dict() |
|
|||
288 |
|
||||
289 | @default('invisible_chars') |
|
|||
290 | def _invisible_chars_default(self): |
|
|||
291 | return {'in': 0, 'in2': 0, 'out': 0, 'rewrite':0} |
|
|||
292 |
|
||||
293 | def __init__(self, shell, **kwargs): |
|
|||
294 | super(PromptManager, self).__init__(shell=shell, **kwargs) |
|
|||
295 |
|
||||
296 | # Prepare colour scheme table |
|
|||
297 | self.color_scheme_table = coloransi.ColorSchemeTable([NoColor, |
|
|||
298 | LinuxColors, LightBGColors], self.color_scheme) |
|
|||
299 |
|
||||
300 | self._formatter = UserNSFormatter(shell) |
|
|||
301 | # Prepare templates & numbers of invisible characters |
|
|||
302 | self.update_prompt('in', self.in_template) |
|
|||
303 | self.update_prompt('in2', self.in2_template) |
|
|||
304 | self.update_prompt('out', self.out_template) |
|
|||
305 | self.update_prompt('rewrite') |
|
|||
306 | self.observe(self._update_prompt_trait, |
|
|||
307 | names=['in_template', 'in2_template', 'out_template']) |
|
|||
308 |
|
||||
309 | def update_prompt(self, name, new_template=None): |
|
|||
310 | """This is called when a prompt template is updated. It processes |
|
|||
311 | abbreviations used in the prompt template (like \#) and calculates how |
|
|||
312 | many invisible characters (ANSI colour escapes) the resulting prompt |
|
|||
313 | contains. |
|
|||
314 |
|
||||
315 | It is also called for each prompt on changing the colour scheme. In both |
|
|||
316 | cases, traitlets should take care of calling this automatically. |
|
|||
317 | """ |
|
|||
318 | if new_template is not None: |
|
|||
319 | self.templates[name] = multiple_replace(prompt_abbreviations, new_template) |
|
|||
320 | # We count invisible characters (colour escapes) on the last line of the |
|
|||
321 | # prompt, to calculate the width for lining up subsequent prompts. |
|
|||
322 | invis_chars = _invisible_characters(self._render(name, color=True)) |
|
|||
323 | self.invisible_chars[name] = invis_chars |
|
|||
324 |
|
||||
325 | def _update_prompt_trait(self, changes): |
|
|||
326 | traitname = changes['name'] |
|
|||
327 | new_template = changes['new'] |
|
|||
328 | name = traitname[:-9] # Cut off '_template' |
|
|||
329 | self.update_prompt(name, new_template) |
|
|||
330 |
|
||||
331 | def _render(self, name, color=True, **kwargs): |
|
|||
332 | """Render but don't justify, or update the width or txtwidth attributes. |
|
|||
333 | """ |
|
|||
334 | if name == 'rewrite': |
|
|||
335 | return self._render_rewrite(color=color) |
|
|||
336 |
|
||||
337 | if color: |
|
|||
338 | scheme = self.color_scheme_table.active_colors |
|
|||
339 | if name=='out': |
|
|||
340 | colors = color_lists['normal'] |
|
|||
341 | colors.number, colors.prompt, colors.normal = \ |
|
|||
342 | scheme.out_number, scheme.out_prompt, scheme.normal |
|
|||
343 | else: |
|
|||
344 | colors = color_lists['inp'] |
|
|||
345 | colors.number, colors.prompt, colors.normal = \ |
|
|||
346 | scheme.in_number, scheme.in_prompt, scheme.in_normal |
|
|||
347 | if name=='in2': |
|
|||
348 | colors.prompt = scheme.in_prompt2 |
|
|||
349 | else: |
|
|||
350 | # No color |
|
|||
351 | colors = color_lists['nocolor'] |
|
|||
352 | colors.number, colors.prompt, colors.normal = '', '', '' |
|
|||
353 |
|
||||
354 | count = self.shell.execution_count # Shorthand |
|
|||
355 | # Build the dictionary to be passed to string formatting |
|
|||
356 | fmtargs = dict(color=colors, count=count, |
|
|||
357 | dots="."*len(str(count)), spaces=" "*len(str(count)), |
|
|||
358 | width=self.width, txtwidth=self.txtwidth) |
|
|||
359 | fmtargs.update(self.lazy_evaluate_fields) |
|
|||
360 | fmtargs.update(kwargs) |
|
|||
361 |
|
||||
362 | # Prepare the prompt |
|
|||
363 | prompt = colors.prompt + self.templates[name] + colors.normal |
|
|||
364 |
|
||||
365 | # Fill in required fields |
|
|||
366 | return self._formatter.format(prompt, **fmtargs) |
|
|||
367 |
|
||||
368 | def _render_rewrite(self, color=True): |
|
|||
369 | """Render the ---> rewrite prompt.""" |
|
|||
370 | if color: |
|
|||
371 | scheme = self.color_scheme_table.active_colors |
|
|||
372 | # We need a non-input version of these escapes |
|
|||
373 | color_prompt = scheme.in_prompt.replace("\001","").replace("\002","") |
|
|||
374 | color_normal = scheme.normal |
|
|||
375 | else: |
|
|||
376 | color_prompt, color_normal = '', '' |
|
|||
377 |
|
||||
378 | return color_prompt + "-> ".rjust(self.txtwidth, "-") + color_normal |
|
|||
379 |
|
||||
380 | def render(self, name, color=True, just=None, **kwargs): |
|
|||
381 | """ |
|
|||
382 | Render the selected prompt. |
|
|||
383 |
|
||||
384 | Parameters |
|
|||
385 | ---------- |
|
|||
386 | name : str |
|
|||
387 | Which prompt to render. One of 'in', 'in2', 'out', 'rewrite' |
|
|||
388 | color : bool |
|
|||
389 | If True (default), include ANSI escape sequences for a coloured prompt. |
|
|||
390 | just : bool |
|
|||
391 | If True, justify the prompt to the width of the last prompt. The |
|
|||
392 | default is stored in self.justify. |
|
|||
393 | **kwargs : |
|
|||
394 | Additional arguments will be passed to the string formatting operation, |
|
|||
395 | so they can override the values that would otherwise fill in the |
|
|||
396 | template. |
|
|||
397 |
|
||||
398 | Returns |
|
|||
399 | ------- |
|
|||
400 | A string containing the rendered prompt. |
|
|||
401 | """ |
|
|||
402 | res = self._render(name, color=color, **kwargs) |
|
|||
403 |
|
||||
404 | # Handle justification of prompt |
|
|||
405 | invis_chars = self.invisible_chars[name] if color else 0 |
|
|||
406 | self.txtwidth = _lenlastline(res) - invis_chars |
|
|||
407 | just = self.justify if (just is None) else just |
|
|||
408 | # If the prompt spans more than one line, don't try to justify it: |
|
|||
409 | if just and name != 'in' and ('\n' not in res) and ('\r' not in res): |
|
|||
410 | res = res.rjust(self.width + invis_chars) |
|
|||
411 | self.width = _lenlastline(res) - invis_chars |
|
|||
412 | return res |
|
@@ -3,75 +3,14 b'' | |||||
3 |
|
3 | |||
4 | import unittest |
|
4 | import unittest | |
5 |
|
5 | |||
6 | import os |
|
6 | from IPython.core.prompts import LazyEvaluate | |
7 |
|
||||
8 | from IPython.testing import tools as tt, decorators as dec |
|
|||
9 | from IPython.core.prompts import PromptManager, LazyEvaluate, _invisible_characters |
|
|||
10 | from IPython.testing.globalipapp import get_ipython |
|
7 | from IPython.testing.globalipapp import get_ipython | |
11 | from IPython.utils.tempdir import TemporaryWorkingDirectory |
|
|||
12 | from IPython.utils import py3compat |
|
|||
13 | from IPython.utils.py3compat import unicode_type |
|
8 | from IPython.utils.py3compat import unicode_type | |
14 |
|
9 | |||
15 | ip = get_ipython() |
|
10 | ip = get_ipython() | |
16 |
|
11 | |||
17 |
|
12 | |||
18 | class PromptTests(unittest.TestCase): |
|
13 | class PromptTests(unittest.TestCase): | |
19 | def setUp(self): |
|
|||
20 | self.pm = PromptManager(shell=ip, config=ip.config) |
|
|||
21 |
|
||||
22 | def test_multiline_prompt(self): |
|
|||
23 | self.pm.in_template = "[In]\n>>>" |
|
|||
24 | self.pm.render('in') |
|
|||
25 | self.assertEqual(self.pm.width, 3) |
|
|||
26 | self.assertEqual(self.pm.txtwidth, 3) |
|
|||
27 |
|
||||
28 | self.pm.in_template = '[In]\n' |
|
|||
29 | self.pm.render('in') |
|
|||
30 | self.assertEqual(self.pm.width, 0) |
|
|||
31 | self.assertEqual(self.pm.txtwidth, 0) |
|
|||
32 |
|
||||
33 | def test_translate_abbreviations(self): |
|
|||
34 | def do_translate(template): |
|
|||
35 | self.pm.in_template = template |
|
|||
36 | return self.pm.templates['in'] |
|
|||
37 |
|
||||
38 | pairs = [(r'%n>', '{color.number}{count}{color.prompt}>'), |
|
|||
39 | (r'\T', '{time}'), |
|
|||
40 | (r'\n', '\n') |
|
|||
41 | ] |
|
|||
42 |
|
||||
43 | tt.check_pairs(do_translate, pairs) |
|
|||
44 |
|
||||
45 | def test_user_ns(self): |
|
|||
46 | self.pm.color_scheme = 'NoColor' |
|
|||
47 | ip.ex("foo='bar'") |
|
|||
48 | self.pm.in_template = "In [{foo}]" |
|
|||
49 | prompt = self.pm.render('in') |
|
|||
50 | self.assertEqual(prompt, u'In [bar]') |
|
|||
51 |
|
||||
52 | def test_builtins(self): |
|
|||
53 | self.pm.color_scheme = 'NoColor' |
|
|||
54 | self.pm.in_template = "In [{int}]" |
|
|||
55 | prompt = self.pm.render('in') |
|
|||
56 | self.assertEqual(prompt, u"In [%r]" % int) |
|
|||
57 |
|
||||
58 | def test_undefined(self): |
|
|||
59 | self.pm.color_scheme = 'NoColor' |
|
|||
60 | self.pm.in_template = "In [{foo_dne}]" |
|
|||
61 | prompt = self.pm.render('in') |
|
|||
62 | self.assertEqual(prompt, u"In [<ERROR: 'foo_dne' not found>]") |
|
|||
63 |
|
||||
64 | def test_render(self): |
|
|||
65 | self.pm.in_template = r'\#>' |
|
|||
66 | self.assertEqual(self.pm.render('in',color=False), '%d>' % ip.execution_count) |
|
|||
67 |
|
||||
68 | @dec.onlyif_unicode_paths |
|
|||
69 | def test_render_unicode_cwd(self): |
|
|||
70 | with TemporaryWorkingDirectory(u'ΓΌnicΓΈdΓ©'): |
|
|||
71 | self.pm.in_template = r'\w [\#]' |
|
|||
72 | p = self.pm.render('in', color=False) |
|
|||
73 | self.assertEqual(p, u"%s [%i]" % (py3compat.getcwd(), ip.execution_count)) |
|
|||
74 |
|
||||
75 | def test_lazy_eval_unicode(self): |
|
14 | def test_lazy_eval_unicode(self): | |
76 | u = u'ΓΌnicΓΈdΓ©' |
|
15 | u = u'ΓΌnicΓΈdΓ©' | |
77 | lz = LazyEvaluate(lambda : u) |
|
16 | lz = LazyEvaluate(lambda : u) | |
@@ -95,35 +34,4 b' class PromptTests(unittest.TestCase):' | |||||
95 | self.assertEqual(unicode_type(lz), unicode_type(f)) |
|
34 | self.assertEqual(unicode_type(lz), unicode_type(f)) | |
96 | self.assertEqual(format(lz), str(f)) |
|
35 | self.assertEqual(format(lz), str(f)) | |
97 | self.assertEqual(format(lz, '.1'), '0.5') |
|
36 | self.assertEqual(format(lz, '.1'), '0.5') | |
98 |
|
||||
99 | @dec.skip_win32 |
|
|||
100 | def test_cwd_x(self): |
|
|||
101 | self.pm.in_template = r"\X0" |
|
|||
102 | save = py3compat.getcwd() |
|
|||
103 | os.chdir(os.path.expanduser('~')) |
|
|||
104 | p = self.pm.render('in', color=False) |
|
|||
105 | try: |
|
|||
106 | self.assertEqual(p, '~') |
|
|||
107 | finally: |
|
|||
108 | os.chdir(save) |
|
|||
109 |
|
||||
110 | def test_invisible_chars(self): |
|
|||
111 | self.assertEqual(_invisible_characters('abc'), 0) |
|
|||
112 | self.assertEqual(_invisible_characters('\001\033[1;37m\002'), 9) |
|
|||
113 | # Sequences must be between \001 and \002 to be counted |
|
|||
114 | self.assertEqual(_invisible_characters('\033[1;37m'), 0) |
|
|||
115 | # Test custom escape sequences |
|
|||
116 | self.assertEqual(_invisible_characters('\001\033]133;A\a\002'), 10) |
|
|||
117 |
|
||||
118 | def test_width(self): |
|
|||
119 | default_in = '\x01\x1b]133;A\x07\x02In [1]: \x01\x1b]133;B\x07\x02' |
|
|||
120 | self.pm.in_template = default_in |
|
|||
121 | self.pm.render('in') |
|
|||
122 | self.assertEqual(self.pm.width, 8) |
|
|||
123 | self.assertEqual(self.pm.txtwidth, 8) |
|
|||
124 |
|
37 | |||
125 | # Test custom escape sequences |
|
|||
126 | self.pm.in_template = '\001\033]133;A\a\002' + default_in + '\001\033]133;B\a\002' |
|
|||
127 | self.pm.render('in') |
|
|||
128 | self.assertEqual(self.pm.width, 8) |
|
|||
129 | self.assertEqual(self.pm.txtwidth, 8) |
|
@@ -205,13 +205,6 b' class TestMagicRunPass(tt.TempFileMixin):' | |||||
205 | _ip = get_ipython() |
|
205 | _ip = get_ipython() | |
206 | self.run_tmpfile() |
|
206 | self.run_tmpfile() | |
207 | nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys)) |
|
207 | nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys)) | |
208 |
|
||||
209 | def test_prompts(self): |
|
|||
210 | """Test that prompts correctly generate after %run""" |
|
|||
211 | self.run_tmpfile() |
|
|||
212 | _ip = get_ipython() |
|
|||
213 | p2 = _ip.prompt_manager.render('in2').strip() |
|
|||
214 | nt.assert_equal(p2[:3], '...') |
|
|||
215 |
|
208 | |||
216 | def test_run_profile( self ): |
|
209 | def test_run_profile( self ): | |
217 | """Test that the option -p, which invokes the profiler, do not |
|
210 | """Test that the option -p, which invokes the profiler, do not |
@@ -52,10 +52,9 b' class TestFileToRun(unittest.TestCase, tt.TempFileMixin):' | |||||
52 | src = "True\n" |
|
52 | src = "True\n" | |
53 | self.mktmp(src) |
|
53 | self.mktmp(src) | |
54 |
|
54 | |||
55 | out = 'In [1]: False\n\nIn [2]:' |
|
55 | out, err = tt.ipexec(self.fname, options=['-i'], | |
56 | err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None |
|
|||
57 | tt.ipexec_validate(self.fname, out, err, options=['-i'], |
|
|||
58 | commands=['"__file__" in globals()', 'exit()']) |
|
56 | commands=['"__file__" in globals()', 'exit()']) | |
|
57 | self.assertIn("False", out) | |||
59 |
|
58 | |||
60 | @dec.skip_win32 |
|
59 | @dec.skip_win32 | |
61 | @dec.skipif(PY3) |
|
60 | @dec.skipif(PY3) | |
@@ -64,7 +63,6 b' class TestFileToRun(unittest.TestCase, tt.TempFileMixin):' | |||||
64 | src = "from __future__ import division\n" |
|
63 | src = "from __future__ import division\n" | |
65 | self.mktmp(src) |
|
64 | self.mktmp(src) | |
66 |
|
65 | |||
67 | out = 'In [1]: float\n\nIn [2]:' |
|
66 | out, err = tt.ipexec(self.fname, options=['-i'], | |
68 | err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None |
|
|||
69 | tt.ipexec_validate(self.fname, out, err, options=['-i'], |
|
|||
70 | commands=['type(1/2)', 'exit()']) |
|
67 | commands=['type(1/2)', 'exit()']) | |
|
68 | self.assertIn('float', out) |
@@ -898,7 +898,6 b' class IPythonDirective(Directive):' | |||||
898 | if not self.state.document.current_source in self.seen_docs: |
|
898 | if not self.state.document.current_source in self.seen_docs: | |
899 | self.shell.IP.history_manager.reset() |
|
899 | self.shell.IP.history_manager.reset() | |
900 | self.shell.IP.execution_count = 1 |
|
900 | self.shell.IP.execution_count = 1 | |
901 | self.shell.IP.prompt_manager.width = 0 |
|
|||
902 | self.seen_docs.add(self.state.document.current_source) |
|
901 | self.seen_docs.add(self.state.document.current_source) | |
903 |
|
902 | |||
904 | # and attach to shell so we don't have to pass them around |
|
903 | # and attach to shell so we don't have to pass them around |
@@ -614,7 +614,7 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
614 | self.hooks.pre_prompt_hook() |
|
614 | self.hooks.pre_prompt_hook() | |
615 | if more: |
|
615 | if more: | |
616 | try: |
|
616 | try: | |
617 |
prompt = |
|
617 | prompt = ' ...: ' | |
618 | except: |
|
618 | except: | |
619 | self.showtraceback() |
|
619 | self.showtraceback() | |
620 | if self.autoindent: |
|
620 | if self.autoindent: | |
@@ -622,7 +622,7 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
622 |
|
622 | |||
623 | else: |
|
623 | else: | |
624 | try: |
|
624 | try: | |
625 |
prompt = self.separate_in + self. |
|
625 | prompt = self.separate_in + 'In [{}]: '.format(self.execution_count) | |
626 | except: |
|
626 | except: | |
627 | self.showtraceback() |
|
627 | self.showtraceback() | |
628 | try: |
|
628 | try: |
@@ -24,7 +24,6 b' from IPython.core.completer import IPCompleter' | |||||
24 | from IPython.core.crashhandler import CrashHandler |
|
24 | from IPython.core.crashhandler import CrashHandler | |
25 | from IPython.core.formatters import PlainTextFormatter |
|
25 | from IPython.core.formatters import PlainTextFormatter | |
26 | from IPython.core.history import HistoryManager |
|
26 | from IPython.core.history import HistoryManager | |
27 | from IPython.core.prompts import PromptManager |
|
|||
28 | from IPython.core.application import ( |
|
27 | from IPython.core.application import ( | |
29 | ProfileDir, BaseIPythonApplication, base_flags, base_aliases |
|
28 | ProfileDir, BaseIPythonApplication, base_flags, base_aliases | |
30 | ) |
|
29 | ) | |
@@ -195,7 +194,6 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):' | |||||
195 | InteractiveShellApp, # ShellApp comes before TerminalApp, because |
|
194 | InteractiveShellApp, # ShellApp comes before TerminalApp, because | |
196 | self.__class__, # it will also affect subclasses (e.g. QtConsole) |
|
195 | self.__class__, # it will also affect subclasses (e.g. QtConsole) | |
197 | TerminalInteractiveShell, |
|
196 | TerminalInteractiveShell, | |
198 | PromptManager, |
|
|||
199 | HistoryManager, |
|
197 | HistoryManager, | |
200 | ProfileDir, |
|
198 | ProfileDir, | |
201 | PlainTextFormatter, |
|
199 | PlainTextFormatter, |
@@ -11,7 +11,7 b' from IPython.core.interactiveshell import InteractiveShell' | |||||
11 | from IPython.utils.py3compat import cast_unicode_py2, input |
|
11 | from IPython.utils.py3compat import cast_unicode_py2, input | |
12 | from IPython.utils.terminal import toggle_set_term_title, set_term_title |
|
12 | from IPython.utils.terminal import toggle_set_term_title, set_term_title | |
13 | from IPython.utils.process import abbrev_cwd |
|
13 | from IPython.utils.process import abbrev_cwd | |
14 | from traitlets import Bool, Unicode, Dict, Integer, observe |
|
14 | from traitlets import Bool, Unicode, Dict, Integer, observe, Instance | |
15 |
|
15 | |||
16 | from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode |
|
16 | from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode | |
17 | from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone |
|
17 | from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone | |
@@ -29,6 +29,7 b' from pygments.token import Token' | |||||
29 | from .debugger import TerminalPdb, Pdb |
|
29 | from .debugger import TerminalPdb, Pdb | |
30 | from .pt_inputhooks import get_inputhook_func |
|
30 | from .pt_inputhooks import get_inputhook_func | |
31 | from .interactiveshell import get_default_editor, TerminalMagics |
|
31 | from .interactiveshell import get_default_editor, TerminalMagics | |
|
32 | from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook | |||
32 | from .ptutils import IPythonPTCompleter, IPythonPTLexer |
|
33 | from .ptutils import IPythonPTCompleter, IPythonPTLexer | |
33 |
|
34 | |||
34 | _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty() |
|
35 | _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty() | |
@@ -98,7 +99,19 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
98 | editor = Unicode(get_default_editor(), |
|
99 | editor = Unicode(get_default_editor(), | |
99 | help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." |
|
100 | help="Set the editor used by IPython (default to $EDITOR/vi/notepad)." | |
100 | ).tag(config=True) |
|
101 | ).tag(config=True) | |
101 |
|
102 | |||
|
103 | prompts = Instance(Prompts) | |||
|
104 | ||||
|
105 | def _prompts_default(self): | |||
|
106 | return Prompts(self) | |||
|
107 | ||||
|
108 | @observe('prompts') | |||
|
109 | def _(self, change): | |||
|
110 | self._update_layout() | |||
|
111 | ||||
|
112 | def _displayhook_class_default(self): | |||
|
113 | return RichPromptDisplayHook | |||
|
114 | ||||
102 | term_title = Bool(True, |
|
115 | term_title = Bool(True, | |
103 | help="Automatically set the terminal title" |
|
116 | help="Automatically set the terminal title" | |
104 | ).tag(config=True) |
|
117 | ).tag(config=True) | |
@@ -120,18 +133,6 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
120 | else: |
|
133 | else: | |
121 | toggle_set_term_title(False) |
|
134 | toggle_set_term_title(False) | |
122 |
|
135 | |||
123 | def get_prompt_tokens(self, cli): |
|
|||
124 | return [ |
|
|||
125 | (Token.Prompt, 'In ['), |
|
|||
126 | (Token.PromptNum, str(self.execution_count)), |
|
|||
127 | (Token.Prompt, ']: '), |
|
|||
128 | ] |
|
|||
129 |
|
||||
130 | def get_continuation_tokens(self, cli, width): |
|
|||
131 | return [ |
|
|||
132 | (Token.Prompt, (' ' * (width - 5)) + '...: '), |
|
|||
133 | ] |
|
|||
134 |
|
||||
135 | def init_prompt_toolkit_cli(self): |
|
136 | def init_prompt_toolkit_cli(self): | |
136 | if self.simple_prompt: |
|
137 | if self.simple_prompt: | |
137 | # Fall back to plain non-interactive output for tests. |
|
138 | # Fall back to plain non-interactive output for tests. | |
@@ -234,6 +235,8 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
234 | style_overrides = { |
|
235 | style_overrides = { | |
235 | Token.Prompt: '#009900', |
|
236 | Token.Prompt: '#009900', | |
236 | Token.PromptNum: '#00ff00 bold', |
|
237 | Token.PromptNum: '#00ff00 bold', | |
|
238 | Token.OutPrompt: '#990000', | |||
|
239 | Token.OutPromptNum: '#ff0000 bold', | |||
237 | } |
|
240 | } | |
238 | if name == 'default': |
|
241 | if name == 'default': | |
239 | style_cls = get_style_by_name('default') |
|
242 | style_cls = get_style_by_name('default') | |
@@ -261,8 +264,8 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
261 | return { |
|
264 | return { | |
262 | 'lexer':IPythonPTLexer(), |
|
265 | 'lexer':IPythonPTLexer(), | |
263 | 'reserve_space_for_menu':self.space_for_menu, |
|
266 | 'reserve_space_for_menu':self.space_for_menu, | |
264 |
'get_prompt_tokens':self. |
|
267 | 'get_prompt_tokens':self.prompts.in_prompt_tokens, | |
265 |
'get_continuation_tokens':self. |
|
268 | 'get_continuation_tokens':self.prompts.continuation_prompt_tokens, | |
266 | 'multiline':True, |
|
269 | 'multiline':True, | |
267 | 'display_completions_in_columns': self.display_completions_in_columns, |
|
270 | 'display_completions_in_columns': self.display_completions_in_columns, | |
268 |
|
271 | |||
@@ -439,6 +442,29 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
439 | # work correctly. |
|
442 | # work correctly. | |
440 | system = InteractiveShell.system_raw |
|
443 | system = InteractiveShell.system_raw | |
441 |
|
444 | |||
|
445 | def auto_rewrite_input(self, cmd): | |||
|
446 | """Overridden from the parent class to use fancy rewriting prompt""" | |||
|
447 | if not self.show_rewritten_input: | |||
|
448 | return | |||
|
449 | ||||
|
450 | tokens = self.prompts.rewrite_prompt_tokens() | |||
|
451 | if self.pt_cli: | |||
|
452 | self.pt_cli.print_tokens(tokens) | |||
|
453 | print(cmd) | |||
|
454 | else: | |||
|
455 | prompt = ''.join(s for t, s in tokens) | |||
|
456 | print(prompt, cmd, sep='') | |||
|
457 | ||||
|
458 | _prompts_before = None | |||
|
459 | def switch_doctest_mode(self, mode): | |||
|
460 | """Switch prompts to classic for %doctest_mode""" | |||
|
461 | if mode: | |||
|
462 | self._prompts_before = self.prompts | |||
|
463 | self.prompts = ClassicPrompts(self) | |||
|
464 | elif self._prompts_before: | |||
|
465 | self.prompts = self._prompts_before | |||
|
466 | self._prompts_before = None | |||
|
467 | ||||
442 |
|
468 | |||
443 | if __name__ == '__main__': |
|
469 | if __name__ == '__main__': | |
444 | TerminalInteractiveShell.instance().interact() |
|
470 | TerminalInteractiveShell.instance().interact() |
@@ -202,13 +202,7 b' def ipexec(fname, options=None, commands=()):' | |||||
202 | """ |
|
202 | """ | |
203 | if options is None: options = [] |
|
203 | if options is None: options = [] | |
204 |
|
204 | |||
205 | # For these subprocess calls, eliminate all prompt printing so we only see |
|
205 | cmdargs = default_argv() + options | |
206 | # output from script execution |
|
|||
207 | prompt_opts = [ '--PromptManager.in_template=""', |
|
|||
208 | '--PromptManager.in2_template=""', |
|
|||
209 | '--PromptManager.out_template=""' |
|
|||
210 | ] |
|
|||
211 | cmdargs = default_argv() + prompt_opts + options |
|
|||
212 |
|
206 | |||
213 | test_dir = os.path.dirname(__file__) |
|
207 | test_dir = os.path.dirname(__file__) | |
214 |
|
208 |
@@ -2,52 +2,6 b'' | |||||
2 | Specific config details |
|
2 | Specific config details | |
3 | ======================= |
|
3 | ======================= | |
4 |
|
4 | |||
5 | Prompts |
|
|||
6 | ======= |
|
|||
7 |
|
||||
8 | In the terminal, the format of the input and output prompts can be |
|
|||
9 | customised. This does not currently affect other frontends. |
|
|||
10 |
|
||||
11 | The following codes in the prompt string will be substituted into the |
|
|||
12 | prompt string: |
|
|||
13 |
|
||||
14 | ====== =================================== ===================================================== |
|
|||
15 | Short Long Notes |
|
|||
16 | ====== =================================== ===================================================== |
|
|||
17 | %n,\\# {color.number}{count}{color.prompt} history counter with bolding |
|
|||
18 | \\N {count} history counter without bolding |
|
|||
19 | \\D {dots} series of dots the same width as the history counter |
|
|||
20 | \\T {time} current time |
|
|||
21 | \\w {cwd} current working directory |
|
|||
22 | \\W {cwd_last} basename of CWD |
|
|||
23 | \\Xn {cwd_x[n]} Show the last n terms of the CWD. n=0 means show all. |
|
|||
24 | \\Yn {cwd_y[n]} Like \Xn, but show '~' for $HOME |
|
|||
25 | \\h hostname, up to the first '.' |
|
|||
26 | \\H full hostname |
|
|||
27 | \\u username (from the $USER environment variable) |
|
|||
28 | \\v IPython version |
|
|||
29 | \\$ root symbol ("$" for normal user or "#" for root) |
|
|||
30 | ``\\`` escaped '\\' |
|
|||
31 | \\n newline |
|
|||
32 | \\r carriage return |
|
|||
33 | n/a {color.<Name>} set terminal colour - see below for list of names |
|
|||
34 | ====== =================================== ===================================================== |
|
|||
35 |
|
||||
36 | Available colour names are: Black, BlinkBlack, BlinkBlue, BlinkCyan, |
|
|||
37 | BlinkGreen, BlinkLightGray, BlinkPurple, BlinkRed, BlinkYellow, Blue, |
|
|||
38 | Brown, Cyan, DarkGray, Green, LightBlue, LightCyan, LightGray, LightGreen, |
|
|||
39 | LightPurple, LightRed, Purple, Red, White, Yellow. The selected colour |
|
|||
40 | scheme also defines the names *prompt* and *number*. Finally, the name |
|
|||
41 | *normal* resets the terminal to its default colour. |
|
|||
42 |
|
||||
43 | So, this config:: |
|
|||
44 |
|
||||
45 | c.PromptManager.in_template = "{color.LightGreen}{time}{color.Yellow} \u{color.normal}>>>" |
|
|||
46 |
|
||||
47 | will produce input prompts with the time in light green, your username |
|
|||
48 | in yellow, and a ``>>>`` prompt in the default terminal colour. |
|
|||
49 |
|
||||
50 |
|
||||
51 | .. _termcolour: |
|
5 | .. _termcolour: | |
52 |
|
6 | |||
53 | Terminal Colors |
|
7 | Terminal Colors | |
@@ -84,27 +38,10 b' These have shown problems:' | |||||
84 | extensions. Once Gary's readline library is installed, the normal |
|
38 | extensions. Once Gary's readline library is installed, the normal | |
85 | WinXP/2k command prompt works perfectly. |
|
39 | WinXP/2k command prompt works perfectly. | |
86 |
|
40 | |||
87 | Currently the following color schemes are available: |
|
|||
88 |
|
||||
89 | * NoColor: uses no color escapes at all (all escapes are empty '' '' |
|
|||
90 | strings). This 'scheme' is thus fully safe to use in any terminal. |
|
|||
91 | * Linux: works well in Linux console type environments: dark |
|
|||
92 | background with light fonts. It uses bright colors for |
|
|||
93 | information, so it is difficult to read if you have a light |
|
|||
94 | colored background. |
|
|||
95 | * LightBG: the basic colors are similar to those in the Linux scheme |
|
|||
96 | but darker. It is easy to read in terminals with light backgrounds. |
|
|||
97 |
|
||||
98 | IPython uses colors for two main groups of things: prompts and |
|
41 | IPython uses colors for two main groups of things: prompts and | |
99 | tracebacks which are directly printed to the terminal, and the object |
|
42 | tracebacks which are directly printed to the terminal, and the object | |
100 | introspection system which passes large sets of data through a pager. |
|
43 | introspection system which passes large sets of data through a pager. | |
101 |
|
44 | |||
102 | If you are seeing garbage sequences in your terminal and no colour, you |
|
|||
103 | may need to disable colours: run ``%colors NoColor`` inside IPython, or |
|
|||
104 | add this to a config file:: |
|
|||
105 |
|
||||
106 | c.InteractiveShell.colors = 'NoColor' |
|
|||
107 |
|
||||
108 | Colors in the pager |
|
45 | Colors in the pager | |
109 | ------------------- |
|
46 | ------------------- | |
110 |
|
47 |
@@ -73,11 +73,6 b' Example config file' | |||||
73 | c.InteractiveShell.editor = 'nano' |
|
73 | c.InteractiveShell.editor = 'nano' | |
74 | c.InteractiveShell.xmode = 'Context' |
|
74 | c.InteractiveShell.xmode = 'Context' | |
75 |
|
75 | |||
76 | c.PromptManager.in_template = 'In [\#]: ' |
|
|||
77 | c.PromptManager.in2_template = ' .\D.: ' |
|
|||
78 | c.PromptManager.out_template = 'Out[\#]: ' |
|
|||
79 | c.PromptManager.justify = True |
|
|||
80 |
|
||||
81 | c.PrefilterManager.multi_line_specials = True |
|
76 | c.PrefilterManager.multi_line_specials = True | |
82 |
|
77 | |||
83 | c.AliasManager.user_aliases = [ |
|
78 | c.AliasManager.user_aliases = [ |
@@ -63,20 +63,46 b' switching to any of them. Type ``cd?`` for more details.' | |||||
63 | Prompt customization |
|
63 | Prompt customization | |
64 | ==================== |
|
64 | ==================== | |
65 |
|
65 | |||
66 | Here are some prompt configurations you can try out interactively by using the |
|
66 | Starting at IPython 5.0 the prompt customisation is done by subclassing :class:`IPython.terminal.prompts.Prompt`. | |
67 | ``%config`` magic:: |
|
67 | ||
68 |
|
68 | The custom ``Prompt`` receive the current IPython shell instance as first | ||
69 | %config PromptManager.in_template = r'{color.LightGreen}\u@\h{color.LightBlue}[{color.LightCyan}\Y1{color.LightBlue}]{color.Green}|\#> ' |
|
69 | argument, which by default is stored as the ``shell`` attribute of the object. | |
70 | %config PromptManager.in2_template = r'{color.Green}|{color.LightGreen}\D{color.Green}> ' |
|
70 | The class can implement optional methods for each of the available prompt types: | |
71 | %config PromptManager.out_template = r'<\#> ' |
|
71 | ||
72 |
|
72 | - ``in_prompt_tokens(self, cli=None)``, input prompt , default to ``In [1]`` | ||
73 |
|
73 | - ``continuation_prompt_tokens(self, cli=None, width=None)``, continuation prompt for multi lines (default `...:`) | ||
74 | You can change the prompt configuration to your liking permanently by editing |
|
74 | - ``rewrite_prompt_tokens(self)`` | |
75 | ``ipython_config.py``:: |
|
75 | - ``out_prompt_tokens(self)`` | |
76 |
|
76 | |||
77 | c.PromptManager.in_template = r'{color.LightGreen}\u@\h{color.LightBlue}[{color.LightCyan}\Y1{color.LightBlue}]{color.Green}|\#> ' |
|
77 | Each of these methods should return a list of `(TokenType, Token)` pairs. See documentation of `prompt_toolkit` and/or `Pygments`. | |
78 | c.PromptManager.in2_template = r'{color.Green}|{color.LightGreen}\D{color.Green}> ' |
|
78 | ||
79 | c.PromptManager.out_template = r'<\#> ' |
|
79 | Here is an example of Prompt class that will insert the current working directory in front of a prompt: | |
|
80 | ||||
|
81 | ||||
|
82 | .. codeblock:: python | |||
|
83 | ||||
|
84 | from IPython.terminal.prompts import Prompts, Token | |||
|
85 | import os | |||
|
86 | ||||
|
87 | class MyPrompt(Prompts): | |||
|
88 | ||||
|
89 | def in_prompt_tokens(self, cli=None): | |||
|
90 | return [(Token, os.getcwd()), | |||
|
91 | (Token.Prompt, ' >>>')] | |||
|
92 | ||||
|
93 | To set the new prompt, assign it to the `prompts` attribute of the IPython shell: | |||
|
94 | ||||
|
95 | .. codeblock:: python | |||
|
96 | ||||
|
97 | In[2]: ip = get_ipython() | |||
|
98 | ...: ip.prompts = MyPrompt(ip) | |||
|
99 | ||||
|
100 | ~/ >>> # it works | |||
|
101 | ||||
|
102 | ||||
|
103 | See ``IPython/example/utils/cwd_prompt.py`` for an example of how to write an | |||
|
104 | extensions that customise prompts. | |||
|
105 | ||||
80 |
|
106 | |||
81 | Read more about the :ref:`configuration system <config_overview>` for details |
|
107 | Read more about the :ref:`configuration system <config_overview>` for details | |
82 | on how to find ``ipython_config.py``. |
|
108 | on how to find ``ipython_config.py``. |
General Comments 0
You need to be logged in to leave comments.
Login now