|
116 | 116 | from jupyter_server.extension.config import ExtensionConfigManager
|
117 | 117 | from jupyter_server.traittypes import TypeFromClasses
|
118 | 118 |
|
| 119 | +# Tolerate missing terminado package. |
| 120 | +try: |
| 121 | + from .terminal import TerminalManager |
| 122 | + terminado_available = True |
| 123 | +except ImportError: |
| 124 | + terminado_available = False |
| 125 | + |
119 | 126 | #-----------------------------------------------------------------------------
|
120 | 127 | # Module globals
|
121 | 128 | #-----------------------------------------------------------------------------
|
@@ -284,7 +291,7 @@ def init_settings(self, jupyter_app, kernel_manager, contents_manager,
|
284 | 291 | allow_password_change=jupyter_app.allow_password_change,
|
285 | 292 | server_root_dir=root_dir,
|
286 | 293 | jinja2_env=env,
|
287 |
| - terminals_available=False, # Set later if terminals are available |
| 294 | + terminals_available=terminado_available and jupyter_app.terminals_enabled, |
288 | 295 | serverapp=jupyter_app
|
289 | 296 | )
|
290 | 297 |
|
@@ -589,6 +596,8 @@ class ServerApp(JupyterApp):
|
589 | 596 | ContentsManager, FileContentsManager, AsyncContentsManager, AsyncFileContentsManager, NotebookNotary,
|
590 | 597 | GatewayKernelManager, GatewayKernelSpecManager, GatewaySessionManager, GatewayClient
|
591 | 598 | ]
|
| 599 | + if terminado_available: # Only necessary when terminado is available |
| 600 | + classes.append(TerminalManager) |
592 | 601 |
|
593 | 602 | subcommands = dict(
|
594 | 603 | list=(JupyterServerListApp, JupyterServerListApp.description.splitlines()[0]),
|
@@ -1329,6 +1338,15 @@ def _update_server_extensions(self, change):
|
1329 | 1338 | is not available.
|
1330 | 1339 | """))
|
1331 | 1340 |
|
| 1341 | + # Since use of terminals is also a function of whether the terminado package is |
| 1342 | + # available, this variable holds the "final indication" of whether terminal functionality |
| 1343 | + # should be considered (particularly during shutdown/cleanup). It is enabled only |
| 1344 | + # once both the terminals "service" can be initialized and terminals_enabled is True. |
| 1345 | + # Note: this variable is slightly different from 'terminals_available' in the web settings |
| 1346 | + # in that this variable *could* remain false if terminado is available, yet the terminal |
| 1347 | + # service's initialization still fails. As a result, this variable holds the truth. |
| 1348 | + terminals_available = False |
| 1349 | + |
1332 | 1350 | authenticate_prometheus = Bool(
|
1333 | 1351 | True,
|
1334 | 1352 | help=""""
|
@@ -1547,7 +1565,7 @@ def init_terminals(self):
|
1547 | 1565 | try:
|
1548 | 1566 | from .terminal import initialize
|
1549 | 1567 | initialize(self.web_app, self.root_dir, self.connection_url, self.terminado_settings)
|
1550 |
| - self.web_app.settings['terminals_available'] = True |
| 1568 | + self.terminals_available = True |
1551 | 1569 | except ImportError as e:
|
1552 | 1570 | self.log.warning(_i18n("Terminals not available (error was %s)"), e)
|
1553 | 1571 |
|
@@ -1693,11 +1711,8 @@ def shutdown_no_activity(self):
|
1693 | 1711 | if len(km) != 0:
|
1694 | 1712 | return # Kernels still running
|
1695 | 1713 |
|
1696 |
| - try: |
| 1714 | + if self.terminals_available: |
1697 | 1715 | term_mgr = self.web_app.settings['terminal_manager']
|
1698 |
| - except KeyError: |
1699 |
| - pass # Terminals not enabled |
1700 |
| - else: |
1701 | 1716 | if term_mgr.terminals:
|
1702 | 1717 | return # Terminals still running
|
1703 | 1718 |
|
@@ -1846,6 +1861,21 @@ def cleanup_kernels(self):
|
1846 | 1861 | self.log.info(kernel_msg % n_kernels)
|
1847 | 1862 | run_sync(self.kernel_manager.shutdown_all())
|
1848 | 1863 |
|
| 1864 | + def cleanup_terminals(self): |
| 1865 | + """Shutdown all terminals. |
| 1866 | +
|
| 1867 | + The terminals will shutdown themselves when this process no longer exists, |
| 1868 | + but explicit shutdown allows the TerminalManager to cleanup. |
| 1869 | + """ |
| 1870 | + if not self.terminals_available: |
| 1871 | + return |
| 1872 | + |
| 1873 | + terminal_manager = self.web_app.settings['terminal_manager'] |
| 1874 | + n_terminals = len(terminal_manager.list()) |
| 1875 | + terminal_msg = trans.ngettext('Shutting down %d terminal', 'Shutting down %d terminals', n_terminals) |
| 1876 | + self.log.info(terminal_msg % n_terminals) |
| 1877 | + run_sync(terminal_manager.terminate_all()) |
| 1878 | + |
1849 | 1879 | def running_server_info(self, kernel_count=True):
|
1850 | 1880 | "Return the current working directory and the server url information"
|
1851 | 1881 | info = self.contents_manager.info_string() + "\n"
|
@@ -2076,6 +2106,7 @@ def _cleanup(self):
|
2076 | 2106 | self.remove_server_info_file()
|
2077 | 2107 | self.remove_browser_open_files()
|
2078 | 2108 | self.cleanup_kernels()
|
| 2109 | + self.cleanup_terminals() |
2079 | 2110 |
|
2080 | 2111 | def start_ioloop(self):
|
2081 | 2112 | """Start the IO Loop."""
|
|
0 commit comments