##// END OF EJS Templates
Merge pull request #9118 from takluyver/ptshell...
Matthias Bussonnier -
r22108:7d6dfc3c merge
parent child Browse files
Show More
@@ -0,0 +1,16 b''
1 import importlib
2 import os
3
4 aliases = {
5 'qt4': 'qt',
6 }
7
8 def get_inputhook_func(gui):
9 if gui in aliases:
10 return get_inputhook_func(aliases[gui])
11
12 if gui == 'qt5':
13 os.environ['QT_API'] = 'pyqt5'
14
15 mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui)
16 return mod.inputhook
@@ -0,0 +1,141 b''
1 """GLUT Input hook for interactive use with prompt_toolkit
2 """
3 from __future__ import print_function
4
5
6 # GLUT is quite an old library and it is difficult to ensure proper
7 # integration within IPython since original GLUT does not allow to handle
8 # events one by one. Instead, it requires for the mainloop to be entered
9 # and never returned (there is not even a function to exit he
10 # mainloop). Fortunately, there are alternatives such as freeglut
11 # (available for linux and windows) and the OSX implementation gives
12 # access to a glutCheckLoop() function that blocks itself until a new
13 # event is received. This means we have to setup the idle callback to
14 # ensure we got at least one event that will unblock the function.
15 #
16 # Furthermore, it is not possible to install these handlers without a window
17 # being first created. We choose to make this window invisible. This means that
18 # display mode options are set at this level and user won't be able to change
19 # them later without modifying the code. This should probably be made available
20 # via IPython options system.
21
22 import sys
23 import time
24 import signal
25 import OpenGL.GLUT as glut
26 import OpenGL.platform as platform
27 from timeit import default_timer as clock
28
29 # Frame per second : 60
30 # Should probably be an IPython option
31 glut_fps = 60
32
33 # Display mode : double buffeed + rgba + depth
34 # Should probably be an IPython option
35 glut_display_mode = (glut.GLUT_DOUBLE |
36 glut.GLUT_RGBA |
37 glut.GLUT_DEPTH)
38
39 glutMainLoopEvent = None
40 if sys.platform == 'darwin':
41 try:
42 glutCheckLoop = platform.createBaseFunction(
43 'glutCheckLoop', dll=platform.GLUT, resultType=None,
44 argTypes=[],
45 doc='glutCheckLoop( ) -> None',
46 argNames=(),
47 )
48 except AttributeError:
49 raise RuntimeError(
50 '''Your glut implementation does not allow interactive sessions'''
51 '''Consider installing freeglut.''')
52 glutMainLoopEvent = glutCheckLoop
53 elif glut.HAVE_FREEGLUT:
54 glutMainLoopEvent = glut.glutMainLoopEvent
55 else:
56 raise RuntimeError(
57 '''Your glut implementation does not allow interactive sessions. '''
58 '''Consider installing freeglut.''')
59
60
61 def glut_display():
62 # Dummy display function
63 pass
64
65 def glut_idle():
66 # Dummy idle function
67 pass
68
69 def glut_close():
70 # Close function only hides the current window
71 glut.glutHideWindow()
72 glutMainLoopEvent()
73
74 def glut_int_handler(signum, frame):
75 # Catch sigint and print the defaultipyt message
76 signal.signal(signal.SIGINT, signal.default_int_handler)
77 print('\nKeyboardInterrupt')
78 # Need to reprint the prompt at this stage
79
80 # Initialisation code
81 glut.glutInit( sys.argv )
82 glut.glutInitDisplayMode( glut_display_mode )
83 # This is specific to freeglut
84 if bool(glut.glutSetOption):
85 glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
86 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
87 glut.glutCreateWindow( b'ipython' )
88 glut.glutReshapeWindow( 1, 1 )
89 glut.glutHideWindow( )
90 glut.glutWMCloseFunc( glut_close )
91 glut.glutDisplayFunc( glut_display )
92 glut.glutIdleFunc( glut_idle )
93
94
95 def inputhook(context):
96 """Run the pyglet event loop by processing pending events only.
97
98 This keeps processing pending events until stdin is ready. After
99 processing all pending events, a call to time.sleep is inserted. This is
100 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
101 though for best performance.
102 """
103 # We need to protect against a user pressing Control-C when IPython is
104 # idle and this is running. We trap KeyboardInterrupt and pass.
105
106 signal.signal(signal.SIGINT, glut_int_handler)
107
108 try:
109 t = clock()
110
111 # Make sure the default window is set after a window has been closed
112 if glut.glutGetWindow() == 0:
113 glut.glutSetWindow( 1 )
114 glutMainLoopEvent()
115 return 0
116
117 while not context.input_is_ready():
118 glutMainLoopEvent()
119 # We need to sleep at this point to keep the idle CPU load
120 # low. However, if sleep to long, GUI response is poor. As
121 # a compromise, we watch how often GUI events are being processed
122 # and switch between a short and long sleep time. Here are some
123 # stats useful in helping to tune this.
124 # time CPU load
125 # 0.001 13%
126 # 0.005 3%
127 # 0.01 1.5%
128 # 0.05 0.5%
129 used_time = clock() - t
130 if used_time > 10.0:
131 # print 'Sleep for 1 s' # dbg
132 time.sleep(1.0)
133 elif used_time > 0.1:
134 # Few GUI events coming in, so we can sleep longer
135 # print 'Sleep for 0.05 s' # dbg
136 time.sleep(0.05)
137 else:
138 # Many GUI events coming in, so sleep only very little
139 time.sleep(0.001)
140 except KeyboardInterrupt:
141 pass
@@ -0,0 +1,59 b''
1 # Code borrowed from python-prompt-toolkit examples
2 # https://github.com/jonathanslenders/python-prompt-toolkit/blob/77cdcfbc7f4b4c34a9d2f9a34d422d7152f16209/examples/inputhook.py
3
4 # Copyright (c) 2014, Jonathan Slenders
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without modification,
8 # are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice, this
14 # list of conditions and the following disclaimer in the documentation and/or
15 # other materials provided with the distribution.
16 #
17 # * Neither the name of the {organization} nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
25 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 """
33 PyGTK input hook for prompt_toolkit.
34
35 Listens on the pipe prompt_toolkit sets up for a notification that it should
36 return control to the terminal event loop.
37 """
38 from __future__ import absolute_import
39
40 import gtk, gobject
41
42 # Enable threading in GTK. (Otherwise, GTK will keep the GIL.)
43 gtk.gdk.threads_init()
44
45 def inputhook(context):
46 """
47 When the eventloop of prompt-toolkit is idle, call this inputhook.
48
49 This will run the GTK main loop until the file descriptor
50 `context.fileno()` becomes ready.
51
52 :param context: An `InputHookContext` instance.
53 """
54 def _main_quit(*a, **kw):
55 gtk.main_quit()
56 return False
57
58 gobject.io_add_watch(context.fileno(), gobject.IO_IN, _main_quit)
59 gtk.main()
@@ -0,0 +1,12 b''
1 """prompt_toolkit input hook for GTK 3
2 """
3
4 from gi.repository import Gtk, GLib
5
6 def _main_quit(*args, **kwargs):
7 Gtk.main_quit()
8 return False
9
10 def inputhook(context):
11 GLib.io_add_watch(context.fileno(), GLib.IO_IN, _main_quit)
12 Gtk.main()
@@ -0,0 +1,68 b''
1 """Enable pyglet to be used interacively with prompt_toolkit
2 """
3 from __future__ import absolute_import
4
5 import os
6 import sys
7 import time
8 from timeit import default_timer as clock
9 import pyglet
10
11 # On linux only, window.flip() has a bug that causes an AttributeError on
12 # window close. For details, see:
13 # http://groups.google.com/group/pyglet-users/browse_thread/thread/47c1aab9aa4a3d23/c22f9e819826799e?#c22f9e819826799e
14
15 if sys.platform.startswith('linux'):
16 def flip(window):
17 try:
18 window.flip()
19 except AttributeError:
20 pass
21 else:
22 def flip(window):
23 window.flip()
24
25
26 def inputhook(context):
27 """Run the pyglet event loop by processing pending events only.
28
29 This keeps processing pending events until stdin is ready. After
30 processing all pending events, a call to time.sleep is inserted. This is
31 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
32 though for best performance.
33 """
34 # We need to protect against a user pressing Control-C when IPython is
35 # idle and this is running. We trap KeyboardInterrupt and pass.
36 try:
37 t = clock()
38 while not context.input_is_ready():
39 pyglet.clock.tick()
40 for window in pyglet.app.windows:
41 window.switch_to()
42 window.dispatch_events()
43 window.dispatch_event('on_draw')
44 flip(window)
45
46 # We need to sleep at this point to keep the idle CPU load
47 # low. However, if sleep to long, GUI response is poor. As
48 # a compromise, we watch how often GUI events are being processed
49 # and switch between a short and long sleep time. Here are some
50 # stats useful in helping to tune this.
51 # time CPU load
52 # 0.001 13%
53 # 0.005 3%
54 # 0.01 1.5%
55 # 0.05 0.5%
56 used_time = clock() - t
57 if used_time > 10.0:
58 # print 'Sleep for 1 s' # dbg
59 time.sleep(1.0)
60 elif used_time > 0.1:
61 # Few GUI events coming in, so we can sleep longer
62 # print 'Sleep for 0.05 s' # dbg
63 time.sleep(0.05)
64 else:
65 # Many GUI events coming in, so sleep only very little
66 time.sleep(0.001)
67 except KeyboardInterrupt:
68 pass
@@ -0,0 +1,11 b''
1 from IPython.external.qt_for_kernel import QtCore, QtGui
2
3 def inputhook(context):
4 app = QtCore.QCoreApplication.instance()
5 if not app:
6 return
7 event_loop = QtCore.QEventLoop(app)
8 notifier = QtCore.QSocketNotifier(context.fileno(), QtCore.QSocketNotifier.Read)
9 notifier.setEnabled(True)
10 notifier.activated.connect(event_loop.exit)
11 event_loop.exec_()
@@ -0,0 +1,93 b''
1 # Code borrowed from ptpython
2 # https://github.com/jonathanslenders/ptpython/blob/86b71a89626114b18898a0af463978bdb32eeb70/ptpython/eventloop.py
3
4 # Copyright (c) 2015, Jonathan Slenders
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without modification,
8 # are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice, this
14 # list of conditions and the following disclaimer in the documentation and/or
15 # other materials provided with the distribution.
16 #
17 # * Neither the name of the {organization} nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
25 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 """
33 Wrapper around the eventloop that gives some time to the Tkinter GUI to process
34 events when it's loaded and while we are waiting for input at the REPL. This
35 way we don't block the UI of for instance ``turtle`` and other Tk libraries.
36
37 (Normally Tkinter registeres it's callbacks in ``PyOS_InputHook`` to integrate
38 in readline. ``prompt-toolkit`` doesn't understand that input hook, but this
39 will fix it for Tk.)
40 """
41 import time
42
43 import _tkinter
44 try:
45 import tkinter
46 except ImportError:
47 import Tkinter as tkinter # Python 2
48
49 def inputhook(inputhook_context):
50 """
51 Inputhook for Tk.
52 Run the Tk eventloop until prompt-toolkit needs to process the next input.
53 """
54 # Get the current TK application.
55 root = tkinter._default_root
56
57 def wait_using_filehandler():
58 """
59 Run the TK eventloop until the file handler that we got from the
60 inputhook becomes readable.
61 """
62 # Add a handler that sets the stop flag when `prompt-toolkit` has input
63 # to process.
64 stop = [False]
65 def done(*a):
66 stop[0] = True
67
68 root.createfilehandler(inputhook_context.fileno(), _tkinter.READABLE, done)
69
70 # Run the TK event loop as long as we don't receive input.
71 while root.dooneevent(_tkinter.ALL_EVENTS):
72 if stop[0]:
73 break
74
75 root.deletefilehandler(inputhook_context.fileno())
76
77 def wait_using_polling():
78 """
79 Windows TK doesn't support 'createfilehandler'.
80 So, run the TK eventloop and poll until input is ready.
81 """
82 while not inputhook_context.input_is_ready():
83 while root.dooneevent(_tkinter.ALL_EVENTS | _tkinter.DONT_WAIT):
84 pass
85 # Sleep to make the CPU idle, but not too long, so that the UI
86 # stays responsive.
87 time.sleep(.01)
88
89 if root is not None:
90 if hasattr(root, 'createfilehandler'):
91 wait_using_filehandler()
92 else:
93 wait_using_polling()
@@ -0,0 +1,148 b''
1 """Enable wxPython to be used interacively in prompt_toolkit
2 """
3 from __future__ import absolute_import
4
5 import sys
6 import signal
7 import time
8 from timeit import default_timer as clock
9 import wx
10
11
12 def inputhook_wx1(context):
13 """Run the wx event loop by processing pending events only.
14
15 This approach seems to work, but its performance is not great as it
16 relies on having PyOS_InputHook called regularly.
17 """
18 try:
19 app = wx.GetApp()
20 if app is not None:
21 assert wx.Thread_IsMain()
22
23 # Make a temporary event loop and process system events until
24 # there are no more waiting, then allow idle events (which
25 # will also deal with pending or posted wx events.)
26 evtloop = wx.EventLoop()
27 ea = wx.EventLoopActivator(evtloop)
28 while evtloop.Pending():
29 evtloop.Dispatch()
30 app.ProcessIdle()
31 del ea
32 except KeyboardInterrupt:
33 pass
34 return 0
35
36 class EventLoopTimer(wx.Timer):
37
38 def __init__(self, func):
39 self.func = func
40 wx.Timer.__init__(self)
41
42 def Notify(self):
43 self.func()
44
45 class EventLoopRunner(object):
46
47 def Run(self, time, input_is_ready):
48 self.input_is_ready = input_is_ready
49 self.evtloop = wx.EventLoop()
50 self.timer = EventLoopTimer(self.check_stdin)
51 self.timer.Start(time)
52 self.evtloop.Run()
53
54 def check_stdin(self):
55 if self.input_is_ready():
56 self.timer.Stop()
57 self.evtloop.Exit()
58
59 def inputhook_wx2(context):
60 """Run the wx event loop, polling for stdin.
61
62 This version runs the wx eventloop for an undetermined amount of time,
63 during which it periodically checks to see if anything is ready on
64 stdin. If anything is ready on stdin, the event loop exits.
65
66 The argument to elr.Run controls how often the event loop looks at stdin.
67 This determines the responsiveness at the keyboard. A setting of 1000
68 enables a user to type at most 1 char per second. I have found that a
69 setting of 10 gives good keyboard response. We can shorten it further,
70 but eventually performance would suffer from calling select/kbhit too
71 often.
72 """
73 try:
74 app = wx.GetApp()
75 if app is not None:
76 assert wx.Thread_IsMain()
77 elr = EventLoopRunner()
78 # As this time is made shorter, keyboard response improves, but idle
79 # CPU load goes up. 10 ms seems like a good compromise.
80 elr.Run(time=10, # CHANGE time here to control polling interval
81 input_is_ready=context.input_is_ready)
82 except KeyboardInterrupt:
83 pass
84 return 0
85
86 def inputhook_wx3(context):
87 """Run the wx event loop by processing pending events only.
88
89 This is like inputhook_wx1, but it keeps processing pending events
90 until stdin is ready. After processing all pending events, a call to
91 time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
92 This sleep time should be tuned though for best performance.
93 """
94 # We need to protect against a user pressing Control-C when IPython is
95 # idle and this is running. We trap KeyboardInterrupt and pass.
96 try:
97 app = wx.GetApp()
98 if app is not None:
99 assert wx.Thread_IsMain()
100
101 # The import of wx on Linux sets the handler for signal.SIGINT
102 # to 0. This is a bug in wx or gtk. We fix by just setting it
103 # back to the Python default.
104 if not callable(signal.getsignal(signal.SIGINT)):
105 signal.signal(signal.SIGINT, signal.default_int_handler)
106
107 evtloop = wx.EventLoop()
108 ea = wx.EventLoopActivator(evtloop)
109 t = clock()
110 while not context.input_is_ready():
111 while evtloop.Pending():
112 t = clock()
113 evtloop.Dispatch()
114 app.ProcessIdle()
115 # We need to sleep at this point to keep the idle CPU load
116 # low. However, if sleep to long, GUI response is poor. As
117 # a compromise, we watch how often GUI events are being processed
118 # and switch between a short and long sleep time. Here are some
119 # stats useful in helping to tune this.
120 # time CPU load
121 # 0.001 13%
122 # 0.005 3%
123 # 0.01 1.5%
124 # 0.05 0.5%
125 used_time = clock() - t
126 if used_time > 10.0:
127 # print 'Sleep for 1 s' # dbg
128 time.sleep(1.0)
129 elif used_time > 0.1:
130 # Few GUI events coming in, so we can sleep longer
131 # print 'Sleep for 0.05 s' # dbg
132 time.sleep(0.05)
133 else:
134 # Many GUI events coming in, so sleep only very little
135 time.sleep(0.001)
136 del ea
137 except KeyboardInterrupt:
138 pass
139 return 0
140
141 if sys.platform == 'darwin':
142 # On OSX, evtloop.Pending() always returns True, regardless of there being
143 # any events pending. As such we can't use implementations 1 or 3 of the
144 # inputhook as those depend on a pending/dispatch loop.
145 inputhook = inputhook_wx2
146 else:
147 # This is our default implementation
148 inputhook = inputhook_wx3
@@ -0,0 +1,240 b''
1 """IPython terminal interface using prompt_toolkit in place of readline"""
2 from __future__ import print_function
3
4 import sys
5
6 from IPython.core.interactiveshell import InteractiveShell
7 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
8 from traitlets import Bool, Unicode, Dict
9
10 from prompt_toolkit.completion import Completer, Completion
11 from prompt_toolkit.enums import DEFAULT_BUFFER
12 from prompt_toolkit.filters import HasFocus, HasSelection
13 from prompt_toolkit.history import InMemoryHistory
14 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop
15 from prompt_toolkit.interface import CommandLineInterface
16 from prompt_toolkit.key_binding.manager import KeyBindingManager
17 from prompt_toolkit.key_binding.vi_state import InputMode
18 from prompt_toolkit.key_binding.bindings.vi import ViStateFilter
19 from prompt_toolkit.keys import Keys
20 from prompt_toolkit.layout.lexers import PygmentsLexer
21 from prompt_toolkit.styles import PygmentsStyle
22
23 from pygments.styles import get_style_by_name
24 from pygments.lexers import Python3Lexer, PythonLexer
25 from pygments.token import Token
26
27 from .pt_inputhooks import get_inputhook_func
28 from .interactiveshell import get_default_editor
29
30
31 class IPythonPTCompleter(Completer):
32 """Adaptor to provide IPython completions to prompt_toolkit"""
33 def __init__(self, ipy_completer):
34 self.ipy_completer = ipy_completer
35
36 def get_completions(self, document, complete_event):
37 if not document.current_line.strip():
38 return
39
40 used, matches = self.ipy_completer.complete(
41 line_buffer=document.current_line,
42 cursor_pos=document.cursor_position_col
43 )
44 start_pos = -len(used)
45 for m in matches:
46 yield Completion(m, start_position=start_pos)
47
48 class PTInteractiveShell(InteractiveShell):
49 colors_force = True
50
51 pt_cli = None
52
53 vi_mode = Bool(False, config=True,
54 help="Use vi style keybindings at the prompt",
55 )
56
57 mouse_support = Bool(False, config=True,
58 help="Enable mouse support in the prompt"
59 )
60
61 highlighting_style = Unicode('', config=True,
62 help="The name of a Pygments style to use for syntax highlighting"
63 )
64
65 highlighting_style_overrides = Dict(config=True,
66 help="Override highlighting format for specific tokens"
67 )
68
69 editor = Unicode(get_default_editor(), config=True,
70 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
71 )
72
73 def get_prompt_tokens(self, cli):
74 return [
75 (Token.Prompt, 'In ['),
76 (Token.PromptNum, str(self.execution_count)),
77 (Token.Prompt, ']: '),
78 ]
79
80 def get_continuation_tokens(self, cli, width):
81 return [
82 (Token.Prompt, (' ' * (width - 2)) + ': '),
83 ]
84
85 def init_prompt_toolkit_cli(self):
86 if not sys.stdin.isatty():
87 # Piped input - e.g. for tests. Fall back to plain non-interactive
88 # output. This is very limited, and only accepts a single line.
89 def prompt():
90 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
91 self.prompt_for_code = prompt
92 return
93
94 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
95 insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
96 # Ctrl+J == Enter, seemingly
97 @kbmanager.registry.add_binding(Keys.ControlJ,
98 filter=(HasFocus(DEFAULT_BUFFER)
99 & ~HasSelection()
100 & insert_mode
101 ))
102 def _(event):
103 b = event.current_buffer
104 d = b.document
105 if not (d.on_last_line or d.cursor_position_row >= d.line_count
106 - d.empty_line_count_at_the_end()):
107 b.newline()
108 return
109
110 status, indent = self.input_splitter.check_complete(d.text)
111
112 if (status != 'incomplete') and b.accept_action.is_returnable:
113 b.accept_action.validate_and_handle(event.cli, b)
114 else:
115 b.insert_text('\n' + (' ' * (indent or 0)))
116
117 @kbmanager.registry.add_binding(Keys.ControlC)
118 def _(event):
119 event.current_buffer.reset()
120
121 # Pre-populate history from IPython's history database
122 history = InMemoryHistory()
123 last_cell = u""
124 for _, _, cell in self.history_manager.get_tail(self.history_load_length,
125 include_latest=True):
126 # Ignore blank lines and consecutive duplicates
127 cell = cell.rstrip()
128 if cell and (cell != last_cell):
129 history.append(cell)
130
131 style_overrides = {
132 Token.Prompt: '#009900',
133 Token.PromptNum: '#00ff00 bold',
134 }
135 if self.highlighting_style:
136 style_cls = get_style_by_name(self.highlighting_style)
137 else:
138 style_cls = get_style_by_name('default')
139 # The default theme needs to be visible on both a dark background
140 # and a light background, because we can't tell what the terminal
141 # looks like. These tweaks to the default theme help with that.
142 style_overrides.update({
143 Token.Number: '#007700',
144 Token.Operator: 'noinherit',
145 Token.String: '#BB6622',
146 Token.Name.Function: '#2080D0',
147 Token.Name.Class: 'bold #2080D0',
148 Token.Name.Namespace: 'bold #2080D0',
149 })
150 style_overrides.update(self.highlighting_style_overrides)
151 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
152 style_dict=style_overrides)
153
154 app = create_prompt_application(multiline=True,
155 lexer=PygmentsLexer(Python3Lexer if PY3 else PythonLexer),
156 get_prompt_tokens=self.get_prompt_tokens,
157 # The line below is waiting for a new release of
158 # prompt_toolkit (> 0.57)
159 #get_continuation_tokens=self.get_continuation_tokens,
160 key_bindings_registry=kbmanager.registry,
161 history=history,
162 completer=IPythonPTCompleter(self.Completer),
163 enable_history_search=True,
164 style=style,
165 mouse_support=self.mouse_support,
166 )
167
168 self.pt_cli = CommandLineInterface(app,
169 eventloop=create_eventloop(self.inputhook))
170
171 def prompt_for_code(self):
172 document = self.pt_cli.run(pre_run=self.pre_prompt)
173 return document.text
174
175 def init_io(self):
176 if sys.platform not in {'win32', 'cli'}:
177 return
178
179 import colorama
180 colorama.init()
181
182 # For some reason we make these wrappers around stdout/stderr.
183 # For now, we need to reset them so all output gets coloured.
184 # https://github.com/ipython/ipython/issues/8669
185 from IPython.utils import io
186 io.stdout = io.IOStream(sys.stdout)
187 io.stderr = io.IOStream(sys.stderr)
188
189 def __init__(self, *args, **kwargs):
190 super(PTInteractiveShell, self).__init__(*args, **kwargs)
191 self.init_prompt_toolkit_cli()
192 self.keep_running = True
193
194 def ask_exit(self):
195 self.keep_running = False
196
197 rl_next_input = None
198
199 def pre_prompt(self):
200 if self.rl_next_input:
201 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
202 self.rl_next_input = None
203
204 def interact(self):
205 while self.keep_running:
206 print(self.separate_in, end='')
207
208 try:
209 code = self.prompt_for_code()
210 except EOFError:
211 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
212 self.ask_exit()
213
214 else:
215 if code:
216 self.run_cell(code, store_history=True)
217
218 def mainloop(self):
219 # An extra layer of protection in case someone mashing Ctrl-C breaks
220 # out of our internal code.
221 while True:
222 try:
223 self.interact()
224 break
225 except KeyboardInterrupt:
226 print("\nKeyboardInterrupt escaped interact()\n")
227
228 _inputhook = None
229 def inputhook(self, context):
230 if self._inputhook is not None:
231 self._inputhook(context)
232
233 def enable_gui(self, gui=None):
234 if gui:
235 self._inputhook = get_inputhook_func(gui)
236 else:
237 self._inputhook = None
238
239 if __name__ == '__main__':
240 PTInteractiveShell.instance().interact()
@@ -170,41 +170,38 b' def compress_user(path, tilde_expand, tilde_val):'
170
170
171
171
172
172
173 def penalize_magics_key(word):
173 def completions_sorting_key(word):
174 """key for sorting that penalizes magic commands in the ordering
174 """key for sorting completions
175
175
176 Normal words are left alone.
176 This does several things:
177
178 Magic commands have the initial % moved to the end, e.g.
179 %matplotlib is transformed as follows:
180
181 %matplotlib -> matplotlib%
182
183 [The choice of the final % is arbitrary.]
184
185 Since "matplotlib" < "matplotlib%" as strings,
186 "timeit" will appear before the magic "%timeit" in the ordering
187
188 For consistency, move "%%" to the end, so cell magics appear *after*
189 line magics with the same name.
190
191 A check is performed that there are no other "%" in the string;
192 if there are, then the string is not a magic command and is left unchanged.
193
177
178 - Lowercase all completions, so they are sorted alphabetically with
179 upper and lower case words mingled
180 - Demote any completions starting with underscores to the end
181 - Insert any %magic and %%cellmagic completions in the alphabetical order
182 by their name
194 """
183 """
184 # Case insensitive sort
185 word = word.lower()
195
186
196 # Move any % signs from start to end of the key
187 prio1, prio2 = 0, 0
197 # provided there are no others elsewhere in the string
198
188
199 if word[:2] == "%%":
189 if word.startswith('__'):
200 if not "%" in word[2:]:
190 prio1 = 2
201 return word[2:] + "%%"
191 elif word.startswith('_'):
192 prio1 = 1
202
193
203 if word[:1] == "%":
194 if word.startswith('%%'):
195 # If there's another % in there, this is something else, so leave it alone
196 if not "%" in word[2:]:
197 word = word[2:]
198 prio2 = 2
199 elif word.startswith('%'):
204 if not "%" in word[1:]:
200 if not "%" in word[1:]:
205 return word[1:] + "%"
201 word = word[1:]
206
202 prio2 = 1
207 return word
203
204 return prio1, word, prio2
208
205
209
206
210 @undoc
207 @undoc
@@ -1206,8 +1203,7 b' class IPCompleter(Completer):'
1206 # simply collapse the dict into a list for readline, but we'd have
1203 # simply collapse the dict into a list for readline, but we'd have
1207 # richer completion semantics in other evironments.
1204 # richer completion semantics in other evironments.
1208
1205
1209 # use penalize_magics_key to put magics after variables with same name
1206 self.matches = sorted(set(self.matches), key=completions_sorting_key)
1210 self.matches = sorted(set(self.matches), key=penalize_magics_key)
1211
1207
1212 #io.rprint('COMP TEXT, MATCHES: %r, %r' % (text, self.matches)) # dbg
1208 #io.rprint('COMP TEXT, MATCHES: %r, %r' % (text, self.matches)) # dbg
1213 return text, self.matches
1209 return text, self.matches
@@ -52,8 +52,9 b' class TestFileToRun(unittest.TestCase, tt.TempFileMixin):'
52 src = "True\n"
52 src = "True\n"
53 self.mktmp(src)
53 self.mktmp(src)
54
54
55 out = 'In [1]: False\n\nIn [2]:'
55 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
56 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
56 tt.ipexec_validate(self.fname, 'False', err, options=['-i'],
57 tt.ipexec_validate(self.fname, out, err, options=['-i'],
57 commands=['"__file__" in globals()', 'exit()'])
58 commands=['"__file__" in globals()', 'exit()'])
58
59
59 @dec.skip_win32
60 @dec.skip_win32
@@ -63,6 +64,7 b' class TestFileToRun(unittest.TestCase, tt.TempFileMixin):'
63 src = "from __future__ import division\n"
64 src = "from __future__ import division\n"
64 self.mktmp(src)
65 self.mktmp(src)
65
66
67 out = 'In [1]: float\n\nIn [2]:'
66 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
68 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
67 tt.ipexec_validate(self.fname, 'float', err, options=['-i'],
69 tt.ipexec_validate(self.fname, out, err, options=['-i'],
68 commands=['type(1/2)', 'exit()'])
70 commands=['type(1/2)', 'exit()'])
@@ -32,7 +32,7 b' from IPython.core.shellapp import ('
32 InteractiveShellApp, shell_flags, shell_aliases
32 InteractiveShellApp, shell_flags, shell_aliases
33 )
33 )
34 from IPython.extensions.storemagic import StoreMagics
34 from IPython.extensions.storemagic import StoreMagics
35 from IPython.terminal.interactiveshell import TerminalInteractiveShell
35 from .ptshell import PTInteractiveShell as TerminalInteractiveShell
36 from IPython.utils import warn
36 from IPython.utils import warn
37 from IPython.paths import get_ipython_dir
37 from IPython.paths import get_ipython_dir
38 from traitlets import (
38 from traitlets import (
@@ -38,7 +38,7 b' if glut.glutGetWindow() > 0:'
38 else:
38 else:
39 interactive = False
39 interactive = False
40
40
41 glut.glutCreateWindow('gui-glut')
41 glut.glutCreateWindow(b'gui-glut')
42 glut.glutDisplayFunc(display)
42 glut.glutDisplayFunc(display)
43 glut.glutReshapeFunc(resize)
43 glut.glutReshapeFunc(resize)
44 # This is necessary on osx to be able to close the window
44 # This is necessary on osx to be able to close the window
@@ -20,10 +20,10 b' def delete_event(widget, event, data=None):'
20 def destroy(widget, data=None):
20 def destroy(widget, data=None):
21 Gtk.main_quit()
21 Gtk.main_quit()
22
22
23 window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
23 window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL)
24 window.connect("delete_event", delete_event)
24 window.connect("delete_event", delete_event)
25 window.connect("destroy", destroy)
25 window.connect("destroy", destroy)
26 button = Gtk.Button("Hello World")
26 button = Gtk.Button(label="Hello World")
27 button.connect("clicked", hello_world, None)
27 button.connect("clicked", hello_world, None)
28
28
29 window.add(button)
29 window.add(button)
@@ -195,6 +195,7 b' install_requires = ['
195 'pickleshare',
195 'pickleshare',
196 'simplegeneric>0.8',
196 'simplegeneric>0.8',
197 'traitlets',
197 'traitlets',
198 'prompt_toolkit', # We will require > 0.57 once a new release is made
198 ]
199 ]
199
200
200 # Platform-specific dependencies:
201 # Platform-specific dependencies:
@@ -204,8 +205,6 b' install_requires = ['
204 extras_require.update({
205 extras_require.update({
205 ':sys_platform != "win32"': ['pexpect'],
206 ':sys_platform != "win32"': ['pexpect'],
206 ':sys_platform == "darwin"': ['appnope'],
207 ':sys_platform == "darwin"': ['appnope'],
207 ':sys_platform == "darwin" and platform_python_implementation == "CPython"': ['gnureadline'],
208 'terminal:sys_platform == "win32"': ['pyreadline>=2'],
209 'test:python_version == "2.7"': ['mock'],
208 'test:python_version == "2.7"': ['mock'],
210 })
209 })
211 # FIXME: re-specify above platform dependencies for pip < 6
210 # FIXME: re-specify above platform dependencies for pip < 6
General Comments 0
You need to be logged in to leave comments. Login now