##// END OF EJS Templates
Upgrade to prompt_toolkit 2.0
Jonathan Slenders -
Show More
@@ -1,124 +1,117 b''
1 import signal
1 import signal
2 import sys
2 import sys
3
3
4 from IPython.core.debugger import Pdb
4 from IPython.core.debugger import Pdb
5
5
6 from IPython.core.completer import IPCompleter
6 from IPython.core.completer import IPCompleter
7 from .ptutils import IPythonPTCompleter
7 from .ptutils import IPythonPTCompleter
8 from .shortcuts import suspend_to_bg, cursor_in_leading_ws
8 from .shortcuts import suspend_to_bg, cursor_in_leading_ws
9
9
10 from prompt_toolkit.enums import DEFAULT_BUFFER
10 from prompt_toolkit.enums import DEFAULT_BUFFER
11 from prompt_toolkit.filters import (Condition, HasFocus, HasSelection,
11 from prompt_toolkit.filters import (Condition, has_focus, has_selection,
12 ViInsertMode, EmacsInsertMode)
12 vi_insert_mode, emacs_insert_mode)
13 from prompt_toolkit.keys import Keys
13 from prompt_toolkit.key_binding import KeyBindings
14 from prompt_toolkit.key_binding.manager import KeyBindingManager
15 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
14 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
16 from prompt_toolkit.token import Token
15 from pygments.token import Token
17 from prompt_toolkit.shortcuts import create_prompt_application
16 from prompt_toolkit.shortcuts.prompt import PromptSession
18 from prompt_toolkit.interface import CommandLineInterface
19 from prompt_toolkit.enums import EditingMode
17 from prompt_toolkit.enums import EditingMode
18 from prompt_toolkit.formatted_text import PygmentsTokens
20
19
21
20
22 class TerminalPdb(Pdb):
21 class TerminalPdb(Pdb):
23 def __init__(self, *args, **kwargs):
22 def __init__(self, *args, **kwargs):
24 Pdb.__init__(self, *args, **kwargs)
23 Pdb.__init__(self, *args, **kwargs)
25 self._ptcomp = None
24 self._ptcomp = None
26 self.pt_init()
25 self.pt_init()
27
26
28 def pt_init(self):
27 def pt_init(self):
29 def get_prompt_tokens(cli):
28 def get_prompt_tokens():
30 return [(Token.Prompt, self.prompt)]
29 return [(Token.Prompt, self.prompt)]
31
30
32 def patch_stdout(**kwargs):
33 return self.pt_cli.patch_stdout_context(**kwargs)
34
35 if self._ptcomp is None:
31 if self._ptcomp is None:
36 compl = IPCompleter(shell=self.shell,
32 compl = IPCompleter(shell=self.shell,
37 namespace={},
33 namespace={},
38 global_namespace={},
34 global_namespace={},
39 parent=self.shell,
35 parent=self.shell,
40 )
36 )
41 self._ptcomp = IPythonPTCompleter(compl, patch_stdout=patch_stdout)
37 self._ptcomp = IPythonPTCompleter(compl)
42
38
43 kbmanager = KeyBindingManager.for_prompt()
39 kb = KeyBindings()
44 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
40 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
45 kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend
41 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
46 )(suspend_to_bg)
47
42
48 if self.shell.display_completions == 'readlinelike':
43 if self.shell.display_completions == 'readlinelike':
49 kbmanager.registry.add_binding(Keys.ControlI,
44 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
50 filter=(HasFocus(DEFAULT_BUFFER)
45 & ~has_selection
51 & ~HasSelection()
46 & vi_insert_mode | emacs_insert_mode
52 & ViInsertMode() | EmacsInsertMode()
47 & ~cursor_in_leading_ws
53 & ~cursor_in_leading_ws
48 ))(display_completions_like_readline)
54 ))(display_completions_like_readline)
49
55 multicolumn = (self.shell.display_completions == 'multicolumn')
50 self.pt_app = PromptSession(
56
51 message=(lambda: PygmentsTokens(get_prompt_tokens())),
57 self._pt_app = create_prompt_application(
58 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
52 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
59 key_bindings_registry=kbmanager.registry,
53 key_bindings=kb,
60 history=self.shell.debugger_history,
54 history=self.shell.debugger_history,
61 completer= self._ptcomp,
55 completer=self._ptcomp,
62 enable_history_search=True,
56 enable_history_search=True,
63 mouse_support=self.shell.mouse_support,
57 mouse_support=self.shell.mouse_support,
64 get_prompt_tokens=get_prompt_tokens,
58 complete_style=self.shell.pt_complete_style,
65 display_completions_in_columns=multicolumn,
59 style=self.shell.style,
66 style=self.shell.style
60 inputhook=self.shell.inputhook,
67 )
61 )
68 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
69
62
70 def cmdloop(self, intro=None):
63 def cmdloop(self, intro=None):
71 """Repeatedly issue a prompt, accept input, parse an initial prefix
64 """Repeatedly issue a prompt, accept input, parse an initial prefix
72 off the received input, and dispatch to action methods, passing them
65 off the received input, and dispatch to action methods, passing them
73 the remainder of the line as argument.
66 the remainder of the line as argument.
74
67
75 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
68 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
76 """
69 """
77 if not self.use_rawinput:
70 if not self.use_rawinput:
78 raise ValueError('Sorry ipdb does not support use_rawinput=False')
71 raise ValueError('Sorry ipdb does not support use_rawinput=False')
79
72
80 self.preloop()
73 self.preloop()
81
74
82 try:
75 try:
83 if intro is not None:
76 if intro is not None:
84 self.intro = intro
77 self.intro = intro
85 if self.intro:
78 if self.intro:
86 self.stdout.write(str(self.intro)+"\n")
79 self.stdout.write(str(self.intro)+"\n")
87 stop = None
80 stop = None
88 while not stop:
81 while not stop:
89 if self.cmdqueue:
82 if self.cmdqueue:
90 line = self.cmdqueue.pop(0)
83 line = self.cmdqueue.pop(0)
91 else:
84 else:
92 self._ptcomp.ipy_completer.namespace = self.curframe_locals
85 self._ptcomp.ipy_completer.namespace = self.curframe_locals
93 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
86 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
94 try:
87 try:
95 line = self.pt_cli.run(reset_current_buffer=True).text
88 line = self.pt_app.prompt() # reset_current_buffer=True)
96 except EOFError:
89 except EOFError:
97 line = 'EOF'
90 line = 'EOF'
98 line = self.precmd(line)
91 line = self.precmd(line)
99 stop = self.onecmd(line)
92 stop = self.onecmd(line)
100 stop = self.postcmd(stop, line)
93 stop = self.postcmd(stop, line)
101 self.postloop()
94 self.postloop()
102 except Exception:
95 except Exception:
103 raise
96 raise
104
97
105
98
106 def set_trace(frame=None):
99 def set_trace(frame=None):
107 """
100 """
108 Start debugging from `frame`.
101 Start debugging from `frame`.
109
102
110 If frame is not specified, debugging starts from caller's frame.
103 If frame is not specified, debugging starts from caller's frame.
111 """
104 """
112 TerminalPdb().set_trace(frame or sys._getframe().f_back)
105 TerminalPdb().set_trace(frame or sys._getframe().f_back)
113
106
114
107
115 if __name__ == '__main__':
108 if __name__ == '__main__':
116 import pdb
109 import pdb
117 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
110 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
118 # bdb.BdbQuit. When started through __main__ and an exception
111 # bdb.BdbQuit. When started through __main__ and an exception
119 # happened after hitting "c", this is needed in order to
112 # happened after hitting "c", this is needed in order to
120 # be able to quit the debugging session (see #9950).
113 # be able to quit the debugging session (see #9950).
121 old_trace_dispatch = pdb.Pdb.trace_dispatch
114 old_trace_dispatch = pdb.Pdb.trace_dispatch
122 pdb.Pdb = TerminalPdb
115 pdb.Pdb = TerminalPdb
123 pdb.Pdb.trace_dispatch = old_trace_dispatch
116 pdb.Pdb.trace_dispatch = old_trace_dispatch
124 pdb.main()
117 pdb.main()
@@ -1,542 +1,535 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2
2
3 import os
3 import os
4 import sys
4 import sys
5 import warnings
5 import warnings
6 from warnings import warn
6 from warnings import warn
7
7
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.utils import io
9 from IPython.utils import io
10 from IPython.utils.py3compat import input
10 from IPython.utils.py3compat import input
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.process import abbrev_cwd
12 from IPython.utils.process import abbrev_cwd
13 from traitlets import (
13 from traitlets import (
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 Any,
15 Any,
16 )
16 )
17
17
18 from prompt_toolkit.document import Document
18 from prompt_toolkit.document import Document
19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
21 from prompt_toolkit.formatted_text import PygmentsTokens
21 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
23 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle
23 from prompt_toolkit.interface import CommandLineInterface
24 from prompt_toolkit.output.defaults import create_output
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
25 from prompt_toolkit.key_binding import KeyBindings
25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
26 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
27 from prompt_toolkit.patch_stdout import patch_stdout
28 from prompt_toolkit.styles import DynamicStyle, merge_styles
29 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
27
30
28 from pygments.styles import get_style_by_name
31 from pygments.styles import get_style_by_name
29 from pygments.style import Style
32 from pygments.style import Style
30 from pygments.token import Token
33 from pygments.token import Token
31
34
32 from .debugger import TerminalPdb, Pdb
35 from .debugger import TerminalPdb, Pdb
33 from .magics import TerminalMagics
36 from .magics import TerminalMagics
34 from .pt_inputhooks import get_inputhook_name_and_func
37 from .pt_inputhooks import get_inputhook_name_and_func
35 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
38 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 from .ptutils import IPythonPTCompleter, IPythonPTLexer
39 from .ptutils import IPythonPTCompleter, IPythonPTLexer
37 from .shortcuts import register_ipython_shortcuts
40 from .shortcuts import create_ipython_shortcuts
38
41
39 DISPLAY_BANNER_DEPRECATED = object()
42 DISPLAY_BANNER_DEPRECATED = object()
40
43
41
44
42 class _NoStyle(Style): pass
45 class _NoStyle(Style): pass
43
46
44
47
45
48
46 _style_overrides_light_bg = {
49 _style_overrides_light_bg = {
47 Token.Prompt: '#0000ff',
50 Token.Prompt: '#0000ff',
48 Token.PromptNum: '#0000ee bold',
51 Token.PromptNum: '#0000ee bold',
49 Token.OutPrompt: '#cc0000',
52 Token.OutPrompt: '#cc0000',
50 Token.OutPromptNum: '#bb0000 bold',
53 Token.OutPromptNum: '#bb0000 bold',
51 }
54 }
52
55
53 _style_overrides_linux = {
56 _style_overrides_linux = {
54 Token.Prompt: '#00cc00',
57 Token.Prompt: '#00cc00',
55 Token.PromptNum: '#00bb00 bold',
58 Token.PromptNum: '#00bb00 bold',
56 Token.OutPrompt: '#cc0000',
59 Token.OutPrompt: '#cc0000',
57 Token.OutPromptNum: '#bb0000 bold',
60 Token.OutPromptNum: '#bb0000 bold',
58 }
61 }
59
62
60 def get_default_editor():
63 def get_default_editor():
61 try:
64 try:
62 return os.environ['EDITOR']
65 return os.environ['EDITOR']
63 except KeyError:
66 except KeyError:
64 pass
67 pass
65 except UnicodeError:
68 except UnicodeError:
66 warn("$EDITOR environment variable is not pure ASCII. Using platform "
69 warn("$EDITOR environment variable is not pure ASCII. Using platform "
67 "default editor.")
70 "default editor.")
68
71
69 if os.name == 'posix':
72 if os.name == 'posix':
70 return 'vi' # the only one guaranteed to be there!
73 return 'vi' # the only one guaranteed to be there!
71 else:
74 else:
72 return 'notepad' # same in Windows!
75 return 'notepad' # same in Windows!
73
76
74 # conservatively check for tty
77 # conservatively check for tty
75 # overridden streams can result in things like:
78 # overridden streams can result in things like:
76 # - sys.stdin = None
79 # - sys.stdin = None
77 # - no isatty method
80 # - no isatty method
78 for _name in ('stdin', 'stdout', 'stderr'):
81 for _name in ('stdin', 'stdout', 'stderr'):
79 _stream = getattr(sys, _name)
82 _stream = getattr(sys, _name)
80 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
83 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
81 _is_tty = False
84 _is_tty = False
82 break
85 break
83 else:
86 else:
84 _is_tty = True
87 _is_tty = True
85
88
86
89
87 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
90 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
88
91
89 class TerminalInteractiveShell(InteractiveShell):
92 class TerminalInteractiveShell(InteractiveShell):
90 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
93 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
91 'to reserve for the completion menu'
94 'to reserve for the completion menu'
92 ).tag(config=True)
95 ).tag(config=True)
93
96
94 def _space_for_menu_changed(self, old, new):
97 # def _space_for_menu_changed(self, old, new):
95 self._update_layout()
98 # self._update_layout()
96
99
97 pt_cli = None
100 pt_app = None
98 debugger_history = None
101 debugger_history = None
99 _pt_app = None
100
102
101 simple_prompt = Bool(_use_simple_prompt,
103 simple_prompt = Bool(_use_simple_prompt,
102 help="""Use `raw_input` for the REPL, without completion and prompt colors.
104 help="""Use `raw_input` for the REPL, without completion and prompt colors.
103
105
104 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
106 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
105 IPython own testing machinery, and emacs inferior-shell integration through elpy.
107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
106
108
107 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
108 environment variable is set, or the current terminal is not a tty."""
110 environment variable is set, or the current terminal is not a tty."""
109 ).tag(config=True)
111 ).tag(config=True)
110
112
111 @property
113 @property
112 def debugger_cls(self):
114 def debugger_cls(self):
113 return Pdb if self.simple_prompt else TerminalPdb
115 return Pdb if self.simple_prompt else TerminalPdb
114
116
115 confirm_exit = Bool(True,
117 confirm_exit = Bool(True,
116 help="""
118 help="""
117 Set to confirm when you try to exit IPython with an EOF (Control-D
119 Set to confirm when you try to exit IPython with an EOF (Control-D
118 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
120 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
119 you can force a direct exit without any confirmation.""",
121 you can force a direct exit without any confirmation.""",
120 ).tag(config=True)
122 ).tag(config=True)
121
123
122 editing_mode = Unicode('emacs',
124 editing_mode = Unicode('emacs',
123 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
125 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
124 ).tag(config=True)
126 ).tag(config=True)
125
127
126 mouse_support = Bool(False,
128 mouse_support = Bool(False,
127 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
129 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
128 ).tag(config=True)
130 ).tag(config=True)
129
131
130 # We don't load the list of styles for the help string, because loading
132 # We don't load the list of styles for the help string, because loading
131 # Pygments plugins takes time and can cause unexpected errors.
133 # Pygments plugins takes time and can cause unexpected errors.
132 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
134 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
133 help="""The name or class of a Pygments style to use for syntax
135 help="""The name or class of a Pygments style to use for syntax
134 highlighting. To see available styles, run `pygmentize -L styles`."""
136 highlighting. To see available styles, run `pygmentize -L styles`."""
135 ).tag(config=True)
137 ).tag(config=True)
136
138
137
139
138 @observe('highlighting_style')
140 @observe('highlighting_style')
139 @observe('colors')
141 @observe('colors')
140 def _highlighting_style_changed(self, change):
142 def _highlighting_style_changed(self, change):
141 self.refresh_style()
143 self.refresh_style()
142
144
143 def refresh_style(self):
145 def refresh_style(self):
144 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
146 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
145
147
146
148
147 highlighting_style_overrides = Dict(
149 highlighting_style_overrides = Dict(
148 help="Override highlighting format for specific tokens"
150 help="Override highlighting format for specific tokens"
149 ).tag(config=True)
151 ).tag(config=True)
150
152
151 true_color = Bool(False,
153 true_color = Bool(False,
152 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
154 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
153 "If your terminal supports true color, the following command "
155 "If your terminal supports true color, the following command "
154 "should print 'TRUECOLOR' in orange: "
156 "should print 'TRUECOLOR' in orange: "
155 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
157 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
156 ).tag(config=True)
158 ).tag(config=True)
157
159
158 editor = Unicode(get_default_editor(),
160 editor = Unicode(get_default_editor(),
159 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
161 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
160 ).tag(config=True)
162 ).tag(config=True)
161
163
162 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
164 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
163
165
164 prompts = Instance(Prompts)
166 prompts = Instance(Prompts)
165
167
166 @default('prompts')
168 @default('prompts')
167 def _prompts_default(self):
169 def _prompts_default(self):
168 return self.prompts_class(self)
170 return self.prompts_class(self)
169
171
170 @observe('prompts')
172 # @observe('prompts')
171 def _(self, change):
173 # def _(self, change):
172 self._update_layout()
174 # self._update_layout()
173
175
174 @default('displayhook_class')
176 @default('displayhook_class')
175 def _displayhook_class_default(self):
177 def _displayhook_class_default(self):
176 return RichPromptDisplayHook
178 return RichPromptDisplayHook
177
179
178 term_title = Bool(True,
180 term_title = Bool(True,
179 help="Automatically set the terminal title"
181 help="Automatically set the terminal title"
180 ).tag(config=True)
182 ).tag(config=True)
181
183
182 term_title_format = Unicode("IPython: {cwd}",
184 term_title_format = Unicode("IPython: {cwd}",
183 help="Customize the terminal title format. This is a python format string. " +
185 help="Customize the terminal title format. This is a python format string. " +
184 "Available substitutions are: {cwd}."
186 "Available substitutions are: {cwd}."
185 ).tag(config=True)
187 ).tag(config=True)
186
188
187 display_completions = Enum(('column', 'multicolumn','readlinelike'),
189 display_completions = Enum(('column', 'multicolumn','readlinelike'),
188 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
190 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
189 "'readlinelike'. These options are for `prompt_toolkit`, see "
191 "'readlinelike'. These options are for `prompt_toolkit`, see "
190 "`prompt_toolkit` documentation for more information."
192 "`prompt_toolkit` documentation for more information."
191 ),
193 ),
192 default_value='multicolumn').tag(config=True)
194 default_value='multicolumn').tag(config=True)
193
195
194 highlight_matching_brackets = Bool(True,
196 highlight_matching_brackets = Bool(True,
195 help="Highlight matching brackets.",
197 help="Highlight matching brackets.",
196 ).tag(config=True)
198 ).tag(config=True)
197
199
198 extra_open_editor_shortcuts = Bool(False,
200 extra_open_editor_shortcuts = Bool(False,
199 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
201 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
200 "This is in addition to the F2 binding, which is always enabled."
202 "This is in addition to the F2 binding, which is always enabled."
201 ).tag(config=True)
203 ).tag(config=True)
202
204
203 handle_return = Any(None,
205 handle_return = Any(None,
204 help="Provide an alternative handler to be called when the user presses "
206 help="Provide an alternative handler to be called when the user presses "
205 "Return. This is an advanced option intended for debugging, which "
207 "Return. This is an advanced option intended for debugging, which "
206 "may be changed or removed in later releases."
208 "may be changed or removed in later releases."
207 ).tag(config=True)
209 ).tag(config=True)
208
210
209 enable_history_search = Bool(True,
211 enable_history_search = Bool(True,
210 help="Allows to enable/disable the prompt toolkit history search"
212 help="Allows to enable/disable the prompt toolkit history search"
211 ).tag(config=True)
213 ).tag(config=True)
212
214
213 @observe('term_title')
215 @observe('term_title')
214 def init_term_title(self, change=None):
216 def init_term_title(self, change=None):
215 # Enable or disable the terminal title.
217 # Enable or disable the terminal title.
216 if self.term_title:
218 if self.term_title:
217 toggle_set_term_title(True)
219 toggle_set_term_title(True)
218 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
220 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
219 else:
221 else:
220 toggle_set_term_title(False)
222 toggle_set_term_title(False)
221
223
222 def init_display_formatter(self):
224 def init_display_formatter(self):
223 super(TerminalInteractiveShell, self).init_display_formatter()
225 super(TerminalInteractiveShell, self).init_display_formatter()
224 # terminal only supports plain text
226 # terminal only supports plain text
225 self.display_formatter.active_types = ['text/plain']
227 self.display_formatter.active_types = ['text/plain']
226 # disable `_ipython_display_`
228 # disable `_ipython_display_`
227 self.display_formatter.ipython_display_formatter.enabled = False
229 self.display_formatter.ipython_display_formatter.enabled = False
228
230
229 def init_prompt_toolkit_cli(self):
231 def init_prompt_toolkit_cli(self):
230 if self.simple_prompt:
232 if self.simple_prompt:
231 # Fall back to plain non-interactive output for tests.
233 # Fall back to plain non-interactive output for tests.
232 # This is very limited, and only accepts a single line.
234 # This is very limited, and only accepts a single line.
233 def prompt():
235 def prompt():
234 isp = self.input_splitter
236 isp = self.input_splitter
235 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
237 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
236 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
238 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
237 while isp.push_accepts_more():
239 while isp.push_accepts_more():
238 line = input(prompt_text)
240 line = input(prompt_text)
239 isp.push(line)
241 isp.push(line)
240 prompt_text = prompt_continuation
242 prompt_text = prompt_continuation
241 return isp.source_reset()
243 return isp.source_reset()
242 self.prompt_for_code = prompt
244 self.prompt_for_code = prompt
243 return
245 return
244
246
245 # Set up keyboard shortcuts
247 # Set up keyboard shortcuts
246 kbmanager = KeyBindingManager.for_prompt(
248 key_bindings = create_ipython_shortcuts(self)
247 enable_open_in_editor=self.extra_open_editor_shortcuts,
248 )
249 register_ipython_shortcuts(kbmanager.registry, self)
250
249
251 # Pre-populate history from IPython's history database
250 # Pre-populate history from IPython's history database
252 history = InMemoryHistory()
251 history = InMemoryHistory()
253 last_cell = u""
252 last_cell = u""
254 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
253 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
255 include_latest=True):
254 include_latest=True):
256 # Ignore blank lines and consecutive duplicates
255 # Ignore blank lines and consecutive duplicates
257 cell = cell.rstrip()
256 cell = cell.rstrip()
258 if cell and (cell != last_cell):
257 if cell and (cell != last_cell):
259 history.append(cell)
258 history.append_string(cell)
260 last_cell = cell
259 last_cell = cell
261
260
262 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
261 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
263 self.style = DynamicStyle(lambda: self._style)
262 self.style = DynamicStyle(lambda: self._style)
264
263
265 editing_mode = getattr(EditingMode, self.editing_mode.upper())
264 editing_mode = getattr(EditingMode, self.editing_mode.upper())
266
265
267 def patch_stdout(**kwargs):
266 self.pt_app = PromptSession(
268 return self.pt_cli.patch_stdout_context(**kwargs)
269
270 self._pt_app = create_prompt_application(
271 editing_mode=editing_mode,
267 editing_mode=editing_mode,
272 key_bindings_registry=kbmanager.registry,
268 key_bindings=key_bindings,
273 history=history,
269 history=history,
274 completer=IPythonPTCompleter(shell=self,
270 completer=IPythonPTCompleter(shell=self),
275 patch_stdout=patch_stdout),
271 enable_history_search = self.enable_history_search,
276 enable_history_search=self.enable_history_search,
277 style=self.style,
272 style=self.style,
273 include_default_pygments_style=False,
278 mouse_support=self.mouse_support,
274 mouse_support=self.mouse_support,
279 **self._layout_options()
275 enable_open_in_editor=self.extra_open_editor_shortcuts,
280 )
276 color_depth=(ColorDepth.TRUE_COLOR if self.true_color else None),
281 self._eventloop = create_eventloop(self.inputhook)
277 **self._extra_prompt_options())
282 self.pt_cli = CommandLineInterface(
283 self._pt_app, eventloop=self._eventloop,
284 output=create_output(true_color=self.true_color))
285
278
286 def _make_style_from_name_or_cls(self, name_or_cls):
279 def _make_style_from_name_or_cls(self, name_or_cls):
287 """
280 """
288 Small wrapper that make an IPython compatible style from a style name
281 Small wrapper that make an IPython compatible style from a style name
289
282
290 We need that to add style for prompt ... etc.
283 We need that to add style for prompt ... etc.
291 """
284 """
292 style_overrides = {}
285 style_overrides = {}
293 if name_or_cls == 'legacy':
286 if name_or_cls == 'legacy':
294 legacy = self.colors.lower()
287 legacy = self.colors.lower()
295 if legacy == 'linux':
288 if legacy == 'linux':
296 style_cls = get_style_by_name('monokai')
289 style_cls = get_style_by_name('monokai')
297 style_overrides = _style_overrides_linux
290 style_overrides = _style_overrides_linux
298 elif legacy == 'lightbg':
291 elif legacy == 'lightbg':
299 style_overrides = _style_overrides_light_bg
292 style_overrides = _style_overrides_light_bg
300 style_cls = get_style_by_name('pastie')
293 style_cls = get_style_by_name('pastie')
301 elif legacy == 'neutral':
294 elif legacy == 'neutral':
302 # The default theme needs to be visible on both a dark background
295 # The default theme needs to be visible on both a dark background
303 # and a light background, because we can't tell what the terminal
296 # and a light background, because we can't tell what the terminal
304 # looks like. These tweaks to the default theme help with that.
297 # looks like. These tweaks to the default theme help with that.
305 style_cls = get_style_by_name('default')
298 style_cls = get_style_by_name('default')
306 style_overrides.update({
299 style_overrides.update({
307 Token.Number: '#007700',
300 Token.Number: '#007700',
308 Token.Operator: 'noinherit',
301 Token.Operator: 'noinherit',
309 Token.String: '#BB6622',
302 Token.String: '#BB6622',
310 Token.Name.Function: '#2080D0',
303 Token.Name.Function: '#2080D0',
311 Token.Name.Class: 'bold #2080D0',
304 Token.Name.Class: 'bold #2080D0',
312 Token.Name.Namespace: 'bold #2080D0',
305 Token.Name.Namespace: 'bold #2080D0',
313 Token.Prompt: '#009900',
306 Token.Prompt: '#009900',
314 Token.PromptNum: '#00ff00 bold',
307 Token.PromptNum: '#00ff00 bold',
315 Token.OutPrompt: '#990000',
308 Token.OutPrompt: '#990000',
316 Token.OutPromptNum: '#ff0000 bold',
309 Token.OutPromptNum: '#ff0000 bold',
317 })
310 })
318
311
319 # Hack: Due to limited color support on the Windows console
312 # Hack: Due to limited color support on the Windows console
320 # the prompt colors will be wrong without this
313 # the prompt colors will be wrong without this
321 if os.name == 'nt':
314 if os.name == 'nt':
322 style_overrides.update({
315 style_overrides.update({
323 Token.Prompt: '#ansidarkgreen',
316 Token.Prompt: '#ansidarkgreen',
324 Token.PromptNum: '#ansigreen bold',
317 Token.PromptNum: '#ansigreen bold',
325 Token.OutPrompt: '#ansidarkred',
318 Token.OutPrompt: '#ansidarkred',
326 Token.OutPromptNum: '#ansired bold',
319 Token.OutPromptNum: '#ansired bold',
327 })
320 })
328 elif legacy =='nocolor':
321 elif legacy =='nocolor':
329 style_cls=_NoStyle
322 style_cls=_NoStyle
330 style_overrides = {}
323 style_overrides = {}
331 else :
324 else :
332 raise ValueError('Got unknown colors: ', legacy)
325 raise ValueError('Got unknown colors: ', legacy)
333 else :
326 else :
334 if isinstance(name_or_cls, str):
327 if isinstance(name_or_cls, str):
335 style_cls = get_style_by_name(name_or_cls)
328 style_cls = get_style_by_name(name_or_cls)
336 else:
329 else:
337 style_cls = name_or_cls
330 style_cls = name_or_cls
338 style_overrides = {
331 style_overrides = {
339 Token.Prompt: '#009900',
332 Token.Prompt: '#009900',
340 Token.PromptNum: '#00ff00 bold',
333 Token.PromptNum: '#00ff00 bold',
341 Token.OutPrompt: '#990000',
334 Token.OutPrompt: '#990000',
342 Token.OutPromptNum: '#ff0000 bold',
335 Token.OutPromptNum: '#ff0000 bold',
343 }
336 }
344 style_overrides.update(self.highlighting_style_overrides)
337 style_overrides.update(self.highlighting_style_overrides)
345 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
338 style = merge_styles([
346 style_dict=style_overrides)
339 style_from_pygments_cls(style_cls),
340 style_from_pygments_dict(style_overrides),
341 ])
347
342
348 return style
343 return style
349
344
350 def _layout_options(self):
345 @property
346 def pt_complete_style(self):
347 return {
348 'multicolumn': CompleteStyle.MULTI_COLUMN,
349 'column': CompleteStyle.COLUMN,
350 'readlinelike': CompleteStyle.READLINE_LIKE,
351 }[self.display_completions],
352
353 def _extra_prompt_options(self):
351 """
354 """
352 Return the current layout option for the current Terminal InteractiveShell
355 Return the current layout option for the current Terminal InteractiveShell
353 """
356 """
357 def get_message():
358 return PygmentsTokens(self.prompts.in_prompt_tokens())
359
354 return {
360 return {
361 'complete_in_thread': False,
355 'lexer':IPythonPTLexer(),
362 'lexer':IPythonPTLexer(),
356 'reserve_space_for_menu':self.space_for_menu,
363 'reserve_space_for_menu':self.space_for_menu,
357 'get_prompt_tokens':self.prompts.in_prompt_tokens,
364 'message': get_message,
358 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
365 'prompt_continuation': (
359 'multiline':True,
366 lambda width, lineno, is_soft_wrap:
360 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
367 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
368 'multiline': True,
369 'complete_style': self.pt_complete_style,
361
370
362 # Highlight matching brackets, but only when this setting is
371 # Highlight matching brackets, but only when this setting is
363 # enabled, and only when the DEFAULT_BUFFER has the focus.
372 # enabled, and only when the DEFAULT_BUFFER has the focus.
364 'extra_input_processors': [ConditionalProcessor(
373 'input_processors': [ConditionalProcessor(
365 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
374 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
366 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
375 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
367 Condition(lambda cli: self.highlight_matching_brackets))],
376 Condition(lambda: self.highlight_matching_brackets))],
368 }
377 }
369
378
370 def _update_layout(self):
371 """
372 Ask for a re computation of the application layout, if for example ,
373 some configuration options have changed.
374 """
375 if self._pt_app:
376 self._pt_app.layout = create_prompt_layout(**self._layout_options())
377
378 def prompt_for_code(self):
379 def prompt_for_code(self):
379 with self.pt_cli.patch_stdout_context(raw=True):
380 if self.rl_next_input:
380 document = self.pt_cli.run(
381 default = self.rl_next_input
381 pre_run=self.pre_prompt, reset_current_buffer=True)
382 self.rl_next_input = None
382 return document.text
383 else:
384 default = ''
385
386 with patch_stdout(raw=True):
387 text = self.pt_app.prompt(
388 default=default,
389 # pre_run=self.pre_prompt,# reset_current_buffer=True,
390 **self._extra_prompt_options())
391 return text
383
392
384 def enable_win_unicode_console(self):
393 def enable_win_unicode_console(self):
385 if sys.version_info >= (3, 6):
394 if sys.version_info >= (3, 6):
386 # Since PEP 528, Python uses the unicode APIs for the Windows
395 # Since PEP 528, Python uses the unicode APIs for the Windows
387 # console by default, so WUC shouldn't be needed.
396 # console by default, so WUC shouldn't be needed.
388 return
397 return
389
398
390 import win_unicode_console
399 import win_unicode_console
391 win_unicode_console.enable()
400 win_unicode_console.enable()
392
401
393 def init_io(self):
402 def init_io(self):
394 if sys.platform not in {'win32', 'cli'}:
403 if sys.platform not in {'win32', 'cli'}:
395 return
404 return
396
405
397 self.enable_win_unicode_console()
406 self.enable_win_unicode_console()
398
407
399 import colorama
408 import colorama
400 colorama.init()
409 colorama.init()
401
410
402 # For some reason we make these wrappers around stdout/stderr.
411 # For some reason we make these wrappers around stdout/stderr.
403 # For now, we need to reset them so all output gets coloured.
412 # For now, we need to reset them so all output gets coloured.
404 # https://github.com/ipython/ipython/issues/8669
413 # https://github.com/ipython/ipython/issues/8669
405 # io.std* are deprecated, but don't show our own deprecation warnings
414 # io.std* are deprecated, but don't show our own deprecation warnings
406 # during initialization of the deprecated API.
415 # during initialization of the deprecated API.
407 with warnings.catch_warnings():
416 with warnings.catch_warnings():
408 warnings.simplefilter('ignore', DeprecationWarning)
417 warnings.simplefilter('ignore', DeprecationWarning)
409 io.stdout = io.IOStream(sys.stdout)
418 io.stdout = io.IOStream(sys.stdout)
410 io.stderr = io.IOStream(sys.stderr)
419 io.stderr = io.IOStream(sys.stderr)
411
420
412 def init_magics(self):
421 def init_magics(self):
413 super(TerminalInteractiveShell, self).init_magics()
422 super(TerminalInteractiveShell, self).init_magics()
414 self.register_magics(TerminalMagics)
423 self.register_magics(TerminalMagics)
415
424
416 def init_alias(self):
425 def init_alias(self):
417 # The parent class defines aliases that can be safely used with any
426 # The parent class defines aliases that can be safely used with any
418 # frontend.
427 # frontend.
419 super(TerminalInteractiveShell, self).init_alias()
428 super(TerminalInteractiveShell, self).init_alias()
420
429
421 # Now define aliases that only make sense on the terminal, because they
430 # Now define aliases that only make sense on the terminal, because they
422 # need direct access to the console in a way that we can't emulate in
431 # need direct access to the console in a way that we can't emulate in
423 # GUI or web frontend
432 # GUI or web frontend
424 if os.name == 'posix':
433 if os.name == 'posix':
425 for cmd in ['clear', 'more', 'less', 'man']:
434 for cmd in ['clear', 'more', 'less', 'man']:
426 self.alias_manager.soft_define_alias(cmd, cmd)
435 self.alias_manager.soft_define_alias(cmd, cmd)
427
436
428
437
429 def __init__(self, *args, **kwargs):
438 def __init__(self, *args, **kwargs):
430 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
439 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
431 self.init_prompt_toolkit_cli()
440 self.init_prompt_toolkit_cli()
432 self.init_term_title()
441 self.init_term_title()
433 self.keep_running = True
442 self.keep_running = True
434
443
435 self.debugger_history = InMemoryHistory()
444 self.debugger_history = InMemoryHistory()
436
445
437 def ask_exit(self):
446 def ask_exit(self):
438 self.keep_running = False
447 self.keep_running = False
439
448
440 rl_next_input = None
449 rl_next_input = None
441
450
442 def pre_prompt(self):
443 if self.rl_next_input:
444 # We can't set the buffer here, because it will be reset just after
445 # this. Adding a callable to pre_run_callables does what we need
446 # after the buffer is reset.
447 s = self.rl_next_input
448 def set_doc():
449 self.pt_cli.application.buffer.document = Document(s)
450 if hasattr(self.pt_cli, 'pre_run_callables'):
451 self.pt_cli.pre_run_callables.append(set_doc)
452 else:
453 # Older version of prompt_toolkit; it's OK to set the document
454 # directly here.
455 set_doc()
456 self.rl_next_input = None
457
458 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
451 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
459
452
460 if display_banner is not DISPLAY_BANNER_DEPRECATED:
453 if display_banner is not DISPLAY_BANNER_DEPRECATED:
461 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
454 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
462
455
463 self.keep_running = True
456 self.keep_running = True
464 while self.keep_running:
457 while self.keep_running:
465 print(self.separate_in, end='')
458 print(self.separate_in, end='')
466
459
467 try:
460 try:
468 code = self.prompt_for_code()
461 code = self.prompt_for_code()
469 except EOFError:
462 except EOFError:
470 if (not self.confirm_exit) \
463 if (not self.confirm_exit) \
471 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
464 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
472 self.ask_exit()
465 self.ask_exit()
473
466
474 else:
467 else:
475 if code:
468 if code:
476 self.run_cell(code, store_history=True)
469 self.run_cell(code, store_history=True)
477
470
478 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
471 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
479 # An extra layer of protection in case someone mashing Ctrl-C breaks
472 # An extra layer of protection in case someone mashing Ctrl-C breaks
480 # out of our internal code.
473 # out of our internal code.
481 if display_banner is not DISPLAY_BANNER_DEPRECATED:
474 if display_banner is not DISPLAY_BANNER_DEPRECATED:
482 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
475 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
483 while True:
476 while True:
484 try:
477 try:
485 self.interact()
478 self.interact()
486 break
479 break
487 except KeyboardInterrupt as e:
480 except KeyboardInterrupt as e:
488 print("\n%s escaped interact()\n" % type(e).__name__)
481 print("\n%s escaped interact()\n" % type(e).__name__)
489 finally:
482 finally:
490 # An interrupt during the eventloop will mess up the
483 # An interrupt during the eventloop will mess up the
491 # internal state of the prompt_toolkit library.
484 # internal state of the prompt_toolkit library.
492 # Stopping the eventloop fixes this, see
485 # Stopping the eventloop fixes this, see
493 # https://github.com/ipython/ipython/pull/9867
486 # https://github.com/ipython/ipython/pull/9867
494 if hasattr(self, '_eventloop'):
487 if hasattr(self, '_eventloop'):
495 self._eventloop.stop()
488 self._eventloop.stop()
496
489
497 _inputhook = None
490 _inputhook = None
498 def inputhook(self, context):
491 def inputhook(self, context):
499 if self._inputhook is not None:
492 if self._inputhook is not None:
500 self._inputhook(context)
493 self._inputhook(context)
501
494
502 active_eventloop = None
495 active_eventloop = None
503 def enable_gui(self, gui=None):
496 def enable_gui(self, gui=None):
504 if gui:
497 if gui:
505 self.active_eventloop, self._inputhook =\
498 self.active_eventloop, self._inputhook =\
506 get_inputhook_name_and_func(gui)
499 get_inputhook_name_and_func(gui)
507 else:
500 else:
508 self.active_eventloop = self._inputhook = None
501 self.active_eventloop = self._inputhook = None
509
502
510 # Run !system commands directly, not through pipes, so terminal programs
503 # Run !system commands directly, not through pipes, so terminal programs
511 # work correctly.
504 # work correctly.
512 system = InteractiveShell.system_raw
505 system = InteractiveShell.system_raw
513
506
514 def auto_rewrite_input(self, cmd):
507 def auto_rewrite_input(self, cmd):
515 """Overridden from the parent class to use fancy rewriting prompt"""
508 """Overridden from the parent class to use fancy rewriting prompt"""
516 if not self.show_rewritten_input:
509 if not self.show_rewritten_input:
517 return
510 return
518
511
519 tokens = self.prompts.rewrite_prompt_tokens()
512 tokens = self.prompts.rewrite_prompt_tokens()
520 if self.pt_cli:
513 if self.pt_app:
521 self.pt_cli.print_tokens(tokens)
514 self.pt_app.print_tokens(tokens) # XXX
522 print(cmd)
515 print(cmd)
523 else:
516 else:
524 prompt = ''.join(s for t, s in tokens)
517 prompt = ''.join(s for t, s in tokens)
525 print(prompt, cmd, sep='')
518 print(prompt, cmd, sep='')
526
519
527 _prompts_before = None
520 _prompts_before = None
528 def switch_doctest_mode(self, mode):
521 def switch_doctest_mode(self, mode):
529 """Switch prompts to classic for %doctest_mode"""
522 """Switch prompts to classic for %doctest_mode"""
530 if mode:
523 if mode:
531 self._prompts_before = self.prompts
524 self._prompts_before = self.prompts
532 self.prompts = ClassicPrompts(self)
525 self.prompts = ClassicPrompts(self)
533 elif self._prompts_before:
526 elif self._prompts_before:
534 self.prompts = self._prompts_before
527 self.prompts = self._prompts_before
535 self._prompts_before = None
528 self._prompts_before = None
536 self._update_layout()
529 # self._update_layout()
537
530
538
531
539 InteractiveShellABC.register(TerminalInteractiveShell)
532 InteractiveShellABC.register(TerminalInteractiveShell)
540
533
541 if __name__ == '__main__':
534 if __name__ == '__main__':
542 TerminalInteractiveShell.instance().interact()
535 TerminalInteractiveShell.instance().interact()
@@ -1,79 +1,82 b''
1 """Terminal input and output prompts."""
1 """Terminal input and output prompts."""
2
2
3 from pygments.token import Token
3 from pygments.token import Token
4 import sys
4 import sys
5
5
6 from IPython.core.displayhook import DisplayHook
6 from IPython.core.displayhook import DisplayHook
7
7
8 from prompt_toolkit.layout.utils import token_list_width
8 from prompt_toolkit.formatted_text import fragment_list_width, PygmentsTokens
9 from prompt_toolkit.shortcuts import print_formatted_text
10
9
11
10 class Prompts(object):
12 class Prompts(object):
11 def __init__(self, shell):
13 def __init__(self, shell):
12 self.shell = shell
14 self.shell = shell
13
15
14 def in_prompt_tokens(self, cli=None):
16 def in_prompt_tokens(self):
15 return [
17 return [
16 (Token.Prompt, 'In ['),
18 (Token.Prompt, 'In ['),
17 (Token.PromptNum, str(self.shell.execution_count)),
19 (Token.PromptNum, str(self.shell.execution_count)),
18 (Token.Prompt, ']: '),
20 (Token.Prompt, ']: '),
19 ]
21 ]
20
22
21 def _width(self):
23 def _width(self):
22 return token_list_width(self.in_prompt_tokens())
24 return fragment_list_width(self.in_prompt_tokens())
23
25
24 def continuation_prompt_tokens(self, cli=None, width=None):
26 def continuation_prompt_tokens(self, width=None):
25 if width is None:
27 if width is None:
26 width = self._width()
28 width = self._width()
27 return [
29 return [
28 (Token.Prompt, (' ' * (width - 5)) + '...: '),
30 (Token.Prompt, (' ' * (width - 5)) + '...: '),
29 ]
31 ]
30
32
31 def rewrite_prompt_tokens(self):
33 def rewrite_prompt_tokens(self):
32 width = self._width()
34 width = self._width()
33 return [
35 return [
34 (Token.Prompt, ('-' * (width - 2)) + '> '),
36 (Token.Prompt, ('-' * (width - 2)) + '> '),
35 ]
37 ]
36
38
37 def out_prompt_tokens(self):
39 def out_prompt_tokens(self):
38 return [
40 return [
39 (Token.OutPrompt, 'Out['),
41 (Token.OutPrompt, 'Out['),
40 (Token.OutPromptNum, str(self.shell.execution_count)),
42 (Token.OutPromptNum, str(self.shell.execution_count)),
41 (Token.OutPrompt, ']: '),
43 (Token.OutPrompt, ']: '),
42 ]
44 ]
43
45
44 class ClassicPrompts(Prompts):
46 class ClassicPrompts(Prompts):
45 def in_prompt_tokens(self, cli=None):
47 def in_prompt_tokens(self):
46 return [
48 return [
47 (Token.Prompt, '>>> '),
49 (Token.Prompt, '>>> '),
48 ]
50 ]
49
51
50 def continuation_prompt_tokens(self, cli=None, width=None):
52 def continuation_prompt_tokens(self, width=None):
51 return [
53 return [
52 (Token.Prompt, '... ')
54 (Token.Prompt, '... ')
53 ]
55 ]
54
56
55 def rewrite_prompt_tokens(self):
57 def rewrite_prompt_tokens(self):
56 return []
58 return []
57
59
58 def out_prompt_tokens(self):
60 def out_prompt_tokens(self):
59 return []
61 return []
60
62
61 class RichPromptDisplayHook(DisplayHook):
63 class RichPromptDisplayHook(DisplayHook):
62 """Subclass of base display hook using coloured prompt"""
64 """Subclass of base display hook using coloured prompt"""
63 def write_output_prompt(self):
65 def write_output_prompt(self):
64 sys.stdout.write(self.shell.separate_out)
66 sys.stdout.write(self.shell.separate_out)
65 # If we're not displaying a prompt, it effectively ends with a newline,
67 # If we're not displaying a prompt, it effectively ends with a newline,
66 # because the output will be left-aligned.
68 # because the output will be left-aligned.
67 self.prompt_end_newline = True
69 self.prompt_end_newline = True
68
70
69 if self.do_full_cache:
71 if self.do_full_cache:
70 tokens = self.shell.prompts.out_prompt_tokens()
72 tokens = self.shell.prompts.out_prompt_tokens()
71 prompt_txt = ''.join(s for t, s in tokens)
73 prompt_txt = ''.join(s for t, s in tokens)
72 if prompt_txt and not prompt_txt.endswith('\n'):
74 if prompt_txt and not prompt_txt.endswith('\n'):
73 # Ask for a newline before multiline output
75 # Ask for a newline before multiline output
74 self.prompt_end_newline = False
76 self.prompt_end_newline = False
75
77
76 if self.shell.pt_cli:
78 if self.shell.pt_app:
77 self.shell.pt_cli.print_tokens(tokens)
79 print_formatted_text(
80 PygmentsTokens(tokens), style=self.shell.pt_app.app.style)
78 else:
81 else:
79 sys.stdout.write(prompt_txt)
82 sys.stdout.write(prompt_txt)
@@ -1,160 +1,158 b''
1 """prompt-toolkit utilities
1 """prompt-toolkit utilities
2
2
3 Everything in this module is a private API,
3 Everything in this module is a private API,
4 not to be used outside IPython.
4 not to be used outside IPython.
5 """
5 """
6
6
7 # Copyright (c) IPython Development Team.
7 # Copyright (c) IPython Development Team.
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9
9
10 import unicodedata
10 import unicodedata
11 from wcwidth import wcwidth
11 from wcwidth import wcwidth
12
12
13 from IPython.core.completer import (
13 from IPython.core.completer import (
14 provisionalcompleter, cursor_to_position,
14 provisionalcompleter, cursor_to_position,
15 _deduplicate_completions)
15 _deduplicate_completions)
16 from prompt_toolkit.completion import Completer, Completion
16 from prompt_toolkit.completion import Completer, Completion
17 from prompt_toolkit.layout.lexers import Lexer
17 from prompt_toolkit.lexers import Lexer
18 from prompt_toolkit.layout.lexers import PygmentsLexer
18 from prompt_toolkit.lexers import PygmentsLexer
19 from prompt_toolkit.patch_stdout import patch_stdout
19
20
20 import pygments.lexers as pygments_lexers
21 import pygments.lexers as pygments_lexers
21
22
22 _completion_sentinel = object()
23 _completion_sentinel = object()
23
24
24 def _elide(string, *, min_elide=30):
25 def _elide(string, *, min_elide=30):
25 """
26 """
26 If a string is long enough, and has at least 2 dots,
27 If a string is long enough, and has at least 2 dots,
27 replace the middle part with ellipses.
28 replace the middle part with ellipses.
28
29
29 If three consecutive dots, or two consecutive dots are encountered these are
30 If three consecutive dots, or two consecutive dots are encountered these are
30 replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode
31 replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode
31 equivalents
32 equivalents
32 """
33 """
33 string = string.replace('...','\N{HORIZONTAL ELLIPSIS}')
34 string = string.replace('...','\N{HORIZONTAL ELLIPSIS}')
34 string = string.replace('..','\N{TWO DOT LEADER}')
35 string = string.replace('..','\N{TWO DOT LEADER}')
35 if len(string) < min_elide:
36 if len(string) < min_elide:
36 return string
37 return string
37
38
38 parts = string.split('.')
39 parts = string.split('.')
39
40
40 if len(parts) <= 3:
41 if len(parts) <= 3:
41 return string
42 return string
42
43
43 return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(parts[0], parts[1][0], parts[-2][-1], parts[-1])
44 return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(parts[0], parts[1][0], parts[-2][-1], parts[-1])
44
45
45
46
46 def _adjust_completion_text_based_on_context(text, body, offset):
47 def _adjust_completion_text_based_on_context(text, body, offset):
47 if text.endswith('=') and len(body) > offset and body[offset] is '=':
48 if text.endswith('=') and len(body) > offset and body[offset] is '=':
48 return text[:-1]
49 return text[:-1]
49 else:
50 else:
50 return text
51 return text
51
52
52
53
53 class IPythonPTCompleter(Completer):
54 class IPythonPTCompleter(Completer):
54 """Adaptor to provide IPython completions to prompt_toolkit"""
55 """Adaptor to provide IPython completions to prompt_toolkit"""
55 def __init__(self, ipy_completer=None, shell=None, patch_stdout=None):
56 def __init__(self, ipy_completer=None, shell=None):
56 if shell is None and ipy_completer is None:
57 if shell is None and ipy_completer is None:
57 raise TypeError("Please pass shell=an InteractiveShell instance.")
58 raise TypeError("Please pass shell=an InteractiveShell instance.")
58 self._ipy_completer = ipy_completer
59 self._ipy_completer = ipy_completer
59 self.shell = shell
60 self.shell = shell
60 if patch_stdout is None:
61 raise TypeError("Please pass patch_stdout")
62 self.patch_stdout = patch_stdout
63
61
64 @property
62 @property
65 def ipy_completer(self):
63 def ipy_completer(self):
66 if self._ipy_completer:
64 if self._ipy_completer:
67 return self._ipy_completer
65 return self._ipy_completer
68 else:
66 else:
69 return self.shell.Completer
67 return self.shell.Completer
70
68
71 def get_completions(self, document, complete_event):
69 def get_completions(self, document, complete_event):
72 if not document.current_line.strip():
70 if not document.current_line.strip():
73 return
71 return
74 # Some bits of our completion system may print stuff (e.g. if a module
72 # Some bits of our completion system may print stuff (e.g. if a module
75 # is imported). This context manager ensures that doesn't interfere with
73 # is imported). This context manager ensures that doesn't interfere with
76 # the prompt.
74 # the prompt.
77
75
78 with self.patch_stdout(), provisionalcompleter():
76 with patch_stdout(), provisionalcompleter():
79 body = document.text
77 body = document.text
80 cursor_row = document.cursor_position_row
78 cursor_row = document.cursor_position_row
81 cursor_col = document.cursor_position_col
79 cursor_col = document.cursor_position_col
82 cursor_position = document.cursor_position
80 cursor_position = document.cursor_position
83 offset = cursor_to_position(body, cursor_row, cursor_col)
81 offset = cursor_to_position(body, cursor_row, cursor_col)
84 yield from self._get_completions(body, offset, cursor_position, self.ipy_completer)
82 yield from self._get_completions(body, offset, cursor_position, self.ipy_completer)
85
83
86 @staticmethod
84 @staticmethod
87 def _get_completions(body, offset, cursor_position, ipyc):
85 def _get_completions(body, offset, cursor_position, ipyc):
88 """
86 """
89 Private equivalent of get_completions() use only for unit_testing.
87 Private equivalent of get_completions() use only for unit_testing.
90 """
88 """
91 debug = getattr(ipyc, 'debug', False)
89 debug = getattr(ipyc, 'debug', False)
92 completions = _deduplicate_completions(
90 completions = _deduplicate_completions(
93 body, ipyc.completions(body, offset))
91 body, ipyc.completions(body, offset))
94 for c in completions:
92 for c in completions:
95 if not c.text:
93 if not c.text:
96 # Guard against completion machinery giving us an empty string.
94 # Guard against completion machinery giving us an empty string.
97 continue
95 continue
98 text = unicodedata.normalize('NFC', c.text)
96 text = unicodedata.normalize('NFC', c.text)
99 # When the first character of the completion has a zero length,
97 # When the first character of the completion has a zero length,
100 # then it's probably a decomposed unicode character. E.g. caused by
98 # then it's probably a decomposed unicode character. E.g. caused by
101 # the "\dot" completion. Try to compose again with the previous
99 # the "\dot" completion. Try to compose again with the previous
102 # character.
100 # character.
103 if wcwidth(text[0]) == 0:
101 if wcwidth(text[0]) == 0:
104 if cursor_position + c.start > 0:
102 if cursor_position + c.start > 0:
105 char_before = body[c.start - 1]
103 char_before = body[c.start - 1]
106 fixed_text = unicodedata.normalize(
104 fixed_text = unicodedata.normalize(
107 'NFC', char_before + text)
105 'NFC', char_before + text)
108
106
109 # Yield the modified completion instead, if this worked.
107 # Yield the modified completion instead, if this worked.
110 if wcwidth(text[0:1]) == 1:
108 if wcwidth(text[0:1]) == 1:
111 yield Completion(fixed_text, start_position=c.start - offset - 1)
109 yield Completion(fixed_text, start_position=c.start - offset - 1)
112 continue
110 continue
113
111
114 # TODO: Use Jedi to determine meta_text
112 # TODO: Use Jedi to determine meta_text
115 # (Jedi currently has a bug that results in incorrect information.)
113 # (Jedi currently has a bug that results in incorrect information.)
116 # meta_text = ''
114 # meta_text = ''
117 # yield Completion(m, start_position=start_pos,
115 # yield Completion(m, start_position=start_pos,
118 # display_meta=meta_text)
116 # display_meta=meta_text)
119 display_text = c.text
117 display_text = c.text
120
118
121 adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset)
119 adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset)
122 if c.type == 'function':
120 if c.type == 'function':
123 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()'), display_meta=c.type+c.signature)
121 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()'), display_meta=c.type+c.signature)
124 else:
122 else:
125 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text), display_meta=c.type)
123 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text), display_meta=c.type)
126
124
127 class IPythonPTLexer(Lexer):
125 class IPythonPTLexer(Lexer):
128 """
126 """
129 Wrapper around PythonLexer and BashLexer.
127 Wrapper around PythonLexer and BashLexer.
130 """
128 """
131 def __init__(self):
129 def __init__(self):
132 l = pygments_lexers
130 l = pygments_lexers
133 self.python_lexer = PygmentsLexer(l.Python3Lexer)
131 self.python_lexer = PygmentsLexer(l.Python3Lexer)
134 self.shell_lexer = PygmentsLexer(l.BashLexer)
132 self.shell_lexer = PygmentsLexer(l.BashLexer)
135
133
136 self.magic_lexers = {
134 self.magic_lexers = {
137 'HTML': PygmentsLexer(l.HtmlLexer),
135 'HTML': PygmentsLexer(l.HtmlLexer),
138 'html': PygmentsLexer(l.HtmlLexer),
136 'html': PygmentsLexer(l.HtmlLexer),
139 'javascript': PygmentsLexer(l.JavascriptLexer),
137 'javascript': PygmentsLexer(l.JavascriptLexer),
140 'js': PygmentsLexer(l.JavascriptLexer),
138 'js': PygmentsLexer(l.JavascriptLexer),
141 'perl': PygmentsLexer(l.PerlLexer),
139 'perl': PygmentsLexer(l.PerlLexer),
142 'ruby': PygmentsLexer(l.RubyLexer),
140 'ruby': PygmentsLexer(l.RubyLexer),
143 'latex': PygmentsLexer(l.TexLexer),
141 'latex': PygmentsLexer(l.TexLexer),
144 }
142 }
145
143
146 def lex_document(self, cli, document):
144 def lex_document(self, document):
147 text = document.text.lstrip()
145 text = document.text.lstrip()
148
146
149 lexer = self.python_lexer
147 lexer = self.python_lexer
150
148
151 if text.startswith('!') or text.startswith('%%bash'):
149 if text.startswith('!') or text.startswith('%%bash'):
152 lexer = self.shell_lexer
150 lexer = self.shell_lexer
153
151
154 elif text.startswith('%%'):
152 elif text.startswith('%%'):
155 for magic, l in self.magic_lexers.items():
153 for magic, l in self.magic_lexers.items():
156 if text.startswith('%%' + magic):
154 if text.startswith('%%' + magic):
157 lexer = l
155 lexer = l
158 break
156 break
159
157
160 return lexer.lex_document(cli, document)
158 return lexer.lex_document(document)
@@ -1,256 +1,244 b''
1 """
1 """
2 Module to define and register Terminal IPython shortcuts with
2 Module to define and register Terminal IPython shortcuts with
3 :mod:`prompt_toolkit`
3 :mod:`prompt_toolkit`
4 """
4 """
5
5
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 import warnings
9 import warnings
10 import signal
10 import signal
11 import sys
11 import sys
12 from typing import Callable
12 from typing import Callable
13
13
14
14
15 from prompt_toolkit.application.current import get_app
15 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
16 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
17 from prompt_toolkit.filters import (has_focus, has_selection, Condition,
17 ViInsertMode, EmacsInsertMode, HasCompletions)
18 vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
18 from prompt_toolkit.filters.cli import ViMode, ViNavigationMode
19 from prompt_toolkit.keys import Keys
20 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
19 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
20 from prompt_toolkit.key_binding import KeyBindings
21
21
22 from IPython.utils.decorators import undoc
22 from IPython.utils.decorators import undoc
23
23
24 @undoc
24 @undoc
25 @Condition
25 @Condition
26 def cursor_in_leading_ws(cli):
26 def cursor_in_leading_ws():
27 before = cli.application.buffer.document.current_line_before_cursor
27 before = get_app().current_buffer.document.current_line_before_cursor
28 return (not before) or before.isspace()
28 return (not before) or before.isspace()
29
29
30 def register_ipython_shortcuts(registry, shell):
30
31 def create_ipython_shortcuts(shell):
31 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
32 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
32 insert_mode = ViInsertMode() | EmacsInsertMode()
33
34 kb = KeyBindings()
35 insert_mode = vi_insert_mode | emacs_insert_mode
33
36
34 if getattr(shell, 'handle_return', None):
37 if getattr(shell, 'handle_return', None):
35 return_handler = shell.handle_return(shell)
38 return_handler = shell.handle_return(shell)
36 else:
39 else:
37 return_handler = newline_or_execute_outer(shell)
40 return_handler = newline_or_execute_outer(shell)
38
41
39 # Ctrl+J == Enter, seemingly
42 kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
40 registry.add_binding(Keys.ControlJ,
43 & ~has_selection
41 filter=(HasFocus(DEFAULT_BUFFER)
44 & insert_mode
42 & ~HasSelection()
43 & insert_mode
44 ))(return_handler)
45 ))(return_handler)
45
46
46 registry.add_binding(Keys.ControlBackslash)(force_exit)
47 kb.add('c-\\')(force_exit)
47
48
48 registry.add_binding(Keys.ControlP,
49 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
49 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
50 )(previous_history_or_previous_completion)
50 ))(previous_history_or_previous_completion)
51
51
52 registry.add_binding(Keys.ControlN,
52 kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
53 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
53 )(next_history_or_next_completion)
54 ))(next_history_or_next_completion)
55
54
56 registry.add_binding(Keys.ControlG,
55 kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
57 filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions()
56 )(dismiss_completion)
58 ))(dismiss_completion)
59
57
60 registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)
58 kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
61 )(reset_buffer)
62
59
63 registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)
60 kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
64 )(reset_search_buffer)
65
61
66 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
62 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
67 registry.add_binding(Keys.ControlZ, filter=supports_suspend
63 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
68 )(suspend_to_bg)
69
64
70 # Ctrl+I == Tab
65 # Ctrl+I == Tab
71 registry.add_binding(Keys.ControlI,
66 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
72 filter=(HasFocus(DEFAULT_BUFFER)
67 & ~has_selection
73 & ~HasSelection()
68 & insert_mode
74 & insert_mode
69 & cursor_in_leading_ws
75 & cursor_in_leading_ws
76 ))(indent_buffer)
70 ))(indent_buffer)
77
71
78 registry.add_binding(Keys.ControlO,
72 kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER)
79 filter=(HasFocus(DEFAULT_BUFFER)
73 & emacs_insert_mode))(newline_autoindent_outer(shell.input_splitter))
80 & EmacsInsertMode()))(newline_autoindent_outer(shell.input_splitter))
81
74
82 registry.add_binding(Keys.F2,
75 kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
83 filter=HasFocus(DEFAULT_BUFFER)
84 )(open_input_in_editor)
85
76
86 if shell.display_completions == 'readlinelike':
77 if shell.display_completions == 'readlinelike':
87 registry.add_binding(Keys.ControlI,
78 kb.add('c-i', filter=(has_focus(DEFAULT_BUFFER)
88 filter=(HasFocus(DEFAULT_BUFFER)
79 & ~has_selection
89 & ~HasSelection()
80 & insert_mode
90 & insert_mode
81 & ~cursor_in_leading_ws
91 & ~cursor_in_leading_ws
82 ))(display_completions_like_readline)
92 ))(display_completions_like_readline)
93
83
94 if sys.platform == 'win32':
84 if sys.platform == 'win32':
95 registry.add_binding(Keys.ControlV,
85 kb.add('c-v', filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
96 filter=(
86
97 HasFocus(
87 return kb
98 DEFAULT_BUFFER) & ~ViMode()
99 ))(win_paste)
100
88
101
89
102 def newline_or_execute_outer(shell):
90 def newline_or_execute_outer(shell):
103 def newline_or_execute(event):
91 def newline_or_execute(event):
104 """When the user presses return, insert a newline or execute the code."""
92 """When the user presses return, insert a newline or execute the code."""
105 b = event.current_buffer
93 b = event.current_buffer
106 d = b.document
94 d = b.document
107
95
108 if b.complete_state:
96 if b.complete_state:
109 cc = b.complete_state.current_completion
97 cc = b.complete_state.current_completion
110 if cc:
98 if cc:
111 b.apply_completion(cc)
99 b.apply_completion(cc)
112 else:
100 else:
113 b.cancel_completion()
101 b.cancel_completion()
114 return
102 return
115
103
116 # If there's only one line, treat it as if the cursor is at the end.
104 # If there's only one line, treat it as if the cursor is at the end.
117 # See https://github.com/ipython/ipython/issues/10425
105 # See https://github.com/ipython/ipython/issues/10425
118 if d.line_count == 1:
106 if d.line_count == 1:
119 check_text = d.text
107 check_text = d.text
120 else:
108 else:
121 check_text = d.text[:d.cursor_position]
109 check_text = d.text[:d.cursor_position]
122 status, indent = shell.input_splitter.check_complete(check_text + '\n')
110 status, indent = shell.input_splitter.check_complete(check_text + '\n')
123
111
124 if not (d.on_last_line or
112 if not (d.on_last_line or
125 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
113 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
126 ):
114 ):
127 b.insert_text('\n' + (' ' * (indent or 0)))
115 b.insert_text('\n' + (' ' * (indent or 0)))
128 return
116 return
129
117
130 if (status != 'incomplete') and b.accept_action.is_returnable:
118 if (status != 'incomplete') and b.accept_handler:
131 b.accept_action.validate_and_handle(event.cli, b)
119 b.validate_and_handle()
132 else:
120 else:
133 b.insert_text('\n' + (' ' * (indent or 0)))
121 b.insert_text('\n' + (' ' * (indent or 0)))
134 return newline_or_execute
122 return newline_or_execute
135
123
136
124
137 def previous_history_or_previous_completion(event):
125 def previous_history_or_previous_completion(event):
138 """
126 """
139 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
127 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
140
128
141 If completer is open this still select previous completion.
129 If completer is open this still select previous completion.
142 """
130 """
143 event.current_buffer.auto_up()
131 event.current_buffer.auto_up()
144
132
145
133
146 def next_history_or_next_completion(event):
134 def next_history_or_next_completion(event):
147 """
135 """
148 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
136 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
149
137
150 If completer is open this still select next completion.
138 If completer is open this still select next completion.
151 """
139 """
152 event.current_buffer.auto_down()
140 event.current_buffer.auto_down()
153
141
154
142
155 def dismiss_completion(event):
143 def dismiss_completion(event):
156 b = event.current_buffer
144 b = event.current_buffer
157 if b.complete_state:
145 if b.complete_state:
158 b.cancel_completion()
146 b.cancel_completion()
159
147
160
148
161 def reset_buffer(event):
149 def reset_buffer(event):
162 b = event.current_buffer
150 b = event.current_buffer
163 if b.complete_state:
151 if b.complete_state:
164 b.cancel_completion()
152 b.cancel_completion()
165 else:
153 else:
166 b.reset()
154 b.reset()
167
155
168
156
169 def reset_search_buffer(event):
157 def reset_search_buffer(event):
170 if event.current_buffer.document.text:
158 if event.current_buffer.document.text:
171 event.current_buffer.reset()
159 event.current_buffer.reset()
172 else:
160 else:
173 event.cli.push_focus(DEFAULT_BUFFER)
161 event.app.layout.focus(DEFAULT_BUFFER)
174
162
175 def suspend_to_bg(event):
163 def suspend_to_bg(event):
176 event.cli.suspend_to_background()
164 event.app.suspend_to_background()
177
165
178 def force_exit(event):
166 def force_exit(event):
179 """
167 """
180 Force exit (with a non-zero return value)
168 Force exit (with a non-zero return value)
181 """
169 """
182 sys.exit("Quit")
170 sys.exit("Quit")
183
171
184 def indent_buffer(event):
172 def indent_buffer(event):
185 event.current_buffer.insert_text(' ' * 4)
173 event.current_buffer.insert_text(' ' * 4)
186
174
187 @undoc
175 @undoc
188 def newline_with_copy_margin(event):
176 def newline_with_copy_margin(event):
189 """
177 """
190 DEPRECATED since IPython 6.0
178 DEPRECATED since IPython 6.0
191
179
192 See :any:`newline_autoindent_outer` for a replacement.
180 See :any:`newline_autoindent_outer` for a replacement.
193
181
194 Preserve margin and cursor position when using
182 Preserve margin and cursor position when using
195 Control-O to insert a newline in EMACS mode
183 Control-O to insert a newline in EMACS mode
196 """
184 """
197 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
185 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
198 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
186 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
199 DeprecationWarning, stacklevel=2)
187 DeprecationWarning, stacklevel=2)
200
188
201 b = event.current_buffer
189 b = event.current_buffer
202 cursor_start_pos = b.document.cursor_position_col
190 cursor_start_pos = b.document.cursor_position_col
203 b.newline(copy_margin=True)
191 b.newline(copy_margin=True)
204 b.cursor_up(count=1)
192 b.cursor_up(count=1)
205 cursor_end_pos = b.document.cursor_position_col
193 cursor_end_pos = b.document.cursor_position_col
206 if cursor_start_pos != cursor_end_pos:
194 if cursor_start_pos != cursor_end_pos:
207 pos_diff = cursor_start_pos - cursor_end_pos
195 pos_diff = cursor_start_pos - cursor_end_pos
208 b.cursor_right(count=pos_diff)
196 b.cursor_right(count=pos_diff)
209
197
210 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
198 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
211 """
199 """
212 Return a function suitable for inserting a indented newline after the cursor.
200 Return a function suitable for inserting a indented newline after the cursor.
213
201
214 Fancier version of deprecated ``newline_with_copy_margin`` which should
202 Fancier version of deprecated ``newline_with_copy_margin`` which should
215 compute the correct indentation of the inserted line. That is to say, indent
203 compute the correct indentation of the inserted line. That is to say, indent
216 by 4 extra space after a function definition, class definition, context
204 by 4 extra space after a function definition, class definition, context
217 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
205 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
218 """
206 """
219
207
220 def newline_autoindent(event):
208 def newline_autoindent(event):
221 """insert a newline after the cursor indented appropriately."""
209 """insert a newline after the cursor indented appropriately."""
222 b = event.current_buffer
210 b = event.current_buffer
223 d = b.document
211 d = b.document
224
212
225 if b.complete_state:
213 if b.complete_state:
226 b.cancel_completion()
214 b.cancel_completion()
227 text = d.text[:d.cursor_position] + '\n'
215 text = d.text[:d.cursor_position] + '\n'
228 _, indent = inputsplitter.check_complete(text)
216 _, indent = inputsplitter.check_complete(text)
229 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
217 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
230
218
231 return newline_autoindent
219 return newline_autoindent
232
220
233
221
234 def open_input_in_editor(event):
222 def open_input_in_editor(event):
235 event.cli.current_buffer.tempfile_suffix = ".py"
223 event.app.current_buffer.tempfile_suffix = ".py"
236 event.cli.current_buffer.open_in_editor(event.cli)
224 event.app.current_buffer.open_in_editor()
237
225
238
226
239 if sys.platform == 'win32':
227 if sys.platform == 'win32':
240 from IPython.core.error import TryNext
228 from IPython.core.error import TryNext
241 from IPython.lib.clipboard import (ClipboardEmpty,
229 from IPython.lib.clipboard import (ClipboardEmpty,
242 win32_clipboard_get,
230 win32_clipboard_get,
243 tkinter_clipboard_get)
231 tkinter_clipboard_get)
244
232
245 @undoc
233 @undoc
246 def win_paste(event):
234 def win_paste(event):
247 try:
235 try:
248 text = win32_clipboard_get()
236 text = win32_clipboard_get()
249 except TryNext:
237 except TryNext:
250 try:
238 try:
251 text = tkinter_clipboard_get()
239 text = tkinter_clipboard_get()
252 except (TryNext, ClipboardEmpty):
240 except (TryNext, ClipboardEmpty):
253 return
241 return
254 except ClipboardEmpty:
242 except ClipboardEmpty:
255 return
243 return
256 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
244 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
@@ -1,264 +1,264 b''
1 #!/usr/bin/env python3
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """Setup script for IPython.
3 """Setup script for IPython.
4
4
5 Under Posix environments it works like a typical setup.py script.
5 Under Posix environments it works like a typical setup.py script.
6 Under Windows, the command sdist is not supported, since IPython
6 Under Windows, the command sdist is not supported, since IPython
7 requires utilities which are not available under Windows."""
7 requires utilities which are not available under Windows."""
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (c) 2008-2011, IPython Development Team.
10 # Copyright (c) 2008-2011, IPython Development Team.
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 #
14 #
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16 #
16 #
17 # The full license is in the file COPYING.rst, distributed with this software.
17 # The full license is in the file COPYING.rst, distributed with this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import os
22 import os
23 import sys
23 import sys
24
24
25 # **Python version check**
25 # **Python version check**
26 #
26 #
27 # This check is also made in IPython/__init__, don't forget to update both when
27 # This check is also made in IPython/__init__, don't forget to update both when
28 # changing Python version requirements.
28 # changing Python version requirements.
29 if sys.version_info < (3, 4):
29 if sys.version_info < (3, 4):
30 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
30 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
31 try:
31 try:
32 import pip
32 import pip
33 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
33 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
34 if pip_version < (9, 0, 1) :
34 if pip_version < (9, 0, 1) :
35 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
35 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
36 'pip {} detected.'.format(pip.__version__)
36 'pip {} detected.'.format(pip.__version__)
37 else:
37 else:
38 # pip is new enough - it must be something else
38 # pip is new enough - it must be something else
39 pip_message = ''
39 pip_message = ''
40 except Exception:
40 except Exception:
41 pass
41 pass
42
42
43
43
44 error = """
44 error = """
45 IPython 7.0+ supports Python 3.4 and above.
45 IPython 7.0+ supports Python 3.4 and above.
46 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
46 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
47 Python 3.3 was supported up to IPython 6.x.
47 Python 3.3 was supported up to IPython 6.x.
48
48
49 See IPython `README.rst` file for more information:
49 See IPython `README.rst` file for more information:
50
50
51 https://github.com/ipython/ipython/blob/master/README.rst
51 https://github.com/ipython/ipython/blob/master/README.rst
52
52
53 Python {py} detected.
53 Python {py} detected.
54 {pip}
54 {pip}
55 """.format(py=sys.version_info, pip=pip_message )
55 """.format(py=sys.version_info, pip=pip_message )
56
56
57 print(error, file=sys.stderr)
57 print(error, file=sys.stderr)
58 sys.exit(1)
58 sys.exit(1)
59
59
60 # At least we're on the python version we need, move on.
60 # At least we're on the python version we need, move on.
61
61
62 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
62 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
63 # update it when the contents of directories change.
63 # update it when the contents of directories change.
64 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
64 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
65
65
66 from distutils.core import setup
66 from distutils.core import setup
67
67
68 # Our own imports
68 # Our own imports
69 from setupbase import target_update
69 from setupbase import target_update
70
70
71 from setupbase import (
71 from setupbase import (
72 setup_args,
72 setup_args,
73 find_packages,
73 find_packages,
74 find_package_data,
74 find_package_data,
75 check_package_data_first,
75 check_package_data_first,
76 find_entry_points,
76 find_entry_points,
77 build_scripts_entrypt,
77 build_scripts_entrypt,
78 find_data_files,
78 find_data_files,
79 git_prebuild,
79 git_prebuild,
80 install_symlinked,
80 install_symlinked,
81 install_lib_symlink,
81 install_lib_symlink,
82 install_scripts_for_symlink,
82 install_scripts_for_symlink,
83 unsymlink,
83 unsymlink,
84 )
84 )
85
85
86 isfile = os.path.isfile
86 isfile = os.path.isfile
87 pjoin = os.path.join
87 pjoin = os.path.join
88
88
89 #-------------------------------------------------------------------------------
89 #-------------------------------------------------------------------------------
90 # Handle OS specific things
90 # Handle OS specific things
91 #-------------------------------------------------------------------------------
91 #-------------------------------------------------------------------------------
92
92
93 if os.name in ('nt','dos'):
93 if os.name in ('nt','dos'):
94 os_name = 'windows'
94 os_name = 'windows'
95 else:
95 else:
96 os_name = os.name
96 os_name = os.name
97
97
98 # Under Windows, 'sdist' has not been supported. Now that the docs build with
98 # Under Windows, 'sdist' has not been supported. Now that the docs build with
99 # Sphinx it might work, but let's not turn it on until someone confirms that it
99 # Sphinx it might work, but let's not turn it on until someone confirms that it
100 # actually works.
100 # actually works.
101 if os_name == 'windows' and 'sdist' in sys.argv:
101 if os_name == 'windows' and 'sdist' in sys.argv:
102 print('The sdist command is not available under Windows. Exiting.')
102 print('The sdist command is not available under Windows. Exiting.')
103 sys.exit(1)
103 sys.exit(1)
104
104
105
105
106 #-------------------------------------------------------------------------------
106 #-------------------------------------------------------------------------------
107 # Things related to the IPython documentation
107 # Things related to the IPython documentation
108 #-------------------------------------------------------------------------------
108 #-------------------------------------------------------------------------------
109
109
110 # update the manuals when building a source dist
110 # update the manuals when building a source dist
111 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
111 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
112
112
113 # List of things to be updated. Each entry is a triplet of args for
113 # List of things to be updated. Each entry is a triplet of args for
114 # target_update()
114 # target_update()
115 to_update = [
115 to_update = [
116 ('docs/man/ipython.1.gz',
116 ('docs/man/ipython.1.gz',
117 ['docs/man/ipython.1'],
117 ['docs/man/ipython.1'],
118 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
118 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
119 ]
119 ]
120
120
121
121
122 [ target_update(*t) for t in to_update ]
122 [ target_update(*t) for t in to_update ]
123
123
124 #---------------------------------------------------------------------------
124 #---------------------------------------------------------------------------
125 # Find all the packages, package data, and data_files
125 # Find all the packages, package data, and data_files
126 #---------------------------------------------------------------------------
126 #---------------------------------------------------------------------------
127
127
128 packages = find_packages()
128 packages = find_packages()
129 package_data = find_package_data()
129 package_data = find_package_data()
130
130
131 data_files = find_data_files()
131 data_files = find_data_files()
132
132
133 setup_args['packages'] = packages
133 setup_args['packages'] = packages
134 setup_args['package_data'] = package_data
134 setup_args['package_data'] = package_data
135 setup_args['data_files'] = data_files
135 setup_args['data_files'] = data_files
136
136
137 #---------------------------------------------------------------------------
137 #---------------------------------------------------------------------------
138 # custom distutils commands
138 # custom distutils commands
139 #---------------------------------------------------------------------------
139 #---------------------------------------------------------------------------
140 # imports here, so they are after setuptools import if there was one
140 # imports here, so they are after setuptools import if there was one
141 from distutils.command.sdist import sdist
141 from distutils.command.sdist import sdist
142
142
143 setup_args['cmdclass'] = {
143 setup_args['cmdclass'] = {
144 'build_py': \
144 'build_py': \
145 check_package_data_first(git_prebuild('IPython')),
145 check_package_data_first(git_prebuild('IPython')),
146 'sdist' : git_prebuild('IPython', sdist),
146 'sdist' : git_prebuild('IPython', sdist),
147 'symlink': install_symlinked,
147 'symlink': install_symlinked,
148 'install_lib_symlink': install_lib_symlink,
148 'install_lib_symlink': install_lib_symlink,
149 'install_scripts_sym': install_scripts_for_symlink,
149 'install_scripts_sym': install_scripts_for_symlink,
150 'unsymlink': unsymlink,
150 'unsymlink': unsymlink,
151 }
151 }
152
152
153
153
154 #---------------------------------------------------------------------------
154 #---------------------------------------------------------------------------
155 # Handle scripts, dependencies, and setuptools specific things
155 # Handle scripts, dependencies, and setuptools specific things
156 #---------------------------------------------------------------------------
156 #---------------------------------------------------------------------------
157
157
158 # For some commands, use setuptools. Note that we do NOT list install here!
158 # For some commands, use setuptools. Note that we do NOT list install here!
159 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
159 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
160 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
160 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
161 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
161 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
162 'egg_info', 'easy_install', 'upload', 'install_egg_info',
162 'egg_info', 'easy_install', 'upload', 'install_egg_info',
163 }
163 }
164
164
165 if len(needs_setuptools.intersection(sys.argv)) > 0:
165 if len(needs_setuptools.intersection(sys.argv)) > 0:
166 import setuptools
166 import setuptools
167
167
168 # This dict is used for passing extra arguments that are setuptools
168 # This dict is used for passing extra arguments that are setuptools
169 # specific to setup
169 # specific to setup
170 setuptools_extra_args = {}
170 setuptools_extra_args = {}
171
171
172 # setuptools requirements
172 # setuptools requirements
173
173
174 extras_require = dict(
174 extras_require = dict(
175 parallel = ['ipyparallel'],
175 parallel = ['ipyparallel'],
176 qtconsole = ['qtconsole'],
176 qtconsole = ['qtconsole'],
177 doc = ['Sphinx>=1.3'],
177 doc = ['Sphinx>=1.3'],
178 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy'],
178 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy'],
179 terminal = [],
179 terminal = [],
180 kernel = ['ipykernel'],
180 kernel = ['ipykernel'],
181 nbformat = ['nbformat'],
181 nbformat = ['nbformat'],
182 notebook = ['notebook', 'ipywidgets'],
182 notebook = ['notebook', 'ipywidgets'],
183 nbconvert = ['nbconvert'],
183 nbconvert = ['nbconvert'],
184 )
184 )
185
185
186 install_requires = [
186 install_requires = [
187 'setuptools>=18.5',
187 'setuptools>=18.5',
188 'jedi>=0.10',
188 'jedi>=0.10',
189 'decorator',
189 'decorator',
190 'pickleshare',
190 'pickleshare',
191 'simplegeneric>0.8',
191 'simplegeneric>0.8',
192 'traitlets>=4.2',
192 'traitlets>=4.2',
193 'prompt_toolkit>=1.0.15,<2.0.0',
193 'prompt_toolkit>=2.0.0,<2.1.0',
194 'pygments',
194 'pygments',
195 'backcall',
195 'backcall',
196 ]
196 ]
197
197
198 # Platform-specific dependencies:
198 # Platform-specific dependencies:
199 # This is the correct way to specify these,
199 # This is the correct way to specify these,
200 # but requires pip >= 6. pip < 6 ignores these.
200 # but requires pip >= 6. pip < 6 ignores these.
201
201
202 extras_require.update({
202 extras_require.update({
203 ':python_version == "3.4"': ['typing'],
203 ':python_version == "3.4"': ['typing'],
204 ':sys_platform != "win32"': ['pexpect'],
204 ':sys_platform != "win32"': ['pexpect'],
205 ':sys_platform == "darwin"': ['appnope'],
205 ':sys_platform == "darwin"': ['appnope'],
206 ':sys_platform == "win32"': ['colorama'],
206 ':sys_platform == "win32"': ['colorama'],
207 ':sys_platform == "win32" and python_version < "3.6"': ['win_unicode_console>=0.5'],
207 ':sys_platform == "win32" and python_version < "3.6"': ['win_unicode_console>=0.5'],
208 })
208 })
209 # FIXME: re-specify above platform dependencies for pip < 6
209 # FIXME: re-specify above platform dependencies for pip < 6
210 # These would result in non-portable bdists.
210 # These would result in non-portable bdists.
211 if not any(arg.startswith('bdist') for arg in sys.argv):
211 if not any(arg.startswith('bdist') for arg in sys.argv):
212 if sys.platform == 'darwin':
212 if sys.platform == 'darwin':
213 install_requires.extend(['appnope'])
213 install_requires.extend(['appnope'])
214
214
215 if not sys.platform.startswith('win'):
215 if not sys.platform.startswith('win'):
216 install_requires.append('pexpect')
216 install_requires.append('pexpect')
217
217
218 # workaround pypa/setuptools#147, where setuptools misspells
218 # workaround pypa/setuptools#147, where setuptools misspells
219 # platform_python_implementation as python_implementation
219 # platform_python_implementation as python_implementation
220 if 'setuptools' in sys.modules:
220 if 'setuptools' in sys.modules:
221 for key in list(extras_require):
221 for key in list(extras_require):
222 if 'platform_python_implementation' in key:
222 if 'platform_python_implementation' in key:
223 new_key = key.replace('platform_python_implementation', 'python_implementation')
223 new_key = key.replace('platform_python_implementation', 'python_implementation')
224 extras_require[new_key] = extras_require.pop(key)
224 extras_require[new_key] = extras_require.pop(key)
225
225
226 everything = set()
226 everything = set()
227 for key, deps in extras_require.items():
227 for key, deps in extras_require.items():
228 if ':' not in key:
228 if ':' not in key:
229 everything.update(deps)
229 everything.update(deps)
230 extras_require['all'] = everything
230 extras_require['all'] = everything
231
231
232 if 'setuptools' in sys.modules:
232 if 'setuptools' in sys.modules:
233 setuptools_extra_args['python_requires'] = '>=3.4'
233 setuptools_extra_args['python_requires'] = '>=3.4'
234 setuptools_extra_args['zip_safe'] = False
234 setuptools_extra_args['zip_safe'] = False
235 setuptools_extra_args['entry_points'] = {
235 setuptools_extra_args['entry_points'] = {
236 'console_scripts': find_entry_points(),
236 'console_scripts': find_entry_points(),
237 'pygments.lexers': [
237 'pygments.lexers': [
238 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
238 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
239 'ipython = IPython.lib.lexers:IPythonLexer',
239 'ipython = IPython.lib.lexers:IPythonLexer',
240 'ipython3 = IPython.lib.lexers:IPython3Lexer',
240 'ipython3 = IPython.lib.lexers:IPython3Lexer',
241 ],
241 ],
242 }
242 }
243 setup_args['extras_require'] = extras_require
243 setup_args['extras_require'] = extras_require
244 setup_args['install_requires'] = install_requires
244 setup_args['install_requires'] = install_requires
245
245
246 else:
246 else:
247 # scripts has to be a non-empty list, or install_scripts isn't called
247 # scripts has to be a non-empty list, or install_scripts isn't called
248 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
248 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
249
249
250 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
250 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
251
251
252 #---------------------------------------------------------------------------
252 #---------------------------------------------------------------------------
253 # Do the actual setup now
253 # Do the actual setup now
254 #---------------------------------------------------------------------------
254 #---------------------------------------------------------------------------
255
255
256 setup_args.update(setuptools_extra_args)
256 setup_args.update(setuptools_extra_args)
257
257
258
258
259
259
260 def main():
260 def main():
261 setup(**setup_args)
261 setup(**setup_args)
262
262
263 if __name__ == '__main__':
263 if __name__ == '__main__':
264 main()
264 main()
General Comments 0
You need to be logged in to leave comments. Login now