-
-
Notifications
You must be signed in to change notification settings - Fork 396
PR: Run GUI eventloop while waiting for input #438
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 21 commits
16e4f96
071e5f6
2d19be4
bad10cc
b2bd633
ddc6405
8bc89a5
0e485a0
0ee0cee
05a2959
2915f56
3487e7d
9397d53
45cedfc
b90b5d5
9f33e69
55f44a1
188c2a1
c0fc6b8
404c437
922bb55
f20c034
94e36a9
190e8ba
99e3d4f
bf3cb03
d16d6e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| import sys | ||
| import time | ||
| import uuid | ||
| import threading | ||
|
|
||
| try: | ||
| # jupyter_client >= 5, use tz-aware now | ||
|
|
@@ -40,6 +41,7 @@ | |
| from jupyter_client.session import Session | ||
|
|
||
| from ._version import kernel_protocol_version | ||
| from .inprocess.socket import DummySocket | ||
|
|
||
| CONTROL_PRIORITY = 1 | ||
| SHELL_PRIORITY = 10 | ||
|
|
@@ -170,6 +172,26 @@ def __init__(self, **kwargs): | |
| for msg_type in self.control_msg_types: | ||
| self.control_handlers[msg_type] = getattr(self, msg_type) | ||
|
|
||
| self._stdin_msg = None | ||
| self._stdin_lock = threading.Lock() | ||
|
|
||
| if isinstance(self.stdin_socket, DummySocket): | ||
| # This is a test | ||
| self.stdin_stream = None | ||
| return | ||
| self.stdin_stream = ZMQStream(self.stdin_socket) | ||
|
|
||
| def handle_msg(msg): | ||
| idents, msg = self.session.feed_identities(msg, copy=False) | ||
| try: | ||
| msg = self.session.deserialize(msg, content=True, copy=False) | ||
| except Exception: | ||
| self.log.error("Invalid Message", exc_info=True) | ||
| return | ||
| self._stdin_msg = msg | ||
|
|
||
| self.stdin_stream.on_recv(handle_msg, copy=False) | ||
|
|
||
| @gen.coroutine | ||
| def dispatch_control(self, msg): | ||
| """dispatch control requests""" | ||
|
|
@@ -860,6 +882,7 @@ def raw_input(self, prompt=''): | |
| ) | ||
|
|
||
| def _input_request(self, prompt, ident, parent, password=False): | ||
| """Send an input request to the frontend and wait for the reply.""" | ||
| # Flush output before making the request. | ||
| sys.stderr.flush() | ||
| sys.stdout.flush() | ||
|
|
@@ -873,22 +896,15 @@ def _input_request(self, prompt, ident, parent, password=False): | |
| else: | ||
| raise | ||
|
|
||
| # Send the input request. | ||
| content = json_clean(dict(prompt=prompt, password=password)) | ||
| self.session.send(self.stdin_socket, u'input_request', content, parent, | ||
| ident=ident) | ||
| with self._stdin_lock: | ||
| self._stdin_msg = None | ||
| # Send the input request. | ||
| content = json_clean(dict(prompt=prompt, password=password)) | ||
| self.session.send(self.stdin_socket, u'input_request', content, parent, | ||
| ident=ident) | ||
| # Await a response. | ||
| reply = self._wait_input_request_reply() | ||
|
|
||
| # Await a response. | ||
| while True: | ||
| try: | ||
| ident, reply = self.session.recv(self.stdin_socket, 0) | ||
| except Exception: | ||
| self.log.warning("Invalid Message:", exc_info=True) | ||
| except KeyboardInterrupt: | ||
| # re-raise KeyboardInterrupt, to truncate traceback | ||
| raise KeyboardInterrupt | ||
| else: | ||
| break | ||
| try: | ||
| value = py3compat.unicode_to_str(reply['content']['value']) | ||
| except: | ||
|
|
@@ -899,6 +915,41 @@ def _input_request(self, prompt, ident, parent, password=False): | |
| raise EOFError | ||
| return value | ||
|
|
||
| def _wait_input_request_reply(self): | ||
| """Wait for an input request reply. | ||
|
|
||
| Raises | ||
| ------ | ||
| KeyboardInterrupt if a keyboard interrupt is recieved. | ||
| """ | ||
| # Await a response. | ||
| reply = None | ||
| while reply is None: | ||
| try: | ||
| reply = self._input_request_loop_step() | ||
| except Exception: | ||
| self.log.warning("Invalid Message:", exc_info=True) | ||
| except KeyboardInterrupt: | ||
| # re-raise KeyboardInterrupt, to truncate traceback | ||
| raise KeyboardInterrupt | ||
| return reply | ||
|
|
||
| def _input_request_loop_step(self): | ||
| """Do one step of the input request loop.""" | ||
| # Allow GUI event loop to update | ||
| if sys.version_info >= (3, 4): | ||
|
||
| is_main_thread = (threading.current_thread() is | ||
| threading.main_thread()) | ||
| else: | ||
| is_main_thread = isinstance(threading.current_thread(), | ||
| threading._MainThread) | ||
| if is_main_thread and self.eventloop: | ||
| self.eventloop(self) | ||
| return self._stdin_msg | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and possibly here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| else: | ||
| ident, reply = self.session.recv(self.stdin_socket, 0) | ||
| return reply | ||
|
|
||
| def _at_shutdown(self): | ||
| """Actions taken at shutdown by the kernel, called by python's atexit. | ||
| """ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need to lock here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The messages are processed by a single thread so a lock would not be useful here.