From bc920e98791f58dc49bb82ee34e9ec13e807085d Mon Sep 17 00:00:00 2001 From: Abhinav Sagar <40603139+abhinavsagar@users.noreply.github.com> Date: Fri, 7 Jun 2019 15:46:09 +0530 Subject: [PATCH 01/35] .travis.yml: The 'sudo' tag is now deprecated in Travis CI (#4654) --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cd7304aa0..df263d4e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,6 @@ cache: python: - 3.6 -sudo: required - env: global: From 922e97922c80e0cef7edd25a3455190cb9ae5ea8 Mon Sep 17 00:00:00 2001 From: Pierre Tholoniat <32133147+tholoz@users.noreply.github.com> Date: Fri, 7 Jun 2019 20:17:18 +1000 Subject: [PATCH 02/35] Fix French typo (#4651) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrected "Échec de la connection" to "Échec de la connexion" --- notebook/i18n/fr_FR/LC_MESSAGES/nbjs.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook/i18n/fr_FR/LC_MESSAGES/nbjs.po b/notebook/i18n/fr_FR/LC_MESSAGES/nbjs.po index f01c7e97d..a8b01bf13 100644 --- a/notebook/i18n/fr_FR/LC_MESSAGES/nbjs.po +++ b/notebook/i18n/fr_FR/LC_MESSAGES/nbjs.po @@ -1187,7 +1187,7 @@ msgstr "" #: notebook/static/notebook/js/notificationarea.js:166 msgid "Connection failed" -msgstr "Échec de la connection" +msgstr "Échec de la connexion" #: notebook/static/notebook/js/notificationarea.js:179 msgid "No kernel" From 6174498ab0c4047aaed81a1bb5aa71b1fd8c842b Mon Sep 17 00:00:00 2001 From: Tim <33530562+tmetzl@users.noreply.github.com> Date: Fri, 7 Jun 2019 12:20:33 +0200 Subject: [PATCH 03/35] Add id for command palette button group (#4661) --- notebook/static/notebook/js/maintoolbar.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/notebook/static/notebook/js/maintoolbar.js b/notebook/static/notebook/js/maintoolbar.js index 2207be4a2..e870053b6 100644 --- a/notebook/static/notebook/js/maintoolbar.js +++ b/notebook/static/notebook/js/maintoolbar.js @@ -58,7 +58,9 @@ define([ ], 'run_int'], [''], - [['jupyter-notebook:show-command-palette']] + [ + ['jupyter-notebook:show-command-palette'], + 'cmd_palette'] ]; this.construct(grps); }; From dd0f5d2608b3e0501e1e2f94d5b1f42a467ae8fc Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 13 Jun 2019 15:36:45 -0700 Subject: [PATCH 04/35] bfix: shutdown_server returns True when pid exists `check_pid` returns `True` if the PID for a notebook server still exists. Therefore, the `if check_pid(pid):` statements on lines 424 and 437 evaluate to `True` even though the notebook server is still running. This commit simply adds a `not` to each line: `if not check_pid(pid):` so that the conditional only evaluates to `True` if `check_pid` returns `False`, which happens when the notebook server has shutdown, as expected. --- notebook/notebookapp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py index a1a210826..ac97cf847 100755 --- a/notebook/notebookapp.py +++ b/notebook/notebookapp.py @@ -421,7 +421,7 @@ def shutdown_server(server_info, timeout=5, log=None): # Poll to see if it shut down. for _ in range(timeout*10): - if check_pid(pid): + if not check_pid(pid): if log: log.debug("Server PID %s is gone", pid) return True time.sleep(0.1) @@ -434,7 +434,7 @@ def shutdown_server(server_info, timeout=5, log=None): # Poll to see if it shut down. for _ in range(timeout * 10): - if check_pid(pid): + if not check_pid(pid): if log: log.debug("Server PID %s is gone", pid) return True time.sleep(0.1) From 37d795e5917e3a7ab8e97e2619826d9c0ad99302 Mon Sep 17 00:00:00 2001 From: Joshua Zeltser Date: Wed, 5 Jun 2019 17:05:02 +0100 Subject: [PATCH 05/35] Solved Issue 4015 --- notebook/static/base/js/dialog.js | 1 + notebook/templates/tree.html | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/notebook/static/base/js/dialog.js b/notebook/static/base/js/dialog.js index b59d9908b..e016077a4 100644 --- a/notebook/static/base/js/dialog.js +++ b/notebook/static/base/js/dialog.js @@ -60,6 +60,7 @@ define(['jquery', }) .append($(" @@ -112,7 +114,7 @@ data-server-root="{{server_root}}"
-
@@ -114,7 +112,7 @@ data-server-root="{{server_root}}"
From 1a9a096f9c5d96a1e157adbc40fbfefb9398bdb4 Mon Sep 17 00:00:00 2001 From: Joshua Zeltser Date: Thu, 20 Jun 2019 15:29:09 +0100 Subject: [PATCH 16/35] Solved issue #4006 --- notebook/static/base/js/dialog.js | 1 + notebook/static/notebook/js/celltoolbarpresets/default.js | 8 +++++--- notebook/static/notebook/js/notebook.js | 5 +++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/notebook/static/base/js/dialog.js b/notebook/static/base/js/dialog.js index b59d9908b..dc9a2c943 100644 --- a/notebook/static/base/js/dialog.js +++ b/notebook/static/base/js/dialog.js @@ -234,6 +234,7 @@ define(['jquery', }); modal_obj.on('shown.bs.modal', function(){ editor.refresh(); }); + modal_obj.on('hide.bs.modal', function(){options.edit_metadata_button.focus(); }); }; var edit_attachments = function (options) { diff --git a/notebook/static/notebook/js/celltoolbarpresets/default.js b/notebook/static/notebook/js/celltoolbarpresets/default.js index b21bbf839..e4a591430 100644 --- a/notebook/static/notebook/js/celltoolbarpresets/default.js +++ b/notebook/static/notebook/js/celltoolbarpresets/default.js @@ -10,7 +10,7 @@ define([ var CellToolbar = celltoolbar.CellToolbar; - var raw_edit = function (cell) { + var raw_edit = function (cell , edit_metadata_button) { dialog.edit_metadata({ md: cell.metadata, callback: function (md) { @@ -18,7 +18,8 @@ define([ }, name: i18n.msg._('Cell'), notebook: this.notebook, - keyboard_manager: this.keyboard_manager + keyboard_manager: this.keyboard_manager, + edit_metadata_button: edit_metadata_button }); }; @@ -28,9 +29,10 @@ define([ .addClass("btn btn-default btn-xs") .text(i18n.msg._("Edit Metadata")) .click( function () { - raw_edit(cell); + raw_edit(cell, this); return false; }); + button_container.append(button); }; diff --git a/notebook/static/notebook/js/notebook.js b/notebook/static/notebook/js/notebook.js index 556f3999a..595353ace 100644 --- a/notebook/static/notebook/js/notebook.js +++ b/notebook/static/notebook/js/notebook.js @@ -938,6 +938,7 @@ define([ if (this.mode !== 'command') { cell.command_mode(); this.mode = 'command'; + $('div[class*="code_cell"]') this.events.trigger('command_mode.Notebook'); this.keyboard_manager.command_mode(); } @@ -965,6 +966,10 @@ define([ if (cell && this.mode !== 'edit') { cell.edit_mode(); this.mode = 'edit'; +<<<<<<< HEAD +======= + $('div[class*="code_cell"]') +>>>>>>> 1dbabaa0a... #4006: fixed the edit-metadata button so that focus returns to it after closing the popup this.events.trigger('edit_mode.Notebook'); this.keyboard_manager.edit_mode(); } From fc2c46141deb06b9e939b295ce254907830d3147 Mon Sep 17 00:00:00 2001 From: Joshua Zeltser Date: Mon, 24 Jun 2019 16:34:08 +0100 Subject: [PATCH 17/35] removed merge conflicts --- notebook/static/notebook/js/notebook.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/notebook/static/notebook/js/notebook.js b/notebook/static/notebook/js/notebook.js index 595353ace..556f3999a 100644 --- a/notebook/static/notebook/js/notebook.js +++ b/notebook/static/notebook/js/notebook.js @@ -938,7 +938,6 @@ define([ if (this.mode !== 'command') { cell.command_mode(); this.mode = 'command'; - $('div[class*="code_cell"]') this.events.trigger('command_mode.Notebook'); this.keyboard_manager.command_mode(); } @@ -966,10 +965,6 @@ define([ if (cell && this.mode !== 'edit') { cell.edit_mode(); this.mode = 'edit'; -<<<<<<< HEAD -======= - $('div[class*="code_cell"]') ->>>>>>> 1dbabaa0a... #4006: fixed the edit-metadata button so that focus returns to it after closing the popup this.events.trigger('edit_mode.Notebook'); this.keyboard_manager.edit_mode(); } From 8d98489f84d68bdd29e64d1479949a773c9ae584 Mon Sep 17 00:00:00 2001 From: Scott Sanderson Date: Mon, 24 Jun 2019 16:41:52 -0400 Subject: [PATCH 18/35] BUG: Pin preact and friends using ~ rather than ^. (#4681) The most recent release of preact-compat appears to have broken notebook. --- bower.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bower.json b/bower.json index 0136a82da..f2e439e90 100644 --- a/bower.json +++ b/bower.json @@ -16,9 +16,9 @@ "marked": "~0.5", "MathJax": "^2.7.4", "moment": "~2.19.3", - "preact": "https://unpkg.com/preact@^7.2.0/dist/preact.min.js", - "preact-compat": "https://unpkg.com/preact-compat@^3.14.3/dist/preact-compat.min.js", - "proptypes": "https://unpkg.com/proptypes@^0.14.4/index.js", + "preact": "https://unpkg.com/preact@~7.2.0/dist/preact.min.js", + "preact-compat": "https://unpkg.com/preact-compat@~3.14.3/dist/preact-compat.min.js", + "proptypes": "https://unpkg.com/proptypes@~0.14.4/index.js", "requirejs": "~2.2", "requirejs-text": "~2.0.15", "requirejs-plugins": "~1.0.3", From 973b06610b481afaf06eac71d4250a0b5030b35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 25 Jun 2019 01:08:16 +0200 Subject: [PATCH 19/35] Fix typo --- .../Distributing Jupyter Extensions as Python Packages.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb b/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb index aee390a3c..3b284ac53 100644 --- a/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb +++ b/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb @@ -226,7 +226,7 @@ }, "source": [ "### Defining the server extension and nbextension\n", - "This example again shows that the server extension and its `load_jupyter_server_extension` function are defined in the `__init__.py` file. This time, there is also a function `_jupyter_nbextension_path` for the nbextension.\n", + "This example again shows that the server extension and its `load_jupyter_server_extension` function are defined in the `__init__.py` file. This time, there is also a function `_jupyter_nbextension_paths` for the nbextension.\n", "\n", "#### `my_fancy_module/__init__.py`\n", "\n", From 83f591b81f0d790229d6cbf9cd50ff986e3bc73d Mon Sep 17 00:00:00 2001 From: Solaris Date: Tue, 25 Jun 2019 10:28:36 +0800 Subject: [PATCH 20/35] Fix binary message issue when configured to forward to gateway (#4576) If the message is bytes, we should set binary=True This is a port from NB2KG PR jupyter/nb2kg#33 --- notebook/gateway/handlers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/notebook/gateway/handlers.py b/notebook/gateway/handlers.py index 0fbbfb18b..06d6cc8fe 100644 --- a/notebook/gateway/handlers.py +++ b/notebook/gateway/handlers.py @@ -80,6 +80,8 @@ class WebSocketChannelsHandler(WebSocketHandler, IPythonHandler): """Send message back to notebook client. This is called via callback from self.gateway._read_messages.""" self.log.debug("Receiving message from gateway: {}".format(message)) if self.ws_connection: # prevent WebSocketClosedError + if isinstance(message, bytes): + binary = True super(WebSocketChannelsHandler, self).write_message(message, binary=binary) elif self.log.isEnabledFor(logging.DEBUG): msg_summary = WebSocketChannelsHandler._get_message_summary(json_decode(utf8(message))) From 191dc333f6772cb87358979c4714c9edc8e36511 Mon Sep 17 00:00:00 2001 From: Kevin Bates Date: Wed, 19 Jun 2019 16:39:08 -0700 Subject: [PATCH 21/35] Modify/add various timeout values The connect and request timeout defaults have been updated from 20 to 60 seconds and a default value of 40 has been added for KERNEL_LAUNCH_TIMEOUT. The code ensures that KERNEL_LAUNCH_TIMEOUT is in the env and that the value of the request timeout is at least 2 greather than KERNEL_LAUNCH_TIMEOUT. This PR is port of the NB2KG PRs 35 and 38. --- notebook/gateway/managers.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/notebook/gateway/managers.py b/notebook/gateway/managers.py index 6c97e57a9..67de68802 100644 --- a/notebook/gateway/managers.py +++ b/notebook/gateway/managers.py @@ -35,6 +35,7 @@ class GatewayClient(SingletonConfigurable): ) url_env = 'JUPYTER_GATEWAY_URL' + @default('url') def _url_default(self): return os.environ.get(self.url_env) @@ -55,6 +56,7 @@ class GatewayClient(SingletonConfigurable): ) ws_url_env = 'JUPYTER_GATEWAY_WS_URL' + @default('ws_url') def _ws_url_default(self): default_value = os.environ.get(self.ws_url_env) @@ -100,7 +102,7 @@ class GatewayClient(SingletonConfigurable): def _kernelspecs_resource_endpoint_default(self): return os.environ.get(self.kernelspecs_resource_endpoint_env, self.kernelspecs_resource_endpoint_default_value) - connect_timeout_default_value = 20.0 + connect_timeout_default_value = 60.0 connect_timeout_env = 'JUPYTER_GATEWAY_CONNECT_TIMEOUT' connect_timeout = Float(default_value=connect_timeout_default_value, config=True, help="""The time allowed for HTTP connection establishment with the Gateway server. @@ -110,7 +112,7 @@ class GatewayClient(SingletonConfigurable): def connect_timeout_default(self): return float(os.environ.get('JUPYTER_GATEWAY_CONNECT_TIMEOUT', self.connect_timeout_default_value)) - request_timeout_default_value = 20.0 + request_timeout_default_value = 60.0 request_timeout_env = 'JUPYTER_GATEWAY_REQUEST_TIMEOUT' request_timeout = Float(default_value=request_timeout_default_value, config=True, help="""The time allowed for HTTP request completion. (JUPYTER_GATEWAY_REQUEST_TIMEOUT env var)""") @@ -171,7 +173,7 @@ class GatewayClient(SingletonConfigurable): headers_default_value = '{}' headers_env = 'JUPYTER_GATEWAY_HEADERS' - headers = Unicode(default_value=headers_default_value, allow_none=True,config=True, + headers = Unicode(default_value=headers_default_value, allow_none=True, config=True, help="""Additional HTTP headers to pass on the request. This value will be converted to a dict. (JUPYTER_GATEWAY_HEADERS env var) """ @@ -222,11 +224,21 @@ class GatewayClient(SingletonConfigurable): def gateway_enabled(self): return bool(self.url is not None and len(self.url) > 0) + # Ensure KERNEL_LAUNCH_TIMEOUT has a default value. + KERNEL_LAUNCH_TIMEOUT = int(os.environ.get('KERNEL_LAUNCH_TIMEOUT', 40)) + os.environ['KERNEL_LAUNCH_TIMEOUT'] = KERNEL_LAUNCH_TIMEOUT + + LAUNCH_TIMEOUT_PAD = int(os.environ.get('LAUNCH_TIMEOUT_PAD', 2)) + def init_static_args(self): """Initialize arguments used on every request. Since these are static values, we'll perform this operation once. """ + # Ensure that request timeout is at least "pad" greater than launch timeout. + if self.request_timeout < float(GatewayClient.KERNEL_LAUNCH_TIMEOUT + GatewayClient.LAUNCH_TIMEOUT_PAD): + self.request_timeout = float(GatewayClient.KERNEL_LAUNCH_TIMEOUT + GatewayClient.LAUNCH_TIMEOUT_PAD) + self._static_args['headers'] = json.loads(self.headers) self._static_args['headers'].update({'Authorization': 'token {}'.format(self.auth_token)}) self._static_args['connect_timeout'] = self.connect_timeout @@ -270,13 +282,13 @@ def gateway_request(endpoint, **kwargs): "Check to be sure the Gateway instance is running.".format(GatewayClient.instance().url)) except HTTPError: # This can occur if the host is valid (e.g., foo.com) but there's nothing there. - raise web.HTTPError(504, "Error attempting to connect to Gateway server url '{}'. " \ - "Ensure gateway url is valid and the Gateway instance is running.".format( - GatewayClient.instance().url)) + raise web.HTTPError(504, "Error attempting to connect to Gateway server url '{}'. " + "Ensure gateway url is valid and the Gateway instance is running.". + format(GatewayClient.instance().url)) except gaierror as e: raise web.HTTPError(404, "The Gateway server specified in the gateway_url '{}' doesn't appear to be valid. " - "Ensure gateway url is valid and the Gateway instance is running.".format( - GatewayClient.instance().url)) + "Ensure gateway url is valid and the Gateway instance is running.". + format(GatewayClient.instance().url)) raise gen.Return(response) @@ -409,7 +421,7 @@ class GatewayKernelManager(MappingKernelManager): self.log.debug("Request list kernels: %s", kernel_url) response = yield gateway_request(kernel_url, method='GET') kernels = json_decode(response.body) - self._kernels = {x['id']:x for x in kernels} + self._kernels = {x['id']: x for x in kernels} raise gen.Return(kernels) @gen.coroutine @@ -420,6 +432,10 @@ class GatewayKernelManager(MappingKernelManager): ========== kernel_id : uuid The id of the kernel to shutdown. + now : bool + Shutdown the kernel immediately (True) or gracefully (False) + restart : bool + The purpose of this shutdown is to restart the kernel (True) """ kernel_url = self._get_kernel_endpoint_url(kernel_id) self.log.debug("Request shutdown kernel at: %s", kernel_url) From 6939fae22baba7a88495ecdc7cea2ea1c6392169 Mon Sep 17 00:00:00 2001 From: Kevin Bates Date: Wed, 19 Jun 2019 16:18:04 -0700 Subject: [PATCH 22/35] Add authorization token to header only if no authorization already --- notebook/gateway/managers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/notebook/gateway/managers.py b/notebook/gateway/managers.py index 67de68802..6b082192c 100644 --- a/notebook/gateway/managers.py +++ b/notebook/gateway/managers.py @@ -191,7 +191,7 @@ class GatewayClient(SingletonConfigurable): @default('auth_token') def _auth_token_default(self): - return os.environ.get(self.auth_token_env) + return os.environ.get(self.auth_token_env, '') validate_cert_default_value = True validate_cert_env = 'JUPYTER_GATEWAY_VALIDATE_CERT' @@ -240,7 +240,10 @@ class GatewayClient(SingletonConfigurable): self.request_timeout = float(GatewayClient.KERNEL_LAUNCH_TIMEOUT + GatewayClient.LAUNCH_TIMEOUT_PAD) self._static_args['headers'] = json.loads(self.headers) - self._static_args['headers'].update({'Authorization': 'token {}'.format(self.auth_token)}) + if 'Authorization' not in self._static_args['headers'].keys(): + self._static_args['headers'].update({ + 'Authorization': 'token {}'.format(self.auth_token) + }) self._static_args['connect_timeout'] = self.connect_timeout self._static_args['request_timeout'] = self.request_timeout self._static_args['validate_cert'] = self.validate_cert From f68e34f198ccd93639458d1e1310fffe8c6ec858 Mon Sep 17 00:00:00 2001 From: Kevin Bates Date: Wed, 19 Jun 2019 16:24:23 -0700 Subject: [PATCH 23/35] Add keepalive ping on gateway websocket --- notebook/gateway/handlers.py | 16 +++++++++++++++- notebook/gateway/managers.py | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/notebook/gateway/handlers.py b/notebook/gateway/handlers.py index 06d6cc8fe..cdfbb64e5 100644 --- a/notebook/gateway/handlers.py +++ b/notebook/gateway/handlers.py @@ -10,7 +10,7 @@ from ..utils import url_path_join from tornado import gen, web from tornado.concurrent import Future -from tornado.ioloop import IOLoop +from tornado.ioloop import IOLoop, PeriodicCallback from tornado.websocket import WebSocketHandler, websocket_connect from tornado.httpclient import HTTPRequest from tornado.escape import url_escape, json_decode, utf8 @@ -21,12 +21,16 @@ from traitlets.config.configurable import LoggingConfigurable from .managers import GatewayClient +# Keepalive ping interval (default: 30 seconds) +GATEWAY_WS_PING_INTERVAL_SECS = int(os.getenv('GATEWAY_WS_PING_INTERVAL_SECS', 30)) + class WebSocketChannelsHandler(WebSocketHandler, IPythonHandler): session = None gateway = None kernel_id = None + ping_callback = None def set_default_headers(self): """Undo the set_default_headers in IPythonHandler which doesn't make sense for websockets""" @@ -63,8 +67,18 @@ class WebSocketChannelsHandler(WebSocketHandler, IPythonHandler): self.kernel_id = cast_unicode(kernel_id, 'ascii') super(WebSocketChannelsHandler, self).get(kernel_id=kernel_id, *args, **kwargs) + def send_ping(self): + if self.ws_connection is None and self.ping_callback is not None: + self.ping_callback.stop() + return + + self.ping(b'') + def open(self, kernel_id, *args, **kwargs): """Handle web socket connection open to notebook server and delegate to gateway web socket handler """ + self.ping_callback = PeriodicCallback(self.send_ping, GATEWAY_WS_PING_INTERVAL_SECS * 1000) + self.ping_callback.start() + self.gateway.on_open( kernel_id=kernel_id, message_callback=self.write_message, diff --git a/notebook/gateway/managers.py b/notebook/gateway/managers.py index 6b082192c..7f2ff8695 100644 --- a/notebook/gateway/managers.py +++ b/notebook/gateway/managers.py @@ -226,7 +226,7 @@ class GatewayClient(SingletonConfigurable): # Ensure KERNEL_LAUNCH_TIMEOUT has a default value. KERNEL_LAUNCH_TIMEOUT = int(os.environ.get('KERNEL_LAUNCH_TIMEOUT', 40)) - os.environ['KERNEL_LAUNCH_TIMEOUT'] = KERNEL_LAUNCH_TIMEOUT + os.environ['KERNEL_LAUNCH_TIMEOUT'] = str(KERNEL_LAUNCH_TIMEOUT) LAUNCH_TIMEOUT_PAD = int(os.environ.get('LAUNCH_TIMEOUT_PAD', 2)) From efc0f0089e613ef3d2a2c9fb8242c69933dd3d06 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 25 Jun 2019 08:53:50 +0200 Subject: [PATCH 24/35] Allow ?no_track_activity=1 to opt-out of activity tracking (#4235) * Don't track API requests with `?no_track_activity=1` in the activity counter allows external idle-culling scripts to avoid updating the activity counter * Don't track kernel shutdown as kernel activity this causes idle-kernel shutdowns to restart the idle-shutdown timer user-requested shutdowns will still be tracked as api activity * test ?no_track_activity=1 tracking * Changelog for activity --- docs/source/changelog.rst | 10 ++++++++ notebook/base/handlers.py | 6 ++++- notebook/services/api/tests/test_api.py | 29 ++++++++++++++++------ notebook/services/kernels/kernelmanager.py | 1 - 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 51931f20c..daa7a858d 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -21,6 +21,16 @@ We strongly recommend that you upgrade pip to version 9+ of pip before upgrading Use ``pip install pip --upgrade`` to upgrade pip. Check pip version with ``pip --version``. +.. _release-6.0.0: + +6.0.0 +----- + +- add ``?no_track_activity=1`` argument to allow API requests + to not be registered as activity (e.g. API calls by external activity monitors). +- Kernels shutting down due to an idle timeout is no longer considered + an activity-updating event. + .. _release-5.7.8: 5.7.8 diff --git a/notebook/base/handlers.py b/notebook/base/handlers.py index cd801c9a1..31805ebde 100755 --- a/notebook/base/handlers.py +++ b/notebook/base/handlers.py @@ -650,7 +650,11 @@ class APIHandler(IPythonHandler): def update_api_activity(self): """Update last_activity of API requests""" # record activity of authenticated requests - if self._track_activity and getattr(self, '_user_cache', None): + if ( + self._track_activity + and getattr(self, '_user_cache', None) + and self.get_argument('no_track_activity', None) is None + ): self.settings['api_last_activity'] = utcnow() def finish(self, *args, **kwargs): diff --git a/notebook/services/api/tests/test_api.py b/notebook/services/api/tests/test_api.py index 0a48b793e..d09a06032 100644 --- a/notebook/services/api/tests/test_api.py +++ b/notebook/services/api/tests/test_api.py @@ -1,27 +1,27 @@ """Test the basic /api endpoints""" -import requests +from datetime import timedelta -from notebook._tz import isoformat +from notebook._tz import isoformat, utcnow from notebook.utils import url_path_join from notebook.tests.launchnotebook import NotebookTestBase -class KernelAPITest(NotebookTestBase): +class APITest(NotebookTestBase): """Test the kernels web service API""" - + def _req(self, verb, path, **kwargs): r = self.request(verb, url_path_join('api', path)) r.raise_for_status() return r - + def get(self, path, **kwargs): return self._req('GET', path) - + def test_get_spec(self): r = self.get('spec.yaml') assert r.text - + def test_get_status(self): r = self.get('status') data = r.json() @@ -30,3 +30,18 @@ class KernelAPITest(NotebookTestBase): assert data['last_activity'].endswith('Z') assert data['started'].endswith('Z') assert data['started'] == isoformat(self.notebook.web_app.settings['started']) + + def test_no_track_activity(self): + # initialize with old last api activity + old = utcnow() - timedelta(days=1) + settings = self.notebook.web_app.settings + settings['api_last_activity'] = old + # accessing status doesn't update activity + self.get('status') + assert settings['api_last_activity'] == old + # accessing with ?no_track_activity doesn't update activity + self.get('contents?no_track_activity=1') + assert settings['api_last_activity'] == old + # accessing without ?no_track_activity does update activity + self.get('contents') + assert settings['api_last_activity'] > old diff --git a/notebook/services/kernels/kernelmanager.py b/notebook/services/kernels/kernelmanager.py index b072e014b..53ae5d937 100644 --- a/notebook/services/kernels/kernelmanager.py +++ b/notebook/services/kernels/kernelmanager.py @@ -292,7 +292,6 @@ class MappingKernelManager(MultiKernelManager): kernel._activity_stream = None self.stop_buffering(kernel_id) self._kernel_connections.pop(kernel_id, None) - self.last_kernel_activity = utcnow() # Decrease the metric of number of kernels # running for the relevant kernel type by 1 From b341f1217d54c975d62dd06d3d447c0a18562225 Mon Sep 17 00:00:00 2001 From: ednut15 Date: Tue, 25 Jun 2019 10:59:14 +0100 Subject: [PATCH 25/35] #3996 Removed role button attribute from Trusted notificaton as it is not clickable. --- notebook/static/notebook/js/notificationarea.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/notebook/static/notebook/js/notificationarea.js b/notebook/static/notebook/js/notificationarea.js index c515b5ad5..2a6cc7034 100644 --- a/notebook/static/notebook/js/notificationarea.js +++ b/notebook/static/notebook/js/notificationarea.js @@ -398,15 +398,14 @@ define([ return false; }, {'title':'Javascript enabled for notebook display'}); // don't allow 'Trusted' button to be clicked - $(tnw.selector).attr('disabled', true) - $(tnw.selector).attr('role', 'button') + $(tnw.selector).attr('disabled', true); $(tnw.selector).css('cursor', 'help'); } else { tnw.set_message(i18n.msg._("Not Trusted"), undefined, function() { that.notebook.trust_notebook(); return false; }, {'title':'Javascript disabled for notebook display'}); - $(tnw.selector).attr('role', 'button') + $(tnw.selector).attr('role', 'button'); } }); }; From 9588b98ec2eec73d4cd7f3df1a03c895bb913570 Mon Sep 17 00:00:00 2001 From: Joshua Zeltser Date: Tue, 25 Jun 2019 11:49:01 +0100 Subject: [PATCH 26/35] Added trans tags to the "Toggle Dropdown" span --- notebook/templates/tree.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook/templates/tree.html b/notebook/templates/tree.html index 3b5169675..48ff92cbf 100644 --- a/notebook/templates/tree.html +++ b/notebook/templates/tree.html @@ -65,7 +65,7 @@ data-server-root="{{server_root}}"