##// END OF EJS Templates
Merge pull request #11973 from jonathanslenders/run-prompt-in-different-asyncio-loop...
Matthias Bussonnier -
r25280:e9d59e53 merge
parent child Browse files
Show More
@@ -1,610 +1,623
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2
2
3 import asyncio
3 import os
4 import os
4 import sys
5 import sys
5 import warnings
6 import warnings
6 from warnings import warn
7 from warnings import warn
7
8
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.utils import io
10 from IPython.utils import io
10 from IPython.utils.py3compat import input
11 from IPython.utils.py3compat import input
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
12 from IPython.utils.process import abbrev_cwd
13 from IPython.utils.process import abbrev_cwd
13 from traitlets import (
14 from traitlets import (
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 Any, validate
16 Any, validate
16 )
17 )
17
18
18 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 from prompt_toolkit.formatted_text import PygmentsTokens
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.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.output import ColorDepth
24 from prompt_toolkit.output import ColorDepth
24 from prompt_toolkit.patch_stdout import patch_stdout
25 from prompt_toolkit.patch_stdout import patch_stdout
25 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
26 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
26 from prompt_toolkit.styles import DynamicStyle, merge_styles
27 from prompt_toolkit.styles import DynamicStyle, merge_styles
27 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
28 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
28 from prompt_toolkit import __version__ as ptk_version
29 from prompt_toolkit import __version__ as ptk_version
29
30
30 from pygments.styles import get_style_by_name
31 from pygments.styles import get_style_by_name
31 from pygments.style import Style
32 from pygments.style import Style
32 from pygments.token import Token
33 from pygments.token import Token
33
34
34 from .debugger import TerminalPdb, Pdb
35 from .debugger import TerminalPdb, Pdb
35 from .magics import TerminalMagics
36 from .magics import TerminalMagics
36 from .pt_inputhooks import get_inputhook_name_and_func
37 from .pt_inputhooks import get_inputhook_name_and_func
37 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
38 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
38 from .ptutils import IPythonPTCompleter, IPythonPTLexer
39 from .ptutils import IPythonPTCompleter, IPythonPTLexer
39 from .shortcuts import create_ipython_shortcuts
40 from .shortcuts import create_ipython_shortcuts
40
41
41 DISPLAY_BANNER_DEPRECATED = object()
42 DISPLAY_BANNER_DEPRECATED = object()
42 PTK3 = ptk_version.startswith('3.')
43 PTK3 = ptk_version.startswith('3.')
43
44
44
45
45 class _NoStyle(Style): pass
46 class _NoStyle(Style): pass
46
47
47
48
48
49
49 _style_overrides_light_bg = {
50 _style_overrides_light_bg = {
50 Token.Prompt: '#0000ff',
51 Token.Prompt: '#0000ff',
51 Token.PromptNum: '#0000ee bold',
52 Token.PromptNum: '#0000ee bold',
52 Token.OutPrompt: '#cc0000',
53 Token.OutPrompt: '#cc0000',
53 Token.OutPromptNum: '#bb0000 bold',
54 Token.OutPromptNum: '#bb0000 bold',
54 }
55 }
55
56
56 _style_overrides_linux = {
57 _style_overrides_linux = {
57 Token.Prompt: '#00cc00',
58 Token.Prompt: '#00cc00',
58 Token.PromptNum: '#00bb00 bold',
59 Token.PromptNum: '#00bb00 bold',
59 Token.OutPrompt: '#cc0000',
60 Token.OutPrompt: '#cc0000',
60 Token.OutPromptNum: '#bb0000 bold',
61 Token.OutPromptNum: '#bb0000 bold',
61 }
62 }
62
63
63 def get_default_editor():
64 def get_default_editor():
64 try:
65 try:
65 return os.environ['EDITOR']
66 return os.environ['EDITOR']
66 except KeyError:
67 except KeyError:
67 pass
68 pass
68 except UnicodeError:
69 except UnicodeError:
69 warn("$EDITOR environment variable is not pure ASCII. Using platform "
70 warn("$EDITOR environment variable is not pure ASCII. Using platform "
70 "default editor.")
71 "default editor.")
71
72
72 if os.name == 'posix':
73 if os.name == 'posix':
73 return 'vi' # the only one guaranteed to be there!
74 return 'vi' # the only one guaranteed to be there!
74 else:
75 else:
75 return 'notepad' # same in Windows!
76 return 'notepad' # same in Windows!
76
77
77 # conservatively check for tty
78 # conservatively check for tty
78 # overridden streams can result in things like:
79 # overridden streams can result in things like:
79 # - sys.stdin = None
80 # - sys.stdin = None
80 # - no isatty method
81 # - no isatty method
81 for _name in ('stdin', 'stdout', 'stderr'):
82 for _name in ('stdin', 'stdout', 'stderr'):
82 _stream = getattr(sys, _name)
83 _stream = getattr(sys, _name)
83 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
84 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
84 _is_tty = False
85 _is_tty = False
85 break
86 break
86 else:
87 else:
87 _is_tty = True
88 _is_tty = True
88
89
89
90
90 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
91 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
91
92
92 def black_reformat_handler(text_before_cursor):
93 def black_reformat_handler(text_before_cursor):
93 import black
94 import black
94 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
95 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
95 if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
96 if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
96 formatted_text = formatted_text[:-1]
97 formatted_text = formatted_text[:-1]
97 return formatted_text
98 return formatted_text
98
99
99
100
100 class TerminalInteractiveShell(InteractiveShell):
101 class TerminalInteractiveShell(InteractiveShell):
101 mime_renderers = Dict().tag(config=True)
102 mime_renderers = Dict().tag(config=True)
102
103
103 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
104 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
104 'to reserve for the completion menu'
105 'to reserve for the completion menu'
105 ).tag(config=True)
106 ).tag(config=True)
106
107
107 pt_app = None
108 pt_app = None
108 debugger_history = None
109 debugger_history = None
109
110
110 simple_prompt = Bool(_use_simple_prompt,
111 simple_prompt = Bool(_use_simple_prompt,
111 help="""Use `raw_input` for the REPL, without completion and prompt colors.
112 help="""Use `raw_input` for the REPL, without completion and prompt colors.
112
113
113 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
114 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
114 IPython own testing machinery, and emacs inferior-shell integration through elpy.
115 IPython own testing machinery, and emacs inferior-shell integration through elpy.
115
116
116 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
117 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
117 environment variable is set, or the current terminal is not a tty."""
118 environment variable is set, or the current terminal is not a tty."""
118 ).tag(config=True)
119 ).tag(config=True)
119
120
120 @property
121 @property
121 def debugger_cls(self):
122 def debugger_cls(self):
122 return Pdb if self.simple_prompt else TerminalPdb
123 return Pdb if self.simple_prompt else TerminalPdb
123
124
124 confirm_exit = Bool(True,
125 confirm_exit = Bool(True,
125 help="""
126 help="""
126 Set to confirm when you try to exit IPython with an EOF (Control-D
127 Set to confirm when you try to exit IPython with an EOF (Control-D
127 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
128 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
128 you can force a direct exit without any confirmation.""",
129 you can force a direct exit without any confirmation.""",
129 ).tag(config=True)
130 ).tag(config=True)
130
131
131 editing_mode = Unicode('emacs',
132 editing_mode = Unicode('emacs',
132 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
133 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
133 ).tag(config=True)
134 ).tag(config=True)
134
135
135 autoformatter = Unicode(None,
136 autoformatter = Unicode(None,
136 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
137 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
137 allow_none=True
138 allow_none=True
138 ).tag(config=True)
139 ).tag(config=True)
139
140
140 mouse_support = Bool(False,
141 mouse_support = Bool(False,
141 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
142 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
142 ).tag(config=True)
143 ).tag(config=True)
143
144
144 # We don't load the list of styles for the help string, because loading
145 # We don't load the list of styles for the help string, because loading
145 # Pygments plugins takes time and can cause unexpected errors.
146 # Pygments plugins takes time and can cause unexpected errors.
146 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
147 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
147 help="""The name or class of a Pygments style to use for syntax
148 help="""The name or class of a Pygments style to use for syntax
148 highlighting. To see available styles, run `pygmentize -L styles`."""
149 highlighting. To see available styles, run `pygmentize -L styles`."""
149 ).tag(config=True)
150 ).tag(config=True)
150
151
151 @validate('editing_mode')
152 @validate('editing_mode')
152 def _validate_editing_mode(self, proposal):
153 def _validate_editing_mode(self, proposal):
153 if proposal['value'].lower() == 'vim':
154 if proposal['value'].lower() == 'vim':
154 proposal['value']= 'vi'
155 proposal['value']= 'vi'
155 elif proposal['value'].lower() == 'default':
156 elif proposal['value'].lower() == 'default':
156 proposal['value']= 'emacs'
157 proposal['value']= 'emacs'
157
158
158 if hasattr(EditingMode, proposal['value'].upper()):
159 if hasattr(EditingMode, proposal['value'].upper()):
159 return proposal['value'].lower()
160 return proposal['value'].lower()
160
161
161 return self.editing_mode
162 return self.editing_mode
162
163
163
164
164 @observe('editing_mode')
165 @observe('editing_mode')
165 def _editing_mode(self, change):
166 def _editing_mode(self, change):
166 u_mode = change.new.upper()
167 u_mode = change.new.upper()
167 if self.pt_app:
168 if self.pt_app:
168 self.pt_app.editing_mode = u_mode
169 self.pt_app.editing_mode = u_mode
169
170
170 @observe('autoformatter')
171 @observe('autoformatter')
171 def _autoformatter_changed(self, change):
172 def _autoformatter_changed(self, change):
172 formatter = change.new
173 formatter = change.new
173 if formatter is None:
174 if formatter is None:
174 self.reformat_handler = lambda x:x
175 self.reformat_handler = lambda x:x
175 elif formatter == 'black':
176 elif formatter == 'black':
176 self.reformat_handler = black_reformat_handler
177 self.reformat_handler = black_reformat_handler
177 else:
178 else:
178 raise ValueError
179 raise ValueError
179
180
180 @observe('highlighting_style')
181 @observe('highlighting_style')
181 @observe('colors')
182 @observe('colors')
182 def _highlighting_style_changed(self, change):
183 def _highlighting_style_changed(self, change):
183 self.refresh_style()
184 self.refresh_style()
184
185
185 def refresh_style(self):
186 def refresh_style(self):
186 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
187 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
187
188
188
189
189 highlighting_style_overrides = Dict(
190 highlighting_style_overrides = Dict(
190 help="Override highlighting format for specific tokens"
191 help="Override highlighting format for specific tokens"
191 ).tag(config=True)
192 ).tag(config=True)
192
193
193 true_color = Bool(False,
194 true_color = Bool(False,
194 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
195 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
195 "If your terminal supports true color, the following command "
196 "If your terminal supports true color, the following command "
196 "should print 'TRUECOLOR' in orange: "
197 "should print 'TRUECOLOR' in orange: "
197 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
198 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
198 ).tag(config=True)
199 ).tag(config=True)
199
200
200 editor = Unicode(get_default_editor(),
201 editor = Unicode(get_default_editor(),
201 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
202 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
202 ).tag(config=True)
203 ).tag(config=True)
203
204
204 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
205 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
205
206
206 prompts = Instance(Prompts)
207 prompts = Instance(Prompts)
207
208
208 @default('prompts')
209 @default('prompts')
209 def _prompts_default(self):
210 def _prompts_default(self):
210 return self.prompts_class(self)
211 return self.prompts_class(self)
211
212
212 # @observe('prompts')
213 # @observe('prompts')
213 # def _(self, change):
214 # def _(self, change):
214 # self._update_layout()
215 # self._update_layout()
215
216
216 @default('displayhook_class')
217 @default('displayhook_class')
217 def _displayhook_class_default(self):
218 def _displayhook_class_default(self):
218 return RichPromptDisplayHook
219 return RichPromptDisplayHook
219
220
220 term_title = Bool(True,
221 term_title = Bool(True,
221 help="Automatically set the terminal title"
222 help="Automatically set the terminal title"
222 ).tag(config=True)
223 ).tag(config=True)
223
224
224 term_title_format = Unicode("IPython: {cwd}",
225 term_title_format = Unicode("IPython: {cwd}",
225 help="Customize the terminal title format. This is a python format string. " +
226 help="Customize the terminal title format. This is a python format string. " +
226 "Available substitutions are: {cwd}."
227 "Available substitutions are: {cwd}."
227 ).tag(config=True)
228 ).tag(config=True)
228
229
229 display_completions = Enum(('column', 'multicolumn','readlinelike'),
230 display_completions = Enum(('column', 'multicolumn','readlinelike'),
230 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
231 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
231 "'readlinelike'. These options are for `prompt_toolkit`, see "
232 "'readlinelike'. These options are for `prompt_toolkit`, see "
232 "`prompt_toolkit` documentation for more information."
233 "`prompt_toolkit` documentation for more information."
233 ),
234 ),
234 default_value='multicolumn').tag(config=True)
235 default_value='multicolumn').tag(config=True)
235
236
236 highlight_matching_brackets = Bool(True,
237 highlight_matching_brackets = Bool(True,
237 help="Highlight matching brackets.",
238 help="Highlight matching brackets.",
238 ).tag(config=True)
239 ).tag(config=True)
239
240
240 extra_open_editor_shortcuts = Bool(False,
241 extra_open_editor_shortcuts = Bool(False,
241 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
242 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
242 "This is in addition to the F2 binding, which is always enabled."
243 "This is in addition to the F2 binding, which is always enabled."
243 ).tag(config=True)
244 ).tag(config=True)
244
245
245 handle_return = Any(None,
246 handle_return = Any(None,
246 help="Provide an alternative handler to be called when the user presses "
247 help="Provide an alternative handler to be called when the user presses "
247 "Return. This is an advanced option intended for debugging, which "
248 "Return. This is an advanced option intended for debugging, which "
248 "may be changed or removed in later releases."
249 "may be changed or removed in later releases."
249 ).tag(config=True)
250 ).tag(config=True)
250
251
251 enable_history_search = Bool(True,
252 enable_history_search = Bool(True,
252 help="Allows to enable/disable the prompt toolkit history search"
253 help="Allows to enable/disable the prompt toolkit history search"
253 ).tag(config=True)
254 ).tag(config=True)
254
255
255 prompt_includes_vi_mode = Bool(True,
256 prompt_includes_vi_mode = Bool(True,
256 help="Display the current vi mode (when using vi editing mode)."
257 help="Display the current vi mode (when using vi editing mode)."
257 ).tag(config=True)
258 ).tag(config=True)
258
259
259 @observe('term_title')
260 @observe('term_title')
260 def init_term_title(self, change=None):
261 def init_term_title(self, change=None):
261 # Enable or disable the terminal title.
262 # Enable or disable the terminal title.
262 if self.term_title:
263 if self.term_title:
263 toggle_set_term_title(True)
264 toggle_set_term_title(True)
264 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
265 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
265 else:
266 else:
266 toggle_set_term_title(False)
267 toggle_set_term_title(False)
267
268
268 def restore_term_title(self):
269 def restore_term_title(self):
269 if self.term_title:
270 if self.term_title:
270 restore_term_title()
271 restore_term_title()
271
272
272 def init_display_formatter(self):
273 def init_display_formatter(self):
273 super(TerminalInteractiveShell, self).init_display_formatter()
274 super(TerminalInteractiveShell, self).init_display_formatter()
274 # terminal only supports plain text
275 # terminal only supports plain text
275 self.display_formatter.active_types = ['text/plain']
276 self.display_formatter.active_types = ['text/plain']
276 # disable `_ipython_display_`
277 # disable `_ipython_display_`
277 self.display_formatter.ipython_display_formatter.enabled = False
278 self.display_formatter.ipython_display_formatter.enabled = False
278
279
279 def init_prompt_toolkit_cli(self):
280 def init_prompt_toolkit_cli(self):
280 if self.simple_prompt:
281 if self.simple_prompt:
281 # Fall back to plain non-interactive output for tests.
282 # Fall back to plain non-interactive output for tests.
282 # This is very limited.
283 # This is very limited.
283 def prompt():
284 def prompt():
284 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
285 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
285 lines = [input(prompt_text)]
286 lines = [input(prompt_text)]
286 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
287 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
287 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
288 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
288 lines.append( input(prompt_continuation) )
289 lines.append( input(prompt_continuation) )
289 return '\n'.join(lines)
290 return '\n'.join(lines)
290 self.prompt_for_code = prompt
291 self.prompt_for_code = prompt
291 return
292 return
292
293
293 # Set up keyboard shortcuts
294 # Set up keyboard shortcuts
294 key_bindings = create_ipython_shortcuts(self)
295 key_bindings = create_ipython_shortcuts(self)
295
296
296 # Pre-populate history from IPython's history database
297 # Pre-populate history from IPython's history database
297 history = InMemoryHistory()
298 history = InMemoryHistory()
298 last_cell = u""
299 last_cell = u""
299 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
300 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
300 include_latest=True):
301 include_latest=True):
301 # Ignore blank lines and consecutive duplicates
302 # Ignore blank lines and consecutive duplicates
302 cell = cell.rstrip()
303 cell = cell.rstrip()
303 if cell and (cell != last_cell):
304 if cell and (cell != last_cell):
304 history.append_string(cell)
305 history.append_string(cell)
305 last_cell = cell
306 last_cell = cell
306
307
307 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
308 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
308 self.style = DynamicStyle(lambda: self._style)
309 self.style = DynamicStyle(lambda: self._style)
309
310
310 editing_mode = getattr(EditingMode, self.editing_mode.upper())
311 editing_mode = getattr(EditingMode, self.editing_mode.upper())
311
312
313 self.pt_loop = asyncio.new_event_loop()
312 self.pt_app = PromptSession(
314 self.pt_app = PromptSession(
313 editing_mode=editing_mode,
315 editing_mode=editing_mode,
314 key_bindings=key_bindings,
316 key_bindings=key_bindings,
315 history=history,
317 history=history,
316 completer=IPythonPTCompleter(shell=self),
318 completer=IPythonPTCompleter(shell=self),
317 enable_history_search = self.enable_history_search,
319 enable_history_search = self.enable_history_search,
318 style=self.style,
320 style=self.style,
319 include_default_pygments_style=False,
321 include_default_pygments_style=False,
320 mouse_support=self.mouse_support,
322 mouse_support=self.mouse_support,
321 enable_open_in_editor=self.extra_open_editor_shortcuts,
323 enable_open_in_editor=self.extra_open_editor_shortcuts,
322 color_depth=self.color_depth,
324 color_depth=self.color_depth,
323 **self._extra_prompt_options())
325 **self._extra_prompt_options())
324
326
325 def _make_style_from_name_or_cls(self, name_or_cls):
327 def _make_style_from_name_or_cls(self, name_or_cls):
326 """
328 """
327 Small wrapper that make an IPython compatible style from a style name
329 Small wrapper that make an IPython compatible style from a style name
328
330
329 We need that to add style for prompt ... etc.
331 We need that to add style for prompt ... etc.
330 """
332 """
331 style_overrides = {}
333 style_overrides = {}
332 if name_or_cls == 'legacy':
334 if name_or_cls == 'legacy':
333 legacy = self.colors.lower()
335 legacy = self.colors.lower()
334 if legacy == 'linux':
336 if legacy == 'linux':
335 style_cls = get_style_by_name('monokai')
337 style_cls = get_style_by_name('monokai')
336 style_overrides = _style_overrides_linux
338 style_overrides = _style_overrides_linux
337 elif legacy == 'lightbg':
339 elif legacy == 'lightbg':
338 style_overrides = _style_overrides_light_bg
340 style_overrides = _style_overrides_light_bg
339 style_cls = get_style_by_name('pastie')
341 style_cls = get_style_by_name('pastie')
340 elif legacy == 'neutral':
342 elif legacy == 'neutral':
341 # The default theme needs to be visible on both a dark background
343 # The default theme needs to be visible on both a dark background
342 # and a light background, because we can't tell what the terminal
344 # and a light background, because we can't tell what the terminal
343 # looks like. These tweaks to the default theme help with that.
345 # looks like. These tweaks to the default theme help with that.
344 style_cls = get_style_by_name('default')
346 style_cls = get_style_by_name('default')
345 style_overrides.update({
347 style_overrides.update({
346 Token.Number: '#007700',
348 Token.Number: '#007700',
347 Token.Operator: 'noinherit',
349 Token.Operator: 'noinherit',
348 Token.String: '#BB6622',
350 Token.String: '#BB6622',
349 Token.Name.Function: '#2080D0',
351 Token.Name.Function: '#2080D0',
350 Token.Name.Class: 'bold #2080D0',
352 Token.Name.Class: 'bold #2080D0',
351 Token.Name.Namespace: 'bold #2080D0',
353 Token.Name.Namespace: 'bold #2080D0',
352 Token.Prompt: '#009900',
354 Token.Prompt: '#009900',
353 Token.PromptNum: '#ansibrightgreen bold',
355 Token.PromptNum: '#ansibrightgreen bold',
354 Token.OutPrompt: '#990000',
356 Token.OutPrompt: '#990000',
355 Token.OutPromptNum: '#ansibrightred bold',
357 Token.OutPromptNum: '#ansibrightred bold',
356 })
358 })
357
359
358 # Hack: Due to limited color support on the Windows console
360 # Hack: Due to limited color support on the Windows console
359 # the prompt colors will be wrong without this
361 # the prompt colors will be wrong without this
360 if os.name == 'nt':
362 if os.name == 'nt':
361 style_overrides.update({
363 style_overrides.update({
362 Token.Prompt: '#ansidarkgreen',
364 Token.Prompt: '#ansidarkgreen',
363 Token.PromptNum: '#ansigreen bold',
365 Token.PromptNum: '#ansigreen bold',
364 Token.OutPrompt: '#ansidarkred',
366 Token.OutPrompt: '#ansidarkred',
365 Token.OutPromptNum: '#ansired bold',
367 Token.OutPromptNum: '#ansired bold',
366 })
368 })
367 elif legacy =='nocolor':
369 elif legacy =='nocolor':
368 style_cls=_NoStyle
370 style_cls=_NoStyle
369 style_overrides = {}
371 style_overrides = {}
370 else :
372 else :
371 raise ValueError('Got unknown colors: ', legacy)
373 raise ValueError('Got unknown colors: ', legacy)
372 else :
374 else :
373 if isinstance(name_or_cls, str):
375 if isinstance(name_or_cls, str):
374 style_cls = get_style_by_name(name_or_cls)
376 style_cls = get_style_by_name(name_or_cls)
375 else:
377 else:
376 style_cls = name_or_cls
378 style_cls = name_or_cls
377 style_overrides = {
379 style_overrides = {
378 Token.Prompt: '#009900',
380 Token.Prompt: '#009900',
379 Token.PromptNum: '#ansibrightgreen bold',
381 Token.PromptNum: '#ansibrightgreen bold',
380 Token.OutPrompt: '#990000',
382 Token.OutPrompt: '#990000',
381 Token.OutPromptNum: '#ansibrightred bold',
383 Token.OutPromptNum: '#ansibrightred bold',
382 }
384 }
383 style_overrides.update(self.highlighting_style_overrides)
385 style_overrides.update(self.highlighting_style_overrides)
384 style = merge_styles([
386 style = merge_styles([
385 style_from_pygments_cls(style_cls),
387 style_from_pygments_cls(style_cls),
386 style_from_pygments_dict(style_overrides),
388 style_from_pygments_dict(style_overrides),
387 ])
389 ])
388
390
389 return style
391 return style
390
392
391 @property
393 @property
392 def pt_complete_style(self):
394 def pt_complete_style(self):
393 return {
395 return {
394 'multicolumn': CompleteStyle.MULTI_COLUMN,
396 'multicolumn': CompleteStyle.MULTI_COLUMN,
395 'column': CompleteStyle.COLUMN,
397 'column': CompleteStyle.COLUMN,
396 'readlinelike': CompleteStyle.READLINE_LIKE,
398 'readlinelike': CompleteStyle.READLINE_LIKE,
397 }[self.display_completions]
399 }[self.display_completions]
398
400
399 @property
401 @property
400 def color_depth(self):
402 def color_depth(self):
401 return (ColorDepth.TRUE_COLOR if self.true_color else None)
403 return (ColorDepth.TRUE_COLOR if self.true_color else None)
402
404
403 def _extra_prompt_options(self):
405 def _extra_prompt_options(self):
404 """
406 """
405 Return the current layout option for the current Terminal InteractiveShell
407 Return the current layout option for the current Terminal InteractiveShell
406 """
408 """
407 def get_message():
409 def get_message():
408 return PygmentsTokens(self.prompts.in_prompt_tokens())
410 return PygmentsTokens(self.prompts.in_prompt_tokens())
409
411
410 if self.editing_mode == 'emacs':
412 if self.editing_mode == 'emacs':
411 # with emacs mode the prompt is (usually) static, so we call only
413 # with emacs mode the prompt is (usually) static, so we call only
412 # the function once. With VI mode it can toggle between [ins] and
414 # the function once. With VI mode it can toggle between [ins] and
413 # [nor] so we can't precompute.
415 # [nor] so we can't precompute.
414 # here I'm going to favor the default keybinding which almost
416 # here I'm going to favor the default keybinding which almost
415 # everybody uses to decrease CPU usage.
417 # everybody uses to decrease CPU usage.
416 # if we have issues with users with custom Prompts we can see how to
418 # if we have issues with users with custom Prompts we can see how to
417 # work around this.
419 # work around this.
418 get_message = get_message()
420 get_message = get_message()
419
421
420 options = {
422 options = {
421 'complete_in_thread': False,
423 'complete_in_thread': False,
422 'lexer':IPythonPTLexer(),
424 'lexer':IPythonPTLexer(),
423 'reserve_space_for_menu':self.space_for_menu,
425 'reserve_space_for_menu':self.space_for_menu,
424 'message': get_message,
426 'message': get_message,
425 'prompt_continuation': (
427 'prompt_continuation': (
426 lambda width, lineno, is_soft_wrap:
428 lambda width, lineno, is_soft_wrap:
427 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
429 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
428 'multiline': True,
430 'multiline': True,
429 'complete_style': self.pt_complete_style,
431 'complete_style': self.pt_complete_style,
430
432
431 # Highlight matching brackets, but only when this setting is
433 # Highlight matching brackets, but only when this setting is
432 # enabled, and only when the DEFAULT_BUFFER has the focus.
434 # enabled, and only when the DEFAULT_BUFFER has the focus.
433 'input_processors': [ConditionalProcessor(
435 'input_processors': [ConditionalProcessor(
434 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
436 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
435 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
437 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
436 Condition(lambda: self.highlight_matching_brackets))],
438 Condition(lambda: self.highlight_matching_brackets))],
437 }
439 }
438 if not PTK3:
440 if not PTK3:
439 options['inputhook'] = self.inputhook
441 options['inputhook'] = self.inputhook
440
442
441 return options
443 return options
442
444
443 def prompt_for_code(self):
445 def prompt_for_code(self):
444 if self.rl_next_input:
446 if self.rl_next_input:
445 default = self.rl_next_input
447 default = self.rl_next_input
446 self.rl_next_input = None
448 self.rl_next_input = None
447 else:
449 else:
448 default = ''
450 default = ''
449
451
450 with patch_stdout(raw=True):
452 with patch_stdout(raw=True):
451 text = self.pt_app.prompt(
453 # In order to make sure that asyncio code written in the
452 default=default,
454 # interactive shell doesn't interfere with the prompt, we run the
453 # pre_run=self.pre_prompt,# reset_current_buffer=True,
455 # prompt in a different event loop.
454 **self._extra_prompt_options())
456 # If we don't do this, people could spawn coroutine with a
457 # while/true inside which will freeze the prompt.
458
459 old_loop = asyncio.get_event_loop()
460 asyncio.set_event_loop(self.pt_loop)
461 try:
462 text = self.pt_app.prompt(
463 default=default,
464 **self._extra_prompt_options())
465 finally:
466 # Restore the original event loop.
467 asyncio.set_event_loop(old_loop)
455 return text
468 return text
456
469
457 def enable_win_unicode_console(self):
470 def enable_win_unicode_console(self):
458 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
471 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
459 # console by default, so WUC shouldn't be needed.
472 # console by default, so WUC shouldn't be needed.
460 from warnings import warn
473 from warnings import warn
461 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
474 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
462 DeprecationWarning,
475 DeprecationWarning,
463 stacklevel=2)
476 stacklevel=2)
464
477
465 def init_io(self):
478 def init_io(self):
466 if sys.platform not in {'win32', 'cli'}:
479 if sys.platform not in {'win32', 'cli'}:
467 return
480 return
468
481
469 import colorama
482 import colorama
470 colorama.init()
483 colorama.init()
471
484
472 # For some reason we make these wrappers around stdout/stderr.
485 # For some reason we make these wrappers around stdout/stderr.
473 # For now, we need to reset them so all output gets coloured.
486 # For now, we need to reset them so all output gets coloured.
474 # https://github.com/ipython/ipython/issues/8669
487 # https://github.com/ipython/ipython/issues/8669
475 # io.std* are deprecated, but don't show our own deprecation warnings
488 # io.std* are deprecated, but don't show our own deprecation warnings
476 # during initialization of the deprecated API.
489 # during initialization of the deprecated API.
477 with warnings.catch_warnings():
490 with warnings.catch_warnings():
478 warnings.simplefilter('ignore', DeprecationWarning)
491 warnings.simplefilter('ignore', DeprecationWarning)
479 io.stdout = io.IOStream(sys.stdout)
492 io.stdout = io.IOStream(sys.stdout)
480 io.stderr = io.IOStream(sys.stderr)
493 io.stderr = io.IOStream(sys.stderr)
481
494
482 def init_magics(self):
495 def init_magics(self):
483 super(TerminalInteractiveShell, self).init_magics()
496 super(TerminalInteractiveShell, self).init_magics()
484 self.register_magics(TerminalMagics)
497 self.register_magics(TerminalMagics)
485
498
486 def init_alias(self):
499 def init_alias(self):
487 # The parent class defines aliases that can be safely used with any
500 # The parent class defines aliases that can be safely used with any
488 # frontend.
501 # frontend.
489 super(TerminalInteractiveShell, self).init_alias()
502 super(TerminalInteractiveShell, self).init_alias()
490
503
491 # Now define aliases that only make sense on the terminal, because they
504 # Now define aliases that only make sense on the terminal, because they
492 # need direct access to the console in a way that we can't emulate in
505 # need direct access to the console in a way that we can't emulate in
493 # GUI or web frontend
506 # GUI or web frontend
494 if os.name == 'posix':
507 if os.name == 'posix':
495 for cmd in ('clear', 'more', 'less', 'man'):
508 for cmd in ('clear', 'more', 'less', 'man'):
496 self.alias_manager.soft_define_alias(cmd, cmd)
509 self.alias_manager.soft_define_alias(cmd, cmd)
497
510
498
511
499 def __init__(self, *args, **kwargs):
512 def __init__(self, *args, **kwargs):
500 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
513 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
501 self.init_prompt_toolkit_cli()
514 self.init_prompt_toolkit_cli()
502 self.init_term_title()
515 self.init_term_title()
503 self.keep_running = True
516 self.keep_running = True
504
517
505 self.debugger_history = InMemoryHistory()
518 self.debugger_history = InMemoryHistory()
506
519
507 def ask_exit(self):
520 def ask_exit(self):
508 self.keep_running = False
521 self.keep_running = False
509
522
510 rl_next_input = None
523 rl_next_input = None
511
524
512 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
525 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
513
526
514 if display_banner is not DISPLAY_BANNER_DEPRECATED:
527 if display_banner is not DISPLAY_BANNER_DEPRECATED:
515 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
528 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
516
529
517 self.keep_running = True
530 self.keep_running = True
518 while self.keep_running:
531 while self.keep_running:
519 print(self.separate_in, end='')
532 print(self.separate_in, end='')
520
533
521 try:
534 try:
522 code = self.prompt_for_code()
535 code = self.prompt_for_code()
523 except EOFError:
536 except EOFError:
524 if (not self.confirm_exit) \
537 if (not self.confirm_exit) \
525 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
538 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
526 self.ask_exit()
539 self.ask_exit()
527
540
528 else:
541 else:
529 if code:
542 if code:
530 self.run_cell(code, store_history=True)
543 self.run_cell(code, store_history=True)
531
544
532 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
545 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
533 # An extra layer of protection in case someone mashing Ctrl-C breaks
546 # An extra layer of protection in case someone mashing Ctrl-C breaks
534 # out of our internal code.
547 # out of our internal code.
535 if display_banner is not DISPLAY_BANNER_DEPRECATED:
548 if display_banner is not DISPLAY_BANNER_DEPRECATED:
536 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
549 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
537 while True:
550 while True:
538 try:
551 try:
539 self.interact()
552 self.interact()
540 break
553 break
541 except KeyboardInterrupt as e:
554 except KeyboardInterrupt as e:
542 print("\n%s escaped interact()\n" % type(e).__name__)
555 print("\n%s escaped interact()\n" % type(e).__name__)
543 finally:
556 finally:
544 # An interrupt during the eventloop will mess up the
557 # An interrupt during the eventloop will mess up the
545 # internal state of the prompt_toolkit library.
558 # internal state of the prompt_toolkit library.
546 # Stopping the eventloop fixes this, see
559 # Stopping the eventloop fixes this, see
547 # https://github.com/ipython/ipython/pull/9867
560 # https://github.com/ipython/ipython/pull/9867
548 if hasattr(self, '_eventloop'):
561 if hasattr(self, '_eventloop'):
549 self._eventloop.stop()
562 self._eventloop.stop()
550
563
551 self.restore_term_title()
564 self.restore_term_title()
552
565
553
566
554 _inputhook = None
567 _inputhook = None
555 def inputhook(self, context):
568 def inputhook(self, context):
556 if self._inputhook is not None:
569 if self._inputhook is not None:
557 self._inputhook(context)
570 self._inputhook(context)
558
571
559 active_eventloop = None
572 active_eventloop = None
560 def enable_gui(self, gui=None):
573 def enable_gui(self, gui=None):
561 if gui and (gui != 'inline') :
574 if gui and (gui != 'inline') :
562 self.active_eventloop, self._inputhook =\
575 self.active_eventloop, self._inputhook =\
563 get_inputhook_name_and_func(gui)
576 get_inputhook_name_and_func(gui)
564 else:
577 else:
565 self.active_eventloop = self._inputhook = None
578 self.active_eventloop = self._inputhook = None
566
579
567 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
580 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
568 # this inputhook.
581 # this inputhook.
569 if PTK3:
582 if PTK3:
570 if self._inputhook:
583 if self._inputhook:
571 from prompt_toolkit.eventloop import set_eventloop_with_inputhook
584 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
572 set_eventloop_with_inputhook(self._inputhook)
585 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
573 else:
586 else:
574 import asyncio
587 import asyncio
575 asyncio.set_event_loop(asyncio.new_event_loop())
588 self.pt_loop = asyncio.new_event_loop()
576
589
577 # Run !system commands directly, not through pipes, so terminal programs
590 # Run !system commands directly, not through pipes, so terminal programs
578 # work correctly.
591 # work correctly.
579 system = InteractiveShell.system_raw
592 system = InteractiveShell.system_raw
580
593
581 def auto_rewrite_input(self, cmd):
594 def auto_rewrite_input(self, cmd):
582 """Overridden from the parent class to use fancy rewriting prompt"""
595 """Overridden from the parent class to use fancy rewriting prompt"""
583 if not self.show_rewritten_input:
596 if not self.show_rewritten_input:
584 return
597 return
585
598
586 tokens = self.prompts.rewrite_prompt_tokens()
599 tokens = self.prompts.rewrite_prompt_tokens()
587 if self.pt_app:
600 if self.pt_app:
588 print_formatted_text(PygmentsTokens(tokens), end='',
601 print_formatted_text(PygmentsTokens(tokens), end='',
589 style=self.pt_app.app.style)
602 style=self.pt_app.app.style)
590 print(cmd)
603 print(cmd)
591 else:
604 else:
592 prompt = ''.join(s for t, s in tokens)
605 prompt = ''.join(s for t, s in tokens)
593 print(prompt, cmd, sep='')
606 print(prompt, cmd, sep='')
594
607
595 _prompts_before = None
608 _prompts_before = None
596 def switch_doctest_mode(self, mode):
609 def switch_doctest_mode(self, mode):
597 """Switch prompts to classic for %doctest_mode"""
610 """Switch prompts to classic for %doctest_mode"""
598 if mode:
611 if mode:
599 self._prompts_before = self.prompts
612 self._prompts_before = self.prompts
600 self.prompts = ClassicPrompts(self)
613 self.prompts = ClassicPrompts(self)
601 elif self._prompts_before:
614 elif self._prompts_before:
602 self.prompts = self._prompts_before
615 self.prompts = self._prompts_before
603 self._prompts_before = None
616 self._prompts_before = None
604 # self._update_layout()
617 # self._update_layout()
605
618
606
619
607 InteractiveShellABC.register(TerminalInteractiveShell)
620 InteractiveShellABC.register(TerminalInteractiveShell)
608
621
609 if __name__ == '__main__':
622 if __name__ == '__main__':
610 TerminalInteractiveShell.instance().interact()
623 TerminalInteractiveShell.instance().interact()
@@ -1,262 +1,262
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, 6):
29 if sys.version_info < (3, 6):
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.10+ supports Python 3.6 and above, following NEP 29.
45 IPython 7.10+ supports Python 3.6 and above, following NEP 29.
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 and 3.4 were supported up to IPython 6.x.
47 Python 3.3 and 3.4 were supported up to IPython 6.x.
48 Python 3.5 was supported with IPython 7.0 to 7.9.
48 Python 3.5 was supported with IPython 7.0 to 7.9.
49
49
50 See IPython `README.rst` file for more information:
50 See IPython `README.rst` file for more information:
51
51
52 https://github.com/ipython/ipython/blob/master/README.rst
52 https://github.com/ipython/ipython/blob/master/README.rst
53
53
54 Python {py} detected.
54 Python {py} detected.
55 {pip}
55 {pip}
56 """.format(py=sys.version_info, pip=pip_message )
56 """.format(py=sys.version_info, pip=pip_message )
57
57
58 print(error, file=sys.stderr)
58 print(error, file=sys.stderr)
59 sys.exit(1)
59 sys.exit(1)
60
60
61 # At least we're on the python version we need, move on.
61 # At least we're on the python version we need, move on.
62
62
63 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
63 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
64 # update it when the contents of directories change.
64 # update it when the contents of directories change.
65 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
65 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
66
66
67 from distutils.core import setup
67 from distutils.core import setup
68
68
69 # Our own imports
69 # Our own imports
70 from setupbase import target_update
70 from setupbase import target_update
71
71
72 from setupbase import (
72 from setupbase import (
73 setup_args,
73 setup_args,
74 find_packages,
74 find_packages,
75 find_package_data,
75 find_package_data,
76 check_package_data_first,
76 check_package_data_first,
77 find_entry_points,
77 find_entry_points,
78 build_scripts_entrypt,
78 build_scripts_entrypt,
79 find_data_files,
79 find_data_files,
80 git_prebuild,
80 git_prebuild,
81 install_symlinked,
81 install_symlinked,
82 install_lib_symlink,
82 install_lib_symlink,
83 install_scripts_for_symlink,
83 install_scripts_for_symlink,
84 unsymlink,
84 unsymlink,
85 )
85 )
86
86
87 isfile = os.path.isfile
87 isfile = os.path.isfile
88 pjoin = os.path.join
88 pjoin = os.path.join
89
89
90 #-------------------------------------------------------------------------------
90 #-------------------------------------------------------------------------------
91 # Handle OS specific things
91 # Handle OS specific things
92 #-------------------------------------------------------------------------------
92 #-------------------------------------------------------------------------------
93
93
94 if os.name in ('nt','dos'):
94 if os.name in ('nt','dos'):
95 os_name = 'windows'
95 os_name = 'windows'
96 else:
96 else:
97 os_name = os.name
97 os_name = os.name
98
98
99 # Under Windows, 'sdist' has not been supported. Now that the docs build with
99 # Under Windows, 'sdist' has not been supported. Now that the docs build with
100 # Sphinx it might work, but let's not turn it on until someone confirms that it
100 # Sphinx it might work, but let's not turn it on until someone confirms that it
101 # actually works.
101 # actually works.
102 if os_name == 'windows' and 'sdist' in sys.argv:
102 if os_name == 'windows' and 'sdist' in sys.argv:
103 print('The sdist command is not available under Windows. Exiting.')
103 print('The sdist command is not available under Windows. Exiting.')
104 sys.exit(1)
104 sys.exit(1)
105
105
106
106
107 #-------------------------------------------------------------------------------
107 #-------------------------------------------------------------------------------
108 # Things related to the IPython documentation
108 # Things related to the IPython documentation
109 #-------------------------------------------------------------------------------
109 #-------------------------------------------------------------------------------
110
110
111 # update the manuals when building a source dist
111 # update the manuals when building a source dist
112 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
112 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
113
113
114 # List of things to be updated. Each entry is a triplet of args for
114 # List of things to be updated. Each entry is a triplet of args for
115 # target_update()
115 # target_update()
116 to_update = [
116 to_update = [
117 ('docs/man/ipython.1.gz',
117 ('docs/man/ipython.1.gz',
118 ['docs/man/ipython.1'],
118 ['docs/man/ipython.1'],
119 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
119 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
120 ]
120 ]
121
121
122
122
123 [ target_update(*t) for t in to_update ]
123 [ target_update(*t) for t in to_update ]
124
124
125 #---------------------------------------------------------------------------
125 #---------------------------------------------------------------------------
126 # Find all the packages, package data, and data_files
126 # Find all the packages, package data, and data_files
127 #---------------------------------------------------------------------------
127 #---------------------------------------------------------------------------
128
128
129 packages = find_packages()
129 packages = find_packages()
130 package_data = find_package_data()
130 package_data = find_package_data()
131
131
132 data_files = find_data_files()
132 data_files = find_data_files()
133
133
134 setup_args['packages'] = packages
134 setup_args['packages'] = packages
135 setup_args['package_data'] = package_data
135 setup_args['package_data'] = package_data
136 setup_args['data_files'] = data_files
136 setup_args['data_files'] = data_files
137
137
138 #---------------------------------------------------------------------------
138 #---------------------------------------------------------------------------
139 # custom distutils commands
139 # custom distutils commands
140 #---------------------------------------------------------------------------
140 #---------------------------------------------------------------------------
141 # imports here, so they are after setuptools import if there was one
141 # imports here, so they are after setuptools import if there was one
142 from distutils.command.sdist import sdist
142 from distutils.command.sdist import sdist
143
143
144 setup_args['cmdclass'] = {
144 setup_args['cmdclass'] = {
145 'build_py': \
145 'build_py': \
146 check_package_data_first(git_prebuild('IPython')),
146 check_package_data_first(git_prebuild('IPython')),
147 'sdist' : git_prebuild('IPython', sdist),
147 'sdist' : git_prebuild('IPython', sdist),
148 'symlink': install_symlinked,
148 'symlink': install_symlinked,
149 'install_lib_symlink': install_lib_symlink,
149 'install_lib_symlink': install_lib_symlink,
150 'install_scripts_sym': install_scripts_for_symlink,
150 'install_scripts_sym': install_scripts_for_symlink,
151 'unsymlink': unsymlink,
151 'unsymlink': unsymlink,
152 }
152 }
153
153
154
154
155 #---------------------------------------------------------------------------
155 #---------------------------------------------------------------------------
156 # Handle scripts, dependencies, and setuptools specific things
156 # Handle scripts, dependencies, and setuptools specific things
157 #---------------------------------------------------------------------------
157 #---------------------------------------------------------------------------
158
158
159 # For some commands, use setuptools. Note that we do NOT list install here!
159 # For some commands, use setuptools. Note that we do NOT list install here!
160 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
160 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
161 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
161 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
162 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
162 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
163 'egg_info', 'easy_install', 'upload', 'install_egg_info',
163 'egg_info', 'easy_install', 'upload', 'install_egg_info',
164 }
164 }
165
165
166 if len(needs_setuptools.intersection(sys.argv)) > 0:
166 if len(needs_setuptools.intersection(sys.argv)) > 0:
167 import setuptools
167 import setuptools
168
168
169 # This dict is used for passing extra arguments that are setuptools
169 # This dict is used for passing extra arguments that are setuptools
170 # specific to setup
170 # specific to setup
171 setuptools_extra_args = {}
171 setuptools_extra_args = {}
172
172
173 # setuptools requirements
173 # setuptools requirements
174
174
175 extras_require = dict(
175 extras_require = dict(
176 parallel = ['ipyparallel'],
176 parallel = ['ipyparallel'],
177 qtconsole = ['qtconsole'],
177 qtconsole = ['qtconsole'],
178 doc = ['Sphinx>=1.3'],
178 doc = ['Sphinx>=1.3'],
179 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy>=1.14'],
179 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy>=1.14'],
180 terminal = [],
180 terminal = [],
181 kernel = ['ipykernel'],
181 kernel = ['ipykernel'],
182 nbformat = ['nbformat'],
182 nbformat = ['nbformat'],
183 notebook = ['notebook', 'ipywidgets'],
183 notebook = ['notebook', 'ipywidgets'],
184 nbconvert = ['nbconvert'],
184 nbconvert = ['nbconvert'],
185 )
185 )
186
186
187 install_requires = [
187 install_requires = [
188 'setuptools>=18.5',
188 'setuptools>=18.5',
189 'jedi>=0.10',
189 'jedi>=0.10',
190 'decorator',
190 'decorator',
191 'pickleshare',
191 'pickleshare',
192 'traitlets>=4.2',
192 'traitlets>=4.2',
193 'prompt_toolkit>=2.0.0,<3.1.0',
193 'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
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 ':sys_platform != "win32"': ['pexpect'],
203 ':sys_platform != "win32"': ['pexpect'],
204 ':sys_platform == "darwin"': ['appnope'],
204 ':sys_platform == "darwin"': ['appnope'],
205 ':sys_platform == "win32"': ['colorama'],
205 ':sys_platform == "win32"': ['colorama'],
206 })
206 })
207 # FIXME: re-specify above platform dependencies for pip < 6
207 # FIXME: re-specify above platform dependencies for pip < 6
208 # These would result in non-portable bdists.
208 # These would result in non-portable bdists.
209 if not any(arg.startswith('bdist') for arg in sys.argv):
209 if not any(arg.startswith('bdist') for arg in sys.argv):
210 if sys.platform == 'darwin':
210 if sys.platform == 'darwin':
211 install_requires.extend(['appnope'])
211 install_requires.extend(['appnope'])
212
212
213 if not sys.platform.startswith('win'):
213 if not sys.platform.startswith('win'):
214 install_requires.append('pexpect')
214 install_requires.append('pexpect')
215
215
216 # workaround pypa/setuptools#147, where setuptools misspells
216 # workaround pypa/setuptools#147, where setuptools misspells
217 # platform_python_implementation as python_implementation
217 # platform_python_implementation as python_implementation
218 if 'setuptools' in sys.modules:
218 if 'setuptools' in sys.modules:
219 for key in list(extras_require):
219 for key in list(extras_require):
220 if 'platform_python_implementation' in key:
220 if 'platform_python_implementation' in key:
221 new_key = key.replace('platform_python_implementation', 'python_implementation')
221 new_key = key.replace('platform_python_implementation', 'python_implementation')
222 extras_require[new_key] = extras_require.pop(key)
222 extras_require[new_key] = extras_require.pop(key)
223
223
224 everything = set()
224 everything = set()
225 for key, deps in extras_require.items():
225 for key, deps in extras_require.items():
226 if ':' not in key:
226 if ':' not in key:
227 everything.update(deps)
227 everything.update(deps)
228 extras_require['all'] = everything
228 extras_require['all'] = everything
229
229
230 if 'setuptools' in sys.modules:
230 if 'setuptools' in sys.modules:
231 setuptools_extra_args['python_requires'] = '>=3.6'
231 setuptools_extra_args['python_requires'] = '>=3.6'
232 setuptools_extra_args['zip_safe'] = False
232 setuptools_extra_args['zip_safe'] = False
233 setuptools_extra_args['entry_points'] = {
233 setuptools_extra_args['entry_points'] = {
234 'console_scripts': find_entry_points(),
234 'console_scripts': find_entry_points(),
235 'pygments.lexers': [
235 'pygments.lexers': [
236 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
236 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
237 'ipython = IPython.lib.lexers:IPythonLexer',
237 'ipython = IPython.lib.lexers:IPythonLexer',
238 'ipython3 = IPython.lib.lexers:IPython3Lexer',
238 'ipython3 = IPython.lib.lexers:IPython3Lexer',
239 ],
239 ],
240 }
240 }
241 setup_args['extras_require'] = extras_require
241 setup_args['extras_require'] = extras_require
242 setup_args['install_requires'] = install_requires
242 setup_args['install_requires'] = install_requires
243
243
244 else:
244 else:
245 # scripts has to be a non-empty list, or install_scripts isn't called
245 # scripts has to be a non-empty list, or install_scripts isn't called
246 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
246 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
247
247
248 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
248 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
249
249
250 #---------------------------------------------------------------------------
250 #---------------------------------------------------------------------------
251 # Do the actual setup now
251 # Do the actual setup now
252 #---------------------------------------------------------------------------
252 #---------------------------------------------------------------------------
253
253
254 setup_args.update(setuptools_extra_args)
254 setup_args.update(setuptools_extra_args)
255
255
256
256
257
257
258 def main():
258 def main():
259 setup(**setup_args)
259 setup(**setup_args)
260
260
261 if __name__ == '__main__':
261 if __name__ == '__main__':
262 main()
262 main()
General Comments 0
You need to be logged in to leave comments. Login now