Skip to content

Commit 655a7dc

Browse files
committed
Initial commit of monitor.py script
1 parent 021e9c3 commit 655a7dc

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed

monitor.py

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
"""
2+
Monitor for IPython/Jupyter console commands run from Vim.
3+
4+
Usage:
5+
1. Run jupyter/ipython console
6+
2. Run python monitor.py
7+
3. Connect Vim to console kernel using IPython command
8+
"""
9+
from __future__ import print_function
10+
import ast
11+
import os
12+
import re
13+
import six
14+
import sys
15+
try:
16+
from jupyter_client import KernelManager, find_connection_file
17+
except ImportError:
18+
from IPython.kernel import KernelManager, find_connection_file
19+
try:
20+
from Queue import Empty
21+
except ImportError:
22+
from queue import Empty
23+
from glob import glob
24+
25+
try:
26+
from pygments import highlight
27+
except ImportError:
28+
highlight = lambda code, *args: code
29+
else:
30+
from pygments.lexers import PythonLexer, Python3Lexer
31+
from pygments.formatters import TerminalFormatter
32+
formatter = TerminalFormatter()
33+
lexer = Python3Lexer() if six.PY3 else PythonLexer()
34+
35+
colors = {k: i for i, k in enumerate([
36+
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'])}
37+
38+
39+
def paths():
40+
for fullpath in glob(os.path.join(os.path.dirname(filename), 'kernel*')):
41+
if not re.match('^(.*/)?kernel-[0-9]+.json', fullpath):
42+
continue
43+
yield fullpath
44+
45+
46+
connected = False
47+
while not connected:
48+
try:
49+
filename = find_connection_file('kernel*')
50+
except IOError:
51+
continue
52+
53+
for fullpath in paths():
54+
km = KernelManager(connection_file=fullpath)
55+
km.load_connection_file()
56+
57+
kc = km.client()
58+
kc.start_channels()
59+
try:
60+
send = kc.execute
61+
except AttributeError:
62+
send = kc.shell_channel.execute
63+
if not hasattr(kc, 'iopub_channel'):
64+
kc.iopub_channel = kc.sub_channel
65+
66+
send('', silent=True)
67+
try:
68+
msg = kc.shell_channel.get_msg(timeout=1)
69+
connected = True
70+
socket = km.connect_iopub()
71+
print('IPython monitor connected successfully')
72+
break
73+
except KeyboardInterrupt:
74+
sys.exit(0)
75+
except (Empty, KeyError):
76+
continue
77+
except Exception as e:
78+
import traceback
79+
traceback.print_exc()
80+
finally:
81+
if not connected:
82+
kc.stop_channels()
83+
84+
85+
def colorize(string, color, bold=False, bright=False):
86+
if isinstance(color, str):
87+
code = ''.join(('\033[', str(colors[color] + (90 if bright else 30))))
88+
else:
89+
code = '\033[38;5;%d' % color
90+
return ''.join((code, ';1' if bold else '', 'm', string, '\033[0m'))
91+
92+
93+
def get_msgs():
94+
try:
95+
kc.iopub_channel.flush()
96+
return kc.iopub_channel.get_msgs()
97+
except AttributeError:
98+
msgs = []
99+
while True:
100+
try:
101+
msgs.append(kc.iopub_channel.get_msg(timeout=0.001))
102+
except Empty:
103+
return msgs
104+
105+
106+
if len(sys.argv) > 1:
107+
term = open(sys.argv[1], 'w')
108+
sys.stdout = term
109+
else:
110+
msg_id = send('import os as _os; _tty = _os.ttyname(1)', silent=True,
111+
user_expressions=dict(_tty='_tty'))
112+
while True:
113+
try:
114+
msg = kc.shell_channel.get_msg(timeout=1.0)
115+
if msg['parent_header']['msg_id'] == msg_id:
116+
sys.stdout = open(ast.literal_eval(
117+
msg['content']['user_expressions']
118+
['_tty']['data']['text/plain']), 'w+')
119+
break
120+
except Empty:
121+
continue
122+
123+
124+
class IPythonMonitor(object):
125+
126+
def __init__(self):
127+
self.clients = set()
128+
self.execution_count_id = None
129+
self.last_msg_type = None # Only set when text written to stdout
130+
self.last_execution_count = 0
131+
132+
def print_prompt(self, start='In', color=28, num_color=46, count_offset=0):
133+
count = str(self.last_execution_count + count_offset)
134+
sys.stdout.write(colorize(start.rstrip() + ' [', color))
135+
sys.stdout.write(colorize(count, num_color, bold=True))
136+
sys.stdout.write(colorize(']: ', color))
137+
return '%s [%s]: ' % (start.strip(), count)
138+
139+
def listen(self):
140+
while socket.recv():
141+
for msg in get_msgs():
142+
msg_type = msg['msg_type']
143+
144+
if msg_type == 'shutdown_reply':
145+
sys.exit(0)
146+
147+
client = msg['parent_header'].get('session', '')
148+
if (client and msg_type in ('execute_input', 'pyin') and
149+
msg['content']['code'] == '"_vim_client";_=_;__=__'):
150+
self.clients.add(client)
151+
continue
152+
if client not in self.clients:
153+
continue
154+
155+
getattr(self, msg_type, self.other)(msg)
156+
sys.stdout.flush()
157+
158+
def pyin(self, msg):
159+
self.last_execution_count = msg['content']['execution_count']
160+
sys.stdout.write('\r')
161+
dots = ' ' * (len(self.print_prompt().rstrip()) - 1) + ': '
162+
code = highlight(msg['content']['code'], lexer, formatter)
163+
output = code.rstrip().replace('\n', '\n' + colorize(dots, 28))
164+
sys.stdout.write(output)
165+
self.execution_count_id = msg['parent_header']['msg_id']
166+
self.last_msg_type = msg['msg_type']
167+
168+
def pyout(self, msg, prompt=True, spaces=''):
169+
if 'execution_count' in msg['content']:
170+
self.last_execution_count = msg['content']['execution_count']
171+
self.execution_count_id = msg['parent_header']['msg_id']
172+
output = msg['content']['data']['text/plain']
173+
if prompt:
174+
self.print_prompt('\nOut', 196, 196)
175+
sys.stdout.write(('\n' if '\n' in output else '') + output)
176+
else:
177+
sys.stdout.write(output)
178+
self.last_msg_type = msg['msg_type']
179+
180+
def display_data(self, msg):
181+
sys.stdout.write('\n')
182+
self.pyout(msg, prompt=False)
183+
184+
def pyerr(self, msg):
185+
for line in msg['content']['traceback']:
186+
sys.stdout.write('\n' + line)
187+
if self.last_msg_type not in ('execute_input', 'pyin'):
188+
self.print_prompt('\nIn')
189+
self.last_msg_type = msg['msg_type']
190+
191+
def stream(self, msg):
192+
if self.last_msg_type not in ('pyerr', 'error', 'stream'):
193+
sys.stdout.write('\n')
194+
try:
195+
data = msg['content']['data']
196+
except KeyError:
197+
data = msg['content']['text']
198+
sys.stdout.write(colorize(data, 'cyan', bright=True))
199+
self.last_msg_type = msg['msg_type']
200+
201+
def status(self, msg):
202+
if (msg['content']['execution_state'] == 'idle' and
203+
msg['parent_header']['msg_id'] == self.execution_count_id):
204+
self.print_prompt('\nIn', count_offset=1)
205+
self.execution_count_id = None
206+
207+
def clear_output(self, msg):
208+
if self.last_msg_type in ('execute_input', 'pyin'):
209+
print('\n')
210+
print('\033[2K\r', file=sys.stdout, end='')
211+
212+
def other(self, msg):
213+
print('msg_type = %s' % str(msg['msg_type']))
214+
print('msg = %s' % str(msg))
215+
216+
execute_input = pyin
217+
execute_result = pyout
218+
error = pyerr
219+
220+
221+
monitor = IPythonMonitor()
222+
monitor.listen()

0 commit comments

Comments
 (0)