-
-
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 18 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 | ||
|
|
@@ -41,6 +42,7 @@ | |
|
|
||
| from ._version import kernel_protocol_version | ||
|
|
||
|
|
||
| CONTROL_PRIORITY = 1 | ||
| SHELL_PRIORITY = 10 | ||
| ABORT_PRIORITY = 20 | ||
|
|
@@ -170,6 +172,9 @@ 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() | ||
|
|
||
| @gen.coroutine | ||
| def dispatch_control(self, msg): | ||
| """dispatch control requests""" | ||
|
|
@@ -860,6 +865,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 +879,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 +898,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.
I think all these new lines here should be moved to the
Kernelclass (present inkernelbase.py). In particular,handle_msgshould be part ofdispatch_stdin(following whatdispatch_controlanddispatch_shelldo forcontrol_streamandshell_streams, respectively).However, I don't know what the implications of adding a new stream here are for the Jupyter protocol. Pinging @takluyver for his insights about that.
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.
I moved it. I don't want to process the messages recieved on stdin so I don't think
dispatch_stdinis a good idea. We are not dispatching the messages, only saving the most recent. A stream is just a wrapper around a socket. I am not adding a socket, so the protocol is unchanged. The only reason I need a stream around the socket is to get a qt event when a message is recieved.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.
Yes, AFAIK @impact27 is right - there shouldn't be a difference to the protocol. A pyzmq stream is a wrapper around a socket to tie it to an event loop. The stdin socket already existed, so whether the code wraps it in a stream or not should be an implementation detail.
(I haven't tried to review the PR, just answering the question I was mentioned for)