Skip to content
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

Make terminal_class configurable and provide a manager to detect terminal status on linux systems #626

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
85312f7
Fix missing await when call 'async_replace_file'
Oct 20, 2021
1aa046a
Merge branch 'jupyter-server:master' into master
Wh1isper Dec 2, 2021
00c92c2
patch execution_state filed into terminal
Dec 2, 2021
418cb18
Update jupyter_server/terminal/handlers.py
blink1073 Dec 2, 2021
e03952d
Update jupyter_server/terminal/handlers.py
blink1073 Dec 2, 2021
4f4502a
Record the first output to identify the terminal return
Dec 2, 2021
728319a
make set state idle if terminal return as a function
Dec 2, 2021
12a2cad
patch first_stdout into terminals
Dec 3, 2021
fdd7dff
set busy when receive message, set idled when ptyproc read
Dec 3, 2021
8e26e88
Make StatefulTerminalManager a class and configuable
Dec 3, 2021
695cf27
revert TerminalManager and make all state operation into StatefulTerm…
Dec 3, 2021
f4a07e3
add some test on StatefulTerminalManager
Dec 3, 2021
3c0688a
improve stateful terminal test
Dec 3, 2021
3e3b703
change test wait durations
Dec 3, 2021
36fc364
add block to wait init terminal
Dec 3, 2021
b6998c1
only test linux system
Dec 3, 2021
998efff
pre-commit run
Dec 3, 2021
d125c38
Update jupyter_server/tests/test_stateful_terminal.py
blink1073 Dec 3, 2021
1af3064
Update jupyter_server/tests/test_stateful_terminal.py
blink1073 Dec 3, 2021
1b6c158
Update jupyter_server/tests/test_stateful_terminal.py
blink1073 Dec 3, 2021
02f7fb8
Update jupyter_server/tests/test_stateful_terminal.py
blink1073 Dec 3, 2021
8c0186b
Update jupyter_server/tests/test_stateful_terminal.py
blink1073 Dec 3, 2021
d627bb1
Update jupyter_server/tests/test_stateful_terminal.py
blink1073 Dec 3, 2021
4111d90
Only run on Linux
blink1073 Dec 4, 2021
e242e56
add terminal_manager_class to support third-party terminal_manager
Dec 5, 2021
ea00345
change stateful terminal test config to terminal_manager_class
Dec 5, 2021
82847b1
add comments on StatefulTerminalManager
Dec 5, 2021
12f3af0
Compatible with terminado_available is False
Dec 7, 2021
5d4c9f9
pre-commit run
Dec 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions jupyter_server/terminal/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@
"""Tornado handlers for the terminal emulator."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import json

import terminado
from tornado import web

from jupyter_server._tz import utcnow
from ..base.handlers import JupyterHandler
from ..base.zmqhandlers import WebSocketMixin
from jupyter_server._tz import utcnow


class TermSocket(WebSocketMixin, JupyterHandler, terminado.TermSocket):
def initialize(self, *args, **kwargs):
super(TermSocket, self).initialize(*args, **kwargs)
self._first_stdout = ''

def origin_check(self):
"""Terminado adds redundant origin_check
Tornado already calls check_origin, so don't do anything here.
Expand All @@ -27,13 +33,32 @@ def get(self, *args, **kwargs):
def on_message(self, message):
super(TermSocket, self).on_message(message)
self._update_activity()
self._set_state_busy()

def write_message(self, message, binary=False):
super(TermSocket, self).write_message(message, binary=binary)
self._update_activity()
message_seg = json.loads(message)
if not self._first_stdout and message_seg[0] == 'stdout':
# Record the first output to identify the terminal return
# It works well for jupyterhub-singleuser and should also work for other debian-based mirrors
# fixme: May fail if terminal is not properly separated with ':' or change user after connect
# (Any change to the user, hostname or environment may render it invalid)
self._first_stdout = message_seg[1].split(':')[0].lstrip()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This meets my needs, but may not be a good general approach
Maybe we need to raise an issue with terminado?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it, it is terminal dependent, terminado is just reading from the pty: https://github.com/jupyter/terminado/blob/ab4ced0b77c8f198cdc27fe1d52c523635083461/terminado/websocket.py#L77

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it may be a little difficult to implement a reliable solution based on the current architecture. But we can delve into how to determine that an terminal is running

self.log.debug(f'take "{self._first_stdout}" as terminal returned')
if isinstance(message_seg[1], str) and message_seg[1].lstrip().startswith(self._first_stdout):
self._set_state_idle()

def _update_activity(self):
self.application.settings["terminal_last_activity"] = utcnow()
# terminal may not be around on deletion/cull
if self.term_name in self.terminal_manager.terminals:
self.terminal_manager.terminals[self.term_name].last_activity = utcnow()

def _set_state_busy(self):
if self.term_name in self.terminal_manager.terminals:
self.terminal_manager.terminals[self.term_name].execution_state = 'busy'

def _set_state_idle(self):
if self.term_name in self.terminal_manager.terminals:
self.log.debug('set terminal execution_state as idle')
self.terminal_manager.terminals[self.term_name].execution_state = 'idle'
3 changes: 2 additions & 1 deletion jupyter_server/terminal/terminalmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
from traitlets import Integer
from traitlets.config import LoggingConfigurable

from ..prometheus.metrics import TERMINAL_CURRENTLY_RUNNING_TOTAL
from jupyter_server._tz import isoformat
from jupyter_server._tz import utcnow
from ..prometheus.metrics import TERMINAL_CURRENTLY_RUNNING_TOTAL


class TerminalManager(LoggingConfigurable, terminado.NamedTermManager):
Expand Down Expand Up @@ -96,6 +96,7 @@ def get_terminal_model(self, name):
model = {
"name": name,
"last_activity": isoformat(term.last_activity),
'execution_state': getattr(term, 'execution_state', 'not connected yet')
}
return model

Expand Down