Skip to content

Commit 9028b45

Browse files
committed
now that zmq long messages are fixed, no need to split shell messages
1 parent 283802f commit 9028b45

File tree

1 file changed

+170
-183
lines changed

1 file changed

+170
-183
lines changed

custom_components/pyscript/jupyter_kernel.py

+170-183
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ async def recv(self, multipart=False):
120120
# _LOGGER.debug(f"recv: got cmd={cmd}, params={params}")
121121
else:
122122
parts.append(msg_body)
123-
if cmd == 0x0 or cmd == 0x2:
123+
if cmd in (0x0, 0x2):
124124
# _LOGGER.debug(f"recv: got msg {parts}")
125125
if not multipart:
126126
return b''.join(parts)
@@ -311,218 +311,205 @@ def encode(msg):
311311
# else:
312312
# _LOGGER.debug("send skipping msg_type %s since socket is None", msg_type)
313313

314-
async def shell_handler(self, shell_socket, full_msg):
314+
async def shell_handler(self, shell_socket, wire_msg):
315315
"""Handle shell messages."""
316-
#
317-
# Jupyter extensions like black can send several execute requests back-to-back, so
318-
# we need to handle multiple messages separated by DELIM
319-
#
320-
msg_list = None
321-
for this_msg in full_msg:
322-
if this_msg == DELIM:
323-
if msg_list is None:
324-
msg_list = [[this_msg]]
325-
else:
326-
msg_list.append([this_msg])
327-
else:
328-
msg_list[-1].append(this_msg) # pylint: disable=unsubscriptable-object
329-
for this_msg in msg_list: # pylint: disable=too-many-nested-blocks
330-
identities, msg = self.deserialize_wire_msg(this_msg)
331-
# _LOGGER.debug("shell received %s: %s", msg.get('header', {}).get('msg_type', 'UNKNOWN'), msg)
332316

333-
content = {
334-
'execution_state': "busy",
335-
}
336-
await self.send(self.iopub_socket, 'status', content, parent_header=msg['header'])
317+
identities, msg = self.deserialize_wire_msg(wire_msg)
318+
# _LOGGER.debug("shell received %s: %s", msg.get('header', {}).get('msg_type', 'UNKNOWN'), msg)
319+
self.parent_header = msg['header']
337320

338-
self.parent_header = msg['header']
339-
340-
# process request:
321+
content = {
322+
'execution_state': "busy",
323+
}
324+
await self.send(self.iopub_socket, 'status', content, parent_header=msg['header'])
341325

342-
if msg['header']["msg_type"] == "execute_request":
326+
if msg['header']["msg_type"] == "execute_request":
343327

344-
content = {
345-
'execution_count': self.execution_count,
346-
'code': msg['content']["code"],
347-
}
348-
await self.send(self.iopub_socket, 'execute_input', content, parent_header=msg['header'])
328+
content = {
329+
'execution_count': self.execution_count,
330+
'code': msg['content']["code"],
331+
}
332+
await self.send(self.iopub_socket, 'execute_input', content, parent_header=msg['header'])
349333

350-
code = msg['content']["code"]
351-
self.ast_ctx.parse(code)
334+
code = msg['content']["code"]
335+
self.ast_ctx.parse(code)
336+
exc = self.ast_ctx.get_exception_obj()
337+
if exc is None:
338+
result = await self.ast_ctx.eval()
352339
exc = self.ast_ctx.get_exception_obj()
353-
if exc is None:
354-
result = await self.ast_ctx.eval()
355-
exc = self.ast_ctx.get_exception_obj()
356-
if exc:
357-
traceback_mesg = self.ast_ctx.get_exception_long().split("\n")
358-
359-
if msg['content'].get("store_history", True):
360-
self.execution_count += 1
361-
362-
metadata = {
363-
"dependencies_met": True,
364-
"engine": self.engine_id,
365-
"status": "error",
366-
"started": datetime.datetime.now().isoformat(),
367-
}
368-
content = {
369-
'execution_count': self.execution_count,
370-
'status': 'error',
371-
'ename': type(exc).__name__, # Exception name, as a string
372-
'evalue': str(exc), # Exception value, as a string
373-
'traceback': traceback_mesg,
374-
}
375-
_LOGGER.debug("Executing '%s' got exception: %s", code, content)
376-
await self.send(shell_socket, 'execute_reply', content, metadata=metadata,
377-
parent_header=msg['header'], identities=identities)
378-
del content["execution_count"], content["status"]
379-
await self.send(self.iopub_socket, 'error', content, parent_header=msg['header'])
380-
381-
content = {
382-
'execution_state': "idle",
383-
}
384-
await self.send(self.iopub_socket, 'status', content, parent_header=msg['header'])
385-
return
386-
387-
# if True or isinstance(self.ast_ctx.ast, ast.Expr):
388-
_LOGGER.debug("Executing: '%s' got result %s", code, result)
389-
if result is not None:
390-
content = {
391-
'execution_count': self.execution_count,
392-
'data': {"text/plain": repr(result)},
393-
'metadata': {}
394-
}
395-
await self.send(self.iopub_socket, 'execute_result', content, parent_header=msg['header'])
396-
340+
if exc:
341+
traceback_mesg = self.ast_ctx.get_exception_long().split("\n")
397342

398343
metadata = {
399344
"dependencies_met": True,
400345
"engine": self.engine_id,
401-
"status": "ok",
346+
"status": "error",
402347
"started": datetime.datetime.now().isoformat(),
403348
}
404349
content = {
405-
"status": "ok",
406-
"execution_count": self.execution_count,
407-
"user_variables": {},
408-
"payload": [],
409-
"user_expressions": {},
350+
'execution_count': self.execution_count,
351+
'status': 'error',
352+
'ename': type(exc).__name__, # Exception name, as a string
353+
'evalue': str(exc), # Exception value, as a string
354+
'traceback': traceback_mesg,
410355
}
356+
_LOGGER.debug("Executing '%s' got exception: %s", code, content)
411357
await self.send(shell_socket, 'execute_reply', content, metadata=metadata,
412358
parent_header=msg['header'], identities=identities)
413-
if msg['content'].get("store_history", True):
414-
self.execution_count += 1
415-
elif msg['header']["msg_type"] == "kernel_info_request":
359+
del content["execution_count"], content["status"]
360+
await self.send(self.iopub_socket, 'error', content, parent_header=msg['header'])
361+
416362
content = {
417-
"protocol_version": "5.3",
418-
"ipython_version": [1, 1, 0, ""],
419-
"language_version": [0, 0, 1],
420-
"language": "python",
421-
"implementation": "python",
422-
"implementation_version": "3.7",
423-
"language_info": {
424-
"name": "python",
425-
"version": "1.0",
426-
'mimetype': "",
427-
'file_extension': ".py",
428-
#'pygments_lexer': "",
429-
'codemirror_mode': "",
430-
'nbconvert_exporter': "",
431-
},
432-
"banner": ""
363+
'execution_state': "idle",
433364
}
434-
await self.send(shell_socket, 'kernel_info_reply', content, parent_header=msg['header'], identities=identities)
435-
elif msg['header']["msg_type"] == "complete_request":
436365
await self.send(self.iopub_socket, 'status', content, parent_header=msg['header'])
366+
if msg['content'].get("store_history", True):
367+
self.execution_count += 1
368+
return
437369

438-
code = msg["content"]["code"]
439-
posn = msg["content"]["cursor_pos"]
440-
match = self.completion_re.match(code[0:posn].lower())
441-
if match:
442-
root = match[1].lower()
443-
words = self.ast_ctx.state.completions(root)
444-
words = words.union(await self.ast_ctx.handler.service_completions(root))
445-
words = words.union(await self.ast_ctx.handler.func_completions(root))
446-
words = words.union(self.ast_ctx.completions(root))
447-
else:
448-
root = ""
449-
words = set()
450-
# _LOGGER.debug(f"complete_request code={code}, posn={posn}, root={root}, words={words}")
370+
# if True or isinstance(self.ast_ctx.ast, ast.Expr):
371+
_LOGGER.debug("Executing: '%s' got result %s", code, result)
372+
if result is not None:
451373
content = {
452-
"status": "ok",
453-
"matches": sorted(list(words)),
454-
"cursor_start": msg["content"]["cursor_pos"] - len(root),
455-
"cursor_end": msg["content"]["cursor_pos"],
456-
"metadata": {},
374+
'execution_count': self.execution_count,
375+
'data': {"text/plain": repr(result)},
376+
'metadata': {}
457377
}
458-
await self.send(shell_socket, 'complete_reply', content, parent_header=msg['header'], identities=identities)
378+
await self.send(self.iopub_socket, 'execute_result', content, parent_header=msg['header'])
459379

460-
elif msg['header']["msg_type"] == "is_complete_request":
461-
code = msg['content']["code"]
462-
self.ast_ctx.parse(code)
463-
exc = self.ast_ctx.get_exception_obj()
380+
metadata = {
381+
"dependencies_met": True,
382+
"engine": self.engine_id,
383+
"status": "ok",
384+
"started": datetime.datetime.now().isoformat(),
385+
}
386+
content = {
387+
"status": "ok",
388+
"execution_count": self.execution_count,
389+
"user_variables": {},
390+
"payload": [],
391+
"user_expressions": {},
392+
}
393+
await self.send(shell_socket, 'execute_reply', content, metadata=metadata,
394+
parent_header=msg['header'], identities=identities)
395+
if msg['content'].get("store_history", True):
396+
self.execution_count += 1
397+
398+
elif msg['header']["msg_type"] == "kernel_info_request":
399+
content = {
400+
"protocol_version": "5.3",
401+
"ipython_version": [1, 1, 0, ""],
402+
"language_version": [0, 0, 1],
403+
"language": "python",
404+
"implementation": "python",
405+
"implementation_version": "3.7",
406+
"language_info": {
407+
"name": "python",
408+
"version": "1.0",
409+
'mimetype': "",
410+
'file_extension': ".py",
411+
#'pygments_lexer': "",
412+
'codemirror_mode': "",
413+
'nbconvert_exporter': "",
414+
},
415+
"banner": ""
416+
}
417+
await self.send(shell_socket, 'kernel_info_reply', content, parent_header=msg['header'], identities=identities)
464418

465-
# determine indent of last line
466-
indent = 0
467-
i = code.rfind("\n")
468-
if i >= 0:
469-
while i + 1 < len(code) and code[i+1] == " ":
470-
i += 1
471-
indent += 1
472-
if exc is None:
473-
if indent == 0:
474-
content = {
475-
# One of 'complete', 'incomplete', 'invalid', 'unknown'
476-
"status": 'complete',
477-
# If status is 'incomplete', indent should contain the characters to use
478-
# to indent the next line. This is only a hint: frontends may ignore it
479-
# and use their own autoindentation rules. For other statuses, this
480-
# field does not exist.
481-
#"indent": str,
482-
}
483-
else:
484-
content = {
485-
"status": 'incomplete',
486-
"indent": " " * indent,
487-
}
419+
elif msg['header']["msg_type"] == "complete_request":
420+
await self.send(self.iopub_socket, 'status', content, parent_header=msg['header'])
421+
422+
code = msg["content"]["code"]
423+
posn = msg["content"]["cursor_pos"]
424+
match = self.completion_re.match(code[0:posn].lower())
425+
if match:
426+
root = match[1].lower()
427+
words = self.ast_ctx.state.completions(root)
428+
words = words.union(await self.ast_ctx.handler.service_completions(root))
429+
words = words.union(await self.ast_ctx.handler.func_completions(root))
430+
words = words.union(self.ast_ctx.completions(root))
431+
else:
432+
root = ""
433+
words = set()
434+
# _LOGGER.debug(f"complete_request code={code}, posn={posn}, root={root}, words={words}")
435+
content = {
436+
"status": "ok",
437+
"matches": sorted(list(words)),
438+
"cursor_start": msg["content"]["cursor_pos"] - len(root),
439+
"cursor_end": msg["content"]["cursor_pos"],
440+
"metadata": {},
441+
}
442+
await self.send(shell_socket, 'complete_reply', content, parent_header=msg['header'], identities=identities)
443+
444+
elif msg['header']["msg_type"] == "is_complete_request":
445+
code = msg['content']["code"]
446+
self.ast_ctx.parse(code)
447+
exc = self.ast_ctx.get_exception_obj()
448+
449+
# determine indent of last line
450+
indent = 0
451+
i = code.rfind("\n")
452+
if i >= 0:
453+
while i + 1 < len(code) and code[i+1] == " ":
454+
i += 1
455+
indent += 1
456+
if exc is None:
457+
if indent == 0:
458+
content = {
459+
# One of 'complete', 'incomplete', 'invalid', 'unknown'
460+
"status": 'complete',
461+
# If status is 'incomplete', indent should contain the characters to use
462+
# to indent the next line. This is only a hint: frontends may ignore it
463+
# and use their own autoindentation rules. For other statuses, this
464+
# field does not exist.
465+
#"indent": str,
466+
}
488467
else:
489-
#
490-
# if the syntax error is right at the end, then we label it incomplete,
491-
# otherwise it's invalid
492-
#
493-
if str(exc).find("EOF while") >= 0:
494-
# if error is at ":" then increase indent
495-
if hasattr(exc, "lineno"):
496-
line = code.split("\n")[exc.lineno-1]
497-
if self.colon_end_re.match(line):
498-
indent += 4
499-
content = {
500-
"status": 'incomplete',
501-
"indent": " " * indent,
502-
}
503-
else:
504-
content = {
505-
"status": 'invalid',
506-
}
507-
# _LOGGER.debug(f"is_complete_request code={code}, exc={exc}, content={content}")
508-
await self.send(shell_socket, 'is_complete_reply', content, parent_header=msg['header'], identities=identities)
509-
elif msg['header']["msg_type"] == "comm_info_request":
510-
content = {
511-
"comms": {}
512-
}
513-
await self.send(shell_socket, 'comm_info_reply', content, parent_header=msg['header'], identities=identities)
514-
elif msg['header']["msg_type"] == "history_request":
515-
content = {
516-
"history": []
517-
}
518-
await self.send(shell_socket, 'history_reply', content, parent_header=msg['header'], identities=identities)
468+
content = {
469+
"status": 'incomplete',
470+
"indent": " " * indent,
471+
}
519472
else:
520-
_LOGGER.error("unknown msg_type: %s", msg['header']["msg_type"])
473+
#
474+
# if the syntax error is right at the end, then we label it incomplete,
475+
# otherwise it's invalid
476+
#
477+
if str(exc).find("EOF while") >= 0:
478+
# if error is at ":" then increase indent
479+
if hasattr(exc, "lineno"):
480+
line = code.split("\n")[exc.lineno-1]
481+
if self.colon_end_re.match(line):
482+
indent += 4
483+
content = {
484+
"status": 'incomplete',
485+
"indent": " " * indent,
486+
}
487+
else:
488+
content = {
489+
"status": 'invalid',
490+
}
491+
# _LOGGER.debug(f"is_complete_request code={code}, exc={exc}, content={content}")
492+
await self.send(shell_socket, 'is_complete_reply', content, parent_header=msg['header'], identities=identities)
521493

494+
elif msg['header']["msg_type"] == "comm_info_request":
522495
content = {
523-
'execution_state': "idle",
496+
"comms": {}
524497
}
525-
await self.send(self.iopub_socket, 'status', content, parent_header=msg['header'])
498+
await self.send(shell_socket, 'comm_info_reply', content, parent_header=msg['header'], identities=identities)
499+
500+
elif msg['header']["msg_type"] == "history_request":
501+
content = {
502+
"history": []
503+
}
504+
await self.send(shell_socket, 'history_reply', content, parent_header=msg['header'], identities=identities)
505+
506+
else:
507+
_LOGGER.error("unknown msg_type: %s", msg['header']["msg_type"])
508+
509+
content = {
510+
'execution_state': "idle",
511+
}
512+
await self.send(self.iopub_socket, 'status', content, parent_header=msg['header'])
526513

527514
async def control_listen(self, reader, writer):
528515
"""Task that listens to control messages."""

0 commit comments

Comments
 (0)