diff -Nru jupyter-notebook-6.1.6/debian/changelog jupyter-notebook-6.2.0/debian/changelog --- jupyter-notebook-6.1.6/debian/changelog 2021-01-16 01:33:23.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/changelog 2021-01-17 21:52:31.000000000 +0000 @@ -1,11 +1,11 @@ -jupyter-notebook (6.1.6-2ubuntu1) hirsute; urgency=low +jupyter-notebook (6.2.0-1) unstable; urgency=medium - * Merge from Debian unstable. Remaining changes: - - Handle TrashPermissionError, which gets thrown in autopkgtests. - - Switch back to node-term.js AGAIN. node-xterm was removed from Ubuntu - due to FTBFS. + * New upstream version 6.2.0 + * Version dependencies on tornado, send2trash + * Disable all tests of notebook trashing; these are too sensitive to + different mount and container layouts to be useful. - -- Stefano Rivera Fri, 15 Jan 2021 18:33:23 -0700 + -- Gordon Ball Sun, 17 Jan 2021 21:52:31 +0000 jupyter-notebook (6.1.6-2) unstable; urgency=medium @@ -20,14 +20,6 @@ -- Gordon Ball Sat, 26 Dec 2020 20:18:26 +0000 -jupyter-notebook (6.1.5-1ubuntu1) hirsute; urgency=medium - - * Handle TrashPermissionError, which gets thrown in autopkgtests. - * Switch back to node-term.js AGAIN. node-xterm was removed from Ubuntu due - to FTBFS. - - -- Stefano Rivera Tue, 01 Dec 2020 12:11:29 -0800 - jupyter-notebook (6.1.5-1) unstable; urgency=medium * New upstream version 6.1.5 @@ -322,4 +314,3 @@ * Initial release (closes: #801366). -- Gordon Ball Fri, 04 Nov 2016 14:54:21 +0100 - diff -Nru jupyter-notebook-6.1.6/debian/control jupyter-notebook-6.2.0/debian/control --- jupyter-notebook-6.1.6/debian/control 2021-01-13 03:18:38.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/control 2021-01-17 21:52:31.000000000 +0000 @@ -18,7 +18,7 @@ python3-ipython , python3-jupyter-core (>= 4.6.1) , python3-jupyter-client (>= 5.3.4) , - python3-tornado , + python3-tornado (>= 6.1) , python3-nbformat (>= 4.4) , python3-nbconvert (>= 5) , python3-ipykernel , @@ -26,7 +26,7 @@ python3-pytest , python3-terminado (>= 0.8.3) , python3-entrypoints , - python3-send2trash , + python3-send2trash (>= 1.5) , python3-zmq , python3-sphinx , python3-sphinx-rtd-theme , @@ -48,8 +48,7 @@ libjs-text-encoding (>= 0.1), libjs-underscore (>= 1.5), libjs-jquery-typeahead (>= 2.0), -# libjs-xterm (>= 3.8.1-3~), - libjs-term.js, + libjs-xterm (>= 3.8.1-3~), nodejs, node-less (>= 1.5), node-source-map, @@ -87,8 +86,7 @@ libjs-text-encoding (>= 0.1), libjs-underscore (>= 1.5), libjs-jquery-typeahead (>= 2.0), -# libjs-xterm (>= 3.8.1-3~), - libjs-term.js, + libjs-xterm (>= 3.8.1-3~), Recommends: python3-ipywidgets, python3-ipykernel Suggests: python-notebook-doc diff -Nru jupyter-notebook-6.1.6/debian/patches/0010-xterm-legacy.patch jupyter-notebook-6.2.0/debian/patches/0010-xterm-legacy.patch --- jupyter-notebook-6.1.6/debian/patches/0010-xterm-legacy.patch 2020-12-01 20:11:29.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/patches/0010-xterm-legacy.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -From: Gordon Ball -Date: Tue, 24 Sep 2019 20:37:20 +0000 -Subject: compatibility changes for an older version of xterm - ---- - notebook/static/terminal/js/terminado.js | 9 ++------- - 1 file changed, 2 insertions(+), 7 deletions(-) - -diff --git a/notebook/static/terminal/js/terminado.js b/notebook/static/terminal/js/terminado.js -index 3e52d11..0ffc59b 100644 ---- a/notebook/static/terminal/js/terminado.js -+++ b/notebook/static/terminal/js/terminado.js -@@ -1,9 +1,8 @@ --define (["xterm", "xtermjs-fit"], function(Terminal, fit) { -+define (["xterm"], function(Terminal) { - "use strict"; - function make_terminal(element, ws_url) { - var ws = new WebSocket(ws_url); -- Terminal.applyAddon(fit); -- var term = new Terminal(); -+ var term = new Terminal({cols: 80, rows: 24}); - ws.onopen = function(event) { - term.on('data', function(data) { - ws.send(JSON.stringify(['stdin', data])); -@@ -14,10 +13,6 @@ define (["xterm", "xtermjs-fit"], function(Terminal, fit) { - }); - - term.open(element); -- term.fit(); -- // send the terminal size to the server. -- ws.send(JSON.stringify(["set_size", term.rows, term.cols, -- window.innerHeight, window.innerWidth])); - - ws.onmessage = function(event) { - var json_msg = JSON.parse(event.data); diff -Nru jupyter-notebook-6.1.6/debian/patches/0011-Requirejs-shim-config-for-xterm-xterm-addon-fit.patch jupyter-notebook-6.2.0/debian/patches/0011-Requirejs-shim-config-for-xterm-xterm-addon-fit.patch --- jupyter-notebook-6.1.6/debian/patches/0011-Requirejs-shim-config-for-xterm-xterm-addon-fit.patch 2021-01-13 03:18:39.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/patches/0011-Requirejs-shim-config-for-xterm-xterm-addon-fit.patch 2021-01-17 21:52:31.000000000 +0000 @@ -11,10 +11,10 @@ 1 file changed, 6 insertions(+) diff --git a/tools/build-main.js b/tools/build-main.js -index d62924f..1c592b5 100644 +index ba7bbdc..10a1b2c 100644 --- a/tools/build-main.js +++ b/tools/build-main.js -@@ -53,6 +53,12 @@ var rjs_config = { +@@ -55,6 +55,12 @@ var rjs_config = { "jquery-ui": { deps: ["jquery"], }, diff -Nru jupyter-notebook-6.1.6/debian/patches/0012-Skip-all-send2trash-tests.patch jupyter-notebook-6.2.0/debian/patches/0012-Skip-all-send2trash-tests.patch --- jupyter-notebook-6.1.6/debian/patches/0012-Skip-all-send2trash-tests.patch 1970-01-01 00:00:00.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/patches/0012-Skip-all-send2trash-tests.patch 2021-01-17 21:52:31.000000000 +0000 @@ -0,0 +1,92 @@ +From: Gordon Ball +Date: Thu, 14 Jan 2021 15:56:57 +0000 +Subject: Skip all send2trash tests + +send2trash fails in various unpredictable ways in container +environments, overlays and bind mounts. This leads to lots of issues +with different build and CI environments. +--- + notebook/services/contents/tests/test_contents_api.py | 5 +++++ + notebook/services/contents/tests/test_manager.py | 4 ++++ + 2 files changed, 9 insertions(+) + +diff --git a/notebook/services/contents/tests/test_contents_api.py b/notebook/services/contents/tests/test_contents_api.py +index 6e4ad49..7f0e01f 100644 +--- a/notebook/services/contents/tests/test_contents_api.py ++++ b/notebook/services/contents/tests/test_contents_api.py +@@ -11,6 +11,7 @@ from unicodedata import normalize + + pjoin = os.path.join + ++import pytest + import requests + from send2trash import send2trash + from send2trash.exceptions import TrashPermissionError +@@ -510,6 +511,7 @@ class APITest(NotebookTestBase): + with assert_http_error(400): + resp = self.api.copy(u'å b', u'foo') + ++ @pytest.mark.skip("skip send2trash tests") + def test_delete(self): + for d, name in self.dirs_nbs: + print('%r, %r' % (d, name)) +@@ -523,6 +525,7 @@ class APITest(NotebookTestBase): + print(nbs) + self.assertEqual(nbs, []) + ++ @pytest.mark.skip("skip send2trash tests") + def test_delete_dirs(self): + # depth-first delete everything, so we don't try to delete empty directories + for name in sorted(self.dirs + ['/'], key=len, reverse=True): +@@ -532,6 +535,7 @@ class APITest(NotebookTestBase): + listing = self.api.list('/').json()['content'] + self.assertEqual(listing, []) + ++ @pytest.mark.skip("skip send2trash tests") + def test_delete_non_empty_dir(self): + if sys.platform == 'win32': + self.skipTest("Disabled deleting non-empty dirs on Windows") +@@ -559,6 +563,7 @@ class APITest(NotebookTestBase): + self.assertIn('z.ipynb', nbnames) + self.assertNotIn('a.ipynb', nbnames) + ++ @pytest.mark.skip("skip send2trash tests") + def test_checkpoints_follow_file(self): + + # Read initial file state +diff --git a/notebook/services/contents/tests/test_manager.py b/notebook/services/contents/tests/test_manager.py +index dfe5d27..bb854eb 100644 +--- a/notebook/services/contents/tests/test_manager.py ++++ b/notebook/services/contents/tests/test_manager.py +@@ -6,6 +6,7 @@ import time + from contextlib import contextmanager + from itertools import combinations + ++import pytest + from tornado.web import HTTPError + from unittest import TestCase, skipIf + from tempfile import NamedTemporaryFile +@@ -181,6 +182,7 @@ class TestFileContentsManager(TestCase): + else: + self.fail("Should have raised HTTPError(403)") + ++ @pytest.mark.skip("skip send2trash tests") + def test_escape_root(self): + with TemporaryDirectory() as td: + cm = FileContentsManager(root_dir=td) +@@ -506,6 +508,7 @@ class TestContentsManager(TestCase): + self.assertEqual(model['name'], 'Untitled.ipynb') + self.assertEqual(model['path'], 'foo/Untitled.ipynb') + ++ @pytest.mark.skip("skip send2trash tests") + def test_delete(self): + cm = self.contents_manager + # Create a notebook +@@ -574,6 +577,7 @@ class TestContentsManager(TestCase): + # Created a notebook in the renamed directory should work + cm.new_untitled("foo/bar_diff", ext=".ipynb") + ++ @pytest.mark.skip("skip send2trash tests") + def test_delete_root(self): + cm = self.contents_manager + with self.assertRaises(HTTPError) as err: diff -Nru jupyter-notebook-6.1.6/debian/patches/0012-use-term.js.patch jupyter-notebook-6.2.0/debian/patches/0012-use-term.js.patch --- jupyter-notebook-6.1.6/debian/patches/0012-use-term.js.patch 2020-12-01 20:11:29.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/patches/0012-use-term.js.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -From: Debian Python Modules Team - -Date: Tue, 24 Sep 2019 20:37:20 +0000 -Subject: use-term.js - ---- - bower.json | 4 +--- - notebook/static/terminal/js/main.js | 2 +- - notebook/static/terminal/js/terminado.js | 2 +- - notebook/templates/page.html | 2 +- - notebook/templates/terminal.html | 1 - - setupbase.py | 4 +--- - tools/build-main.js | 3 +-- - 7 files changed, 6 insertions(+), 12 deletions(-) - -diff --git a/bower.json b/bower.json -index 5f72f6f..aa780d5 100644 ---- a/bower.json -+++ b/bower.json -@@ -23,8 +23,6 @@ - "requirejs-plugins": "~1.0.3", - "text-encoding": "~0.1", - "underscore": "components/underscore#~1.8.3", -- "xterm.js": "https://unpkg.com/xterm@~3.1.0/dist/xterm.js", -- "xterm.js-css": "https://unpkg.com/xterm@~3.1.0/dist/xterm.css", -- "xterm.js-fit": "https://unpkg.com/xterm@~3.1.0/dist/addons/fit/fit.js" -+ "term.js": "chjj/term.js#~0.0.7" - } - } -diff --git a/notebook/static/terminal/js/main.js b/notebook/static/terminal/js/main.js -index 03ea2ab..c55e3d4 100644 ---- a/notebook/static/terminal/js/main.js -+++ b/notebook/static/terminal/js/main.js -@@ -14,7 +14,7 @@ requirejs([ - page, - loginwidget, - configmod, -- terminado -+ terminado, - ){ - "use strict"; - requirejs(['custom/custom'], function() {}); -diff --git a/notebook/static/terminal/js/terminado.js b/notebook/static/terminal/js/terminado.js -index 0ffc59b..7a4971d 100644 ---- a/notebook/static/terminal/js/terminado.js -+++ b/notebook/static/terminal/js/terminado.js -@@ -1,4 +1,4 @@ --define (["xterm"], function(Terminal) { -+define (['termjs'], function() { - "use strict"; - function make_terminal(element, ws_url) { - var ws = new WebSocket(ws_url); -diff --git a/notebook/templates/page.html b/notebook/templates/page.html -index 30a1555..83df774 100644 ---- a/notebook/templates/page.html -+++ b/notebook/templates/page.html -@@ -42,7 +42,7 @@ - 'jquery-ui': 'components/jquery-ui/jquery-ui.min', - moment: 'components/moment/min/moment-with-locales', - codemirror: 'components/codemirror', -- termjs: 'components/xterm.js/xterm', -+ termjs: 'components/term.js/src/term', - typeahead: 'components/jquery-typeahead/dist/jquery.typeahead.min', - }, - map: { // for backward compatibility -diff --git a/notebook/templates/terminal.html b/notebook/templates/terminal.html -index b61c13b..acdafa3 100644 ---- a/notebook/templates/terminal.html -+++ b/notebook/templates/terminal.html -@@ -15,7 +15,6 @@ data-ws-path="{{ws_path}}" - {% block stylesheet %} - {{super()}} - -- - {% endblock %} - - {% block headercontainer %} -diff --git a/setupbase.py b/setupbase.py -index d81c1d1..aae446d 100644 ---- a/setupbase.py -+++ b/setupbase.py -@@ -157,10 +157,8 @@ def find_package_data(): - pjoin(components, "underscore", "underscore-min.js"), - pjoin(components, "moment", "moment.js"), - pjoin(components, "moment", "min", "*.js"), -- pjoin(components, "xterm.js", "index.js"), -- pjoin(components, "xterm.js-css", "index.css"), -- pjoin(components, "xterm.js-fit", "index.js"), - pjoin(components, "text-encoding", "lib", "encoding.js"), -+ pjoin(components, "term.js" , "term.js"), - ]) - - # Ship all of Codemirror's CSS and JS -diff --git a/tools/build-main.js b/tools/build-main.js -index ba7bbdc..a9fdfb9 100644 ---- a/tools/build-main.js -+++ b/tools/build-main.js -@@ -24,8 +24,7 @@ var rjs_config = { - "jquery-ui": 'components/jquery-ui/jquery-ui.min', - moment: 'components/moment/min/moment-with-locales', - codemirror: 'components/codemirror', -- xterm: 'components/xterm.js/index', -- "xtermjs-fit": 'components/xterm.js-fit/index', -+ termjs: 'components/term.js/term', - "jquery-typeahead": 'components/jquery-typeahead/dist/jquery.typeahead.min', - contents: 'empty:', - custom: 'empty:', diff -Nru jupyter-notebook-6.1.6/debian/patches/0014-Handle-TrashPermissionError.patch jupyter-notebook-6.2.0/debian/patches/0014-Handle-TrashPermissionError.patch --- jupyter-notebook-6.1.6/debian/patches/0014-Handle-TrashPermissionError.patch 2020-12-01 20:11:29.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/patches/0014-Handle-TrashPermissionError.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ -From: Stefano Rivera -Date: Tue, 1 Dec 2020 15:06:27 -0800 -Subject: Handle TrashPermissionError, now that it exists - -Forwarded: https://github.com/jupyter/notebook/pull/5894 - -Raised in autopkgtests on tmpfs. ---- - notebook/services/contents/filemanager.py | 22 ++++------------------ - .../services/contents/tests/test_contents_api.py | 18 +++++++++++++++++- - setup.py | 2 +- - 3 files changed, 22 insertions(+), 20 deletions(-) - -diff --git a/notebook/services/contents/filemanager.py b/notebook/services/contents/filemanager.py -index 3fa6dad..0c9386b 100644 ---- a/notebook/services/contents/filemanager.py -+++ b/notebook/services/contents/filemanager.py -@@ -15,6 +15,7 @@ import mimetypes - import nbformat - - from send2trash import send2trash -+from send2trash.exceptions import TrashPermissionError - from tornado import web - - from .filecheckpoints import FileCheckpoints -@@ -512,17 +513,6 @@ class FileContentsManager(FileManagerMixin, ContentsManager): - if not os.path.exists(os_path): - raise web.HTTPError(404, u'File or directory does not exist: %s' % os_path) - -- def _check_trash(os_path): -- if sys.platform in {'win32', 'darwin'}: -- return True -- -- # It's a bit more nuanced than this, but until we can better -- # distinguish errors from send2trash, assume that we can only trash -- # files on the same partition as the home directory. -- file_dev = os.stat(os_path).st_dev -- home_dev = os.stat(os.path.expanduser('~')).st_dev -- return file_dev == home_dev -- - def is_non_empty_dir(os_path): - if os.path.isdir(os_path): - # A directory containing only leftover checkpoints is -@@ -538,16 +528,12 @@ class FileContentsManager(FileManagerMixin, ContentsManager): - # send2trash can really delete files on Windows, so disallow - # deleting non-empty files. See Github issue 3631. - raise web.HTTPError(400, u'Directory %s not empty' % os_path) -- if _check_trash(os_path): -+ try: - self.log.debug("Sending %s to trash", os_path) -- # Looking at the code in send2trash, I don't think the errors it -- # raises let us distinguish permission errors from other errors in -- # code. So for now, just let them all get logged as server errors. - send2trash(os_path) - return -- else: -- self.log.warning("Skipping trash for %s, on different device " -- "to home directory", os_path) -+ except TrashPermissionError as e: -+ self.log.warning("Skipping trash for %s, %s", os_path, e) - - if os.path.isdir(os_path): - # Don't permanently delete non-empty directories. -diff --git a/notebook/services/contents/tests/test_contents_api.py b/notebook/services/contents/tests/test_contents_api.py -index 5502e67..4111f38 100644 ---- a/notebook/services/contents/tests/test_contents_api.py -+++ b/notebook/services/contents/tests/test_contents_api.py -@@ -13,6 +13,8 @@ from unicodedata import normalize - pjoin = os.path.join - - import requests -+from send2trash import send2trash -+from send2trash.exceptions import TrashPermissionError - - from ..filecheckpoints import GenericFileCheckpoints - -@@ -198,6 +200,14 @@ class APITest(NotebookTestBase): - def isdir(self, api_path): - return os.path.isdir(self.to_os_path(api_path)) - -+ def can_send2trash(self, api_path): -+ """Send a path to trash, if possible. Return success.""" -+ try: -+ send2trash(self.to_os_path(api_path)) -+ return True -+ except TrashPermissionError as e: -+ return False -+ - def setUp(self): - for d in (self.dirs + self.hidden_dirs): - self.make_dir(d) -@@ -527,7 +537,13 @@ class APITest(NotebookTestBase): - if sys.platform == 'win32': - self.skipTest("Disabled deleting non-empty dirs on Windows") - # Test that non empty directory can be deleted -- self.api.delete(u'å b') -+ try: -+ self.api.delete(u'å b') -+ except requests.HTTPError as e: -+ if e.response.status_code == 400: -+ if not self.can_send2trash(u'å b'): -+ self.skipTest("Dir can't be sent to trash") -+ raise - # Check if directory has actually been deleted - with assert_http_error(404): - self.api.list(u'å b') -diff --git a/setup.py b/setup.py -index 95626b2..9a222c5 100755 ---- a/setup.py -+++ b/setup.py -@@ -111,7 +111,7 @@ for more information. - 'nbformat', - 'nbconvert', - 'ipykernel', # bless IPython kernel for now -- 'Send2Trash', -+ 'Send2Trash>=1.5.0', - 'terminado>=0.8.3', - 'prometheus_client' - ], diff -Nru jupyter-notebook-6.1.6/debian/patches/series jupyter-notebook-6.2.0/debian/patches/series --- jupyter-notebook-6.1.6/debian/patches/series 2021-01-13 03:18:39.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/patches/series 2021-01-17 21:52:31.000000000 +0000 @@ -6,9 +6,7 @@ 0006-Debian-specific-hack-to-fix-upstream-s-non-increment.patch 0007-Ignore-errors-in-documentation-notebooks-during-buil.patch 0009-Don-t-try-and-patch-bootstrap-less.patch -0010-xterm-legacy.patch -0012-use-term.js.patch 0013-documentation-fixes.patch 0013-Avoid-manipulating-PYTHONPATH-in-tests.patch -#0011-Requirejs-shim-config-for-xterm-xterm-addon-fit.patch -0014-Handle-TrashPermissionError.patch +0011-Requirejs-shim-config-for-xterm-xterm-addon-fit.patch +0012-Skip-all-send2trash-tests.patch diff -Nru jupyter-notebook-6.1.6/debian/rules jupyter-notebook-6.2.0/debian/rules --- jupyter-notebook-6.1.6/debian/rules 2021-01-13 03:18:38.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/rules 2021-01-17 21:52:31.000000000 +0000 @@ -81,12 +81,10 @@ ln -s $(JSBASE)/requirejs/text.js $(COMP)/requirejs-text/ #xterm.js - #mkdir -p $(COMP)/xterm.js $(COMP)/xterm.js-css $(COMP)/xterm.js-fit - #ln -s $(JSBASE)/xterm/xterm.js $(COMP)/xterm.js/index.js - #ln -s $(JSBASE)/xterm/xterm.css $(COMP)/xterm.js-css/index.css - #ln -s $(JSBASE)/xterm/addons/fit/fit.js $(COMP)/xterm.js-fit/index.js - mkdir -p $(COMP)/term.js - ln -s $(JSBASE)/term.js/term.js $(COMP)/term.js/term.js + mkdir -p $(COMP)/xterm.js $(COMP)/xterm.js-css $(COMP)/xterm.js-fit + ln -s $(JSBASE)/xterm/xterm.js $(COMP)/xterm.js/index.js + ln -s $(JSBASE)/xterm/xterm.css $(COMP)/xterm.js-css/index.css + ln -s $(JSBASE)/xterm/addons/fit/fit.js $(COMP)/xterm.js-fit/index.js #text-encoding mkdir -p $(COMP)/text-encoding/lib @@ -142,12 +140,6 @@ echo "js:Built-Using=$(BUILTUSING)" >> debian/python3-notebook.substvars dh_gencontrol -override_dh_auto_test: - mkdir -p debian/runtime/home debian/runtime/tmpdir debian/runtime/xdg - # send2trash deletion tests fail if the temporary notebook files - # are on a different block device to the implicit trash directory in HOME - TMPDIR=$(CURDIR)/debian/runtime/tmpdir XDG_RUNTIME_DIR=$(CURDIR)/debian/runtime/xdg HOME=$(CURDIR)/debian/runtime/home dh_auto_test - override_dh_installchangelogs: dh_installchangelogs -k docs/source/changelog.rst diff -Nru jupyter-notebook-6.1.6/debian/tests/pytest jupyter-notebook-6.2.0/debian/tests/pytest --- jupyter-notebook-6.1.6/debian/tests/pytest 2021-01-12 18:52:17.000000000 +0000 +++ jupyter-notebook-6.2.0/debian/tests/pytest 2021-01-17 21:52:31.000000000 +0000 @@ -1,19 +1,8 @@ #!/bin/sh -e -for d in tmp home/runtime home/data/trash; do - mkdir -p $AUTOPKGTEST_TMP/$d -done - -# try and ensure files are on the same device, since otherwise -# deletion-related tests fail attempting a cross-device rename -# by default, debci seems to place /tmp and $AUTOPKGTEST_TMP on -# different devices +export XDG_RUNTIME_DIR=$AUTOPKGTEST_TMP/runtime export HOME=$AUTOPKGTEST_TMP/home -export XDG_RUNTIME_DIR=$AUTOPKGTEST_TMP/home/runtime -export XDG_DATA_HOME=$AUTOPKGTEST_TMP/home/data -export TMPDIR=$AUTOPKGTEST_TMP/tmp -export NOSE_IGNORE_CONFIG_FILES=1 - +mkdir -p $XDG_RUNTIME_DIR $HOME chmod 0700 $XDG_RUNTIME_DIR python3 -m pytest --ignore=notebook/tests/selenium diff -Nru jupyter-notebook-6.1.6/docs/source/changelog.rst jupyter-notebook-6.2.0/docs/source/changelog.rst --- jupyter-notebook-6.1.6/docs/source/changelog.rst 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/docs/source/changelog.rst 2021-01-13 16:30:49.000000000 +0000 @@ -22,6 +22,50 @@ ``pip --version``. + +.. _release-6.2.0: + +6.2.0 +----- + +Merged PRs +~~~~~~~~~~ + +- Increase minimum tornado version (:ghpull:`5933`) +- Adjust skip decorators to avoid remaining dependency on nose (:ghpull:`5932`) +- Ensure that cell ids persist after save (:ghpull:`5928`) +- Add reconnection to Gateway (form nb2kg) (:ghpull:`5924`) +- Fix some typos (:ghpull:`5917`) +- Handle TrashPermissionError, now that it exists (:ghpull:`5894`) + +Thank you to all the contributors: + +- @kevin-bates +- @mishaschwartz +- @oyvsyo +- @user202729 +- @stefanor + +.. _release-6.1.6: + +6.1.6 +----- + +Merged PRs +~~~~~~~~~~ + +* do not require nose for testing (:ghpull:`5826`) +* [docs] Update Chinese and Hindi readme.md (:ghpull:`5823`) +* Add support for creating terminals via GET (:ghpull:`5813`) +* Made doc translations in Hindi and Chinese (:ghpull:`5787`) + +Thank you to all the contributors: + +- @pgajdos +- @rjn01 +- @kevin-bates +- @virejdasani + .. _release-6.1.5: 6.1.5 diff -Nru jupyter-notebook-6.1.6/notebook/bundler/tests/test_bundler_api.py jupyter-notebook-6.2.0/notebook/bundler/tests/test_bundler_api.py --- jupyter-notebook-6.1.6/notebook/bundler/tests/test_bundler_api.py 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/bundler/tests/test_bundler_api.py 2021-01-13 16:30:49.000000000 +0000 @@ -46,7 +46,7 @@ self.assertIn('Missing argument bundler', resp.text) def test_notebook_not_found(self): - """Shoudl respond with 404 error about missing notebook""" + """Should respond with 404 error about missing notebook""" resp = self.request('GET', 'bundle/fake.ipynb', params={'bundler': 'fake_bundler'}) self.assertEqual(resp.status_code, 404) diff -Nru jupyter-notebook-6.1.6/notebook/gateway/handlers.py jupyter-notebook-6.2.0/notebook/gateway/handlers.py --- jupyter-notebook-6.1.6/notebook/gateway/handlers.py 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/gateway/handlers.py 2021-01-13 16:30:49.000000000 +0000 @@ -4,6 +4,7 @@ import os import logging import mimetypes +import random from ..base.handlers import APIHandler, IPythonHandler from ..utils import url_path_join @@ -134,6 +135,7 @@ self.ws = None self.ws_future = Future() self.disconnected = False + self.retry = 0 @gen.coroutine def _connect(self, kernel_id): @@ -155,6 +157,7 @@ def _connection_done(self, fut): if not self.disconnected and fut.exception() is None: # prevent concurrent.futures._base.CancelledError self.ws = fut.result() + self.retry = 0 self.log.debug("Connection is ready: ws: {}".format(self.ws)) else: self.log.warning("Websocket connection has been closed via client disconnect or due to error. " @@ -189,8 +192,15 @@ else: # ws cancelled - stop reading break - if not self.disconnected: # if websocket is not disconnected by client, attept to reconnect to Gateway - self.log.info("Attempting to re-establish the connection to Gateway: {}".format(self.kernel_id)) + # NOTE(esevan): if websocket is not disconnected by client, try to reconnect. + if not self.disconnected and self.retry < GatewayClient.instance().gateway_retry_max: + jitter = random.randint(10, 100) * 0.01 + retry_interval = min(GatewayClient.instance().gateway_retry_interval * (2 ** self.retry), + GatewayClient.instance().gateway_retry_interval_max) + jitter + self.retry += 1 + self.log.info("Attempting to re-establish the connection to Gateway in %s secs (%s/%s): %s", + retry_interval, self.retry, GatewayClient.instance().gateway_retry_max, self.kernel_id) + yield gen.sleep(retry_interval) self._connect(self.kernel_id) loop = IOLoop.current() loop.add_future(self.ws_future, lambda future: self._read_messages(callback)) diff -Nru jupyter-notebook-6.1.6/notebook/gateway/managers.py jupyter-notebook-6.2.0/notebook/gateway/managers.py --- jupyter-notebook-6.1.6/notebook/gateway/managers.py 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/gateway/managers.py 2021-01-13 16:30:49.000000000 +0000 @@ -15,14 +15,14 @@ from jupyter_client.kernelspec import KernelSpecManager from ..utils import url_path_join -from traitlets import Instance, Unicode, Float, Bool, default, validate, TraitError +from traitlets import Instance, Unicode, Int, Float, Bool, default, validate, TraitError from traitlets.config import SingletonConfigurable class GatewayClient(SingletonConfigurable): """This class manages the configuration. It's its own singleton class so that we can share these values across all objects. It also contains some helper methods - to build request arguments out of the various config options. + to build request arguments out of the various config options. """ @@ -220,6 +220,38 @@ def _env_whitelist_default(self): return os.environ.get(self.env_whitelist_env, self.env_whitelist_default_value) + gateway_retry_interval_default_value = 1.0 + gateway_retry_interval_env = 'JUPYTER_GATEWAY_RETRY_INTERVAL' + gateway_retry_interval = Float(default_value=gateway_retry_interval_default_value, config=True, + help="""The time allowed for HTTP reconnection with the Gateway server for the first time. + Next will be JUPYTER_GATEWAY_RETRY_INTERVAL multiplied by two in factor of numbers of retries + but less than JUPYTER_GATEWAY_RETRY_INTERVAL_MAX. + (JUPYTER_GATEWAY_RETRY_INTERVAL env var)""") + + @default('gateway_retry_interval') + def gateway_retry_interval_default(self): + return float(os.environ.get('JUPYTER_GATEWAY_RETRY_INTERVAL', self.gateway_retry_interval_default_value)) + + gateway_retry_interval_max_default_value = 30.0 + gateway_retry_interval_max_env = 'JUPYTER_GATEWAY_RETRY_INTERVAL_MAX' + gateway_retry_interval_max = Float(default_value=gateway_retry_interval_max_default_value, config=True, + help="""The maximum time allowed for HTTP reconnection retry with the Gateway server. + (JUPYTER_GATEWAY_RETRY_INTERVAL_MAX env var)""") + + @default('gateway_retry_interval_max') + def gateway_retry_interval_max_default(self): + return float(os.environ.get('JUPYTER_GATEWAY_RETRY_INTERVAL_MAX', self.gateway_retry_interval_max_default_value)) + + gateway_retry_max_default_value = 5 + gateway_retry_max_env = 'JUPYTER_GATEWAY_RETRY_MAX' + gateway_retry_max = Int(default_value=gateway_retry_max_default_value, config=True, + help="""The maximum retries allowed for HTTP reconnection with the Gateway server. + (JUPYTER_GATEWAY_RETRY_MAX env var)""") + + @default('gateway_retry_max') + def gateway_retry_max_default(self): + return int(os.environ.get('JUPYTER_GATEWAY_RETRY_MAX', self.gateway_retry_max_default_value)) + @property def gateway_enabled(self): return bool(self.url is not None and len(self.url) > 0) @@ -503,7 +535,6 @@ self.remove_kernel(kernel_id) - class GatewayKernelSpecManager(KernelSpecManager): def __init__(self, **kwargs): diff -Nru jupyter-notebook-6.1.6/notebook/services/contents/filemanager.py jupyter-notebook-6.2.0/notebook/services/contents/filemanager.py --- jupyter-notebook-6.1.6/notebook/services/contents/filemanager.py 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/services/contents/filemanager.py 2021-01-13 16:30:49.000000000 +0000 @@ -15,6 +15,7 @@ import nbformat from send2trash import send2trash +from send2trash.exceptions import TrashPermissionError from tornado import web from .filecheckpoints import FileCheckpoints @@ -512,17 +513,6 @@ if not os.path.exists(os_path): raise web.HTTPError(404, u'File or directory does not exist: %s' % os_path) - def _check_trash(os_path): - if sys.platform in {'win32', 'darwin'}: - return True - - # It's a bit more nuanced than this, but until we can better - # distinguish errors from send2trash, assume that we can only trash - # files on the same partition as the home directory. - file_dev = os.stat(os_path).st_dev - home_dev = os.stat(os.path.expanduser('~')).st_dev - return file_dev == home_dev - def is_non_empty_dir(os_path): if os.path.isdir(os_path): # A directory containing only leftover checkpoints is @@ -538,16 +528,12 @@ # send2trash can really delete files on Windows, so disallow # deleting non-empty files. See Github issue 3631. raise web.HTTPError(400, u'Directory %s not empty' % os_path) - if _check_trash(os_path): + try: self.log.debug("Sending %s to trash", os_path) - # Looking at the code in send2trash, I don't think the errors it - # raises let us distinguish permission errors from other errors in - # code. So for now, just let them all get logged as server errors. send2trash(os_path) return - else: - self.log.warning("Skipping trash for %s, on different device " - "to home directory", os_path) + except TrashPermissionError as e: + self.log.warning("Skipping trash for %s, %s", os_path, e) if os.path.isdir(os_path): # Don't permanently delete non-empty directories. diff -Nru jupyter-notebook-6.1.6/notebook/services/contents/tests/test_contents_api.py jupyter-notebook-6.2.0/notebook/services/contents/tests/test_contents_api.py --- jupyter-notebook-6.1.6/notebook/services/contents/tests/test_contents_api.py 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/services/contents/tests/test_contents_api.py 2021-01-13 16:30:49.000000000 +0000 @@ -12,6 +12,8 @@ pjoin = os.path.join import requests +from send2trash import send2trash +from send2trash.exceptions import TrashPermissionError from ..filecheckpoints import GenericFileCheckpoints @@ -197,6 +199,14 @@ def isdir(self, api_path): return os.path.isdir(self.to_os_path(api_path)) + def can_send2trash(self, api_path): + """Send a path to trash, if possible. Return success.""" + try: + send2trash(self.to_os_path(api_path)) + return True + except TrashPermissionError as e: + return False + def setUp(self): for d in (self.dirs + self.hidden_dirs): self.make_dir(d) @@ -526,7 +536,13 @@ if sys.platform == 'win32': self.skipTest("Disabled deleting non-empty dirs on Windows") # Test that non empty directory can be deleted - self.api.delete(u'å b') + try: + self.api.delete(u'å b') + except requests.HTTPError as e: + if e.response.status_code == 400: + if not self.can_send2trash(u'å b'): + self.skipTest("Dir can't be sent to trash") + raise # Check if directory has actually been deleted with assert_http_error(404): self.api.list(u'å b') diff -Nru jupyter-notebook-6.1.6/notebook/services/kernels/kernelmanager.py jupyter-notebook-6.2.0/notebook/services/kernels/kernelmanager.py --- jupyter-notebook-6.1.6/notebook/services/kernels/kernelmanager.py 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/services/kernels/kernelmanager.py 2021-01-13 16:30:49.000000000 +0000 @@ -294,7 +294,6 @@ kernel._activity_stream.close() kernel._activity_stream = None self.stop_buffering(kernel_id) - self._kernel_connections.pop(kernel_id, None) # Decrease the metric of number of kernels # running for the relevant kernel type by 1 @@ -302,7 +301,12 @@ type=self._kernels[kernel_id].kernel_name ).dec() - return self.pinned_superclass.shutdown_kernel(self, kernel_id, now=now, restart=restart) + self.pinned_superclass.shutdown_kernel(self, kernel_id, now=now, restart=restart) + # Unlike its async sibling method in AsyncMappingKernelManager, removing the kernel_id + # from the connections dictionary isn't as problematic before the shutdown since the + # method is synchronous. However, we'll keep the relative call orders the same from + # a maintenance perspective. + self._kernel_connections.pop(kernel_id, None) async def restart_kernel(self, kernel_id, now=False): """Restart a kernel by kernel_id""" @@ -376,8 +380,11 @@ kernels = [] kernel_ids = self.pinned_superclass.list_kernel_ids(self) for kernel_id in kernel_ids: - model = self.kernel_model(kernel_id) - kernels.append(model) + try: + model = self.kernel_model(kernel_id) + kernels.append(model) + except (web.HTTPError, KeyError): + pass # Probably due to a (now) non-existent kernel, continue building the list return kernels # override _check_kernel_id to raise 404 instead of KeyError @@ -498,7 +505,6 @@ kernel._activity_stream.close() kernel._activity_stream = None self.stop_buffering(kernel_id) - self._kernel_connections.pop(kernel_id, None) # Decrease the metric of number of kernels # running for the relevant kernel type by 1 @@ -506,4 +512,9 @@ type=self._kernels[kernel_id].kernel_name ).dec() - return await self.pinned_superclass.shutdown_kernel(self, kernel_id, now=now, restart=restart) + await self.pinned_superclass.shutdown_kernel(self, kernel_id, now=now, restart=restart) + # Remove kernel_id from the connections dictionary only after kernel has been shutdown, + # otherwise a race condition can occur since the shutdown may take a while - allowing + # list/fetch kernel operations to access _kernel_connections for a non-existent key + # (kernel_id) while "awaiting" the result of the shutdown. + self._kernel_connections.pop(kernel_id, None) diff -Nru jupyter-notebook-6.1.6/notebook/static/base/js/namespace.js jupyter-notebook-6.2.0/notebook/static/base/js/namespace.js --- jupyter-notebook-6.1.6/notebook/static/base/js/namespace.js 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/static/base/js/namespace.js 2021-01-13 16:30:49.000000000 +0000 @@ -73,7 +73,7 @@ // tree jglobal('SessionList','tree/js/sessionlist'); - Jupyter.version = "6.1.6"; + Jupyter.version = "6.2.0"; Jupyter._target = '_blank'; return Jupyter; diff -Nru jupyter-notebook-6.1.6/notebook/static/notebook/js/cell.js jupyter-notebook-6.2.0/notebook/static/notebook/js/cell.js --- jupyter-notebook-6.1.6/notebook/static/notebook/js/cell.js 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/static/notebook/js/cell.js 2021-01-13 16:30:49.000000000 +0000 @@ -490,6 +490,9 @@ var data = {}; // deepcopy the metadata so copied cells don't share the same object data.metadata = JSON.parse(JSON.stringify(this.metadata)); + if (this.id !== undefined) { + data.id = this.id; + } if (data.metadata.deletable) { delete data.metadata.deletable; } @@ -511,6 +514,9 @@ if (data.metadata !== undefined) { this.metadata = data.metadata; } + if (data.id !== undefined) { + this.id = data.id; + } }; diff -Nru jupyter-notebook-6.1.6/notebook/static/notebook/js/celltoolbar.js jupyter-notebook-6.2.0/notebook/static/notebook/js/celltoolbar.js --- jupyter-notebook-6.1.6/notebook/static/notebook/js/celltoolbar.js 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/static/notebook/js/celltoolbar.js 2021-01-13 16:30:49.000000000 +0000 @@ -403,7 +403,7 @@ * @static * * @param list_list {list_of_sublist} List of sublist of metadata value and name in the dropdown list. - * subslit should contain 2 element each, first a string that woul be displayed in the dropdown list, + * sublist should contain 2 element each, first a string that would be displayed in the dropdown list, * and second the corresponding value to be passed to setter/return by getter. the corresponding value * should not be "undefined" or behavior can be unexpected. * @param setter {function( cell, newValue )} diff -Nru jupyter-notebook-6.1.6/notebook/static/notebook/js/codecell.js jupyter-notebook-6.2.0/notebook/static/notebook/js/codecell.js --- jupyter-notebook-6.1.6/notebook/static/notebook/js/codecell.js 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/static/notebook/js/codecell.js 2021-01-13 16:30:49.000000000 +0000 @@ -253,7 +253,7 @@ } if (event.which === keycodes.down && event.type === 'keypress' && this.tooltip.time_before_tooltip >= 0) { - // triger on keypress (!) otherwise inconsistent event.which depending on plateform + // triger on keypress (!) otherwise inconsistent event.which depending on platform // browser and keyboard layout ! // Pressing '(' , request tooltip, don't forget to reappend it // The second argument says to hide the tooltip if the docstring diff -Nru jupyter-notebook-6.1.6/notebook/static/notebook/js/completer.js jupyter-notebook-6.2.0/notebook/static/notebook/js/completer.js --- jupyter-notebook-6.1.6/notebook/static/notebook/js/completer.js 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/static/notebook/js/completer.js 2021-01-13 16:30:49.000000000 +0000 @@ -348,7 +348,7 @@ } else if (code == keycodes.tab) { //all the fastforwarding operation, //Check that shared start is not null which can append with prefixed completion - // like %pylab , pylab have no shred start, and ff will result in py + // like %pylab , pylab have no shared start, and ff will result in py // to erase py var sh = shared_start(this.raw_result, true); if (sh.str !== '') { @@ -358,7 +358,7 @@ this.carry_on_completion(); } else if (code == keycodes.up || code == keycodes.down) { // need to do that to be able to move the arrow - // when on the first or last line ofo a code cell + // when on the first or last line of a code cell event.codemirrorIgnore = true; event._ipkmIgnore = true; event.preventDefault(); diff -Nru jupyter-notebook-6.1.6/notebook/static/notebook/js/mathjaxutils.js jupyter-notebook-6.2.0/notebook/static/notebook/js/mathjaxutils.js --- jupyter-notebook-6.1.6/notebook/static/notebook/js/mathjaxutils.js 1970-01-01 00:00:00.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/static/notebook/js/mathjaxutils.js 2021-01-13 16:30:49.000000000 +0000 @@ -0,0 +1,8 @@ + +define([ + 'base/js/mathjaxutils' +], function(mathjaxutils) { + "use strict" + + return mathjaxutils; +}); diff -Nru jupyter-notebook-6.1.6/notebook/static/notebook/js/notebook.js jupyter-notebook-6.2.0/notebook/static/notebook/js/notebook.js --- jupyter-notebook-6.1.6/notebook/static/notebook/js/notebook.js 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/static/notebook/js/notebook.js 2021-01-13 16:30:49.000000000 +0000 @@ -1648,6 +1648,9 @@ if (cell_json.metadata.deletable !== undefined) { delete cell_json.metadata.deletable; } + if (cell_json.id !== undefined) { + delete cell_json.id; + } this.clipboard.push(cell_json); } this.enable_paste(); diff -Nru jupyter-notebook-6.1.6/notebook/static/notebook/js/tooltip.js jupyter-notebook-6.2.0/notebook/static/notebook/js/tooltip.js --- jupyter-notebook-6.1.6/notebook/static/notebook/js/tooltip.js 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/static/notebook/js/tooltip.js 2021-01-13 16:30:49.000000000 +0000 @@ -135,7 +135,7 @@ }; // deal with all the logic of hiding the tooltip - // and reset it's status + // and reset its status Tooltip.prototype._hide = function () { this._hidden = true; this.tooltip.fadeOut('fast'); @@ -243,7 +243,7 @@ this._sticky = false; }; - // put the tooltip in a sicky state for 10 seconds + // put the tooltip in a sticky state for 10 seconds // it won't be removed by remove_and_cancel() unless you called with // the first parameter set to true. // remove_and_cancel_tooltip(true) diff -Nru jupyter-notebook-6.1.6/notebook/tests/test_notebookapp_integration.py jupyter-notebook-6.2.0/notebook/tests/test_notebookapp_integration.py --- jupyter-notebook-6.1.6/notebook/tests/test_notebookapp_integration.py 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/tests/test_notebookapp_integration.py 2021-01-13 16:30:49.000000000 +0000 @@ -1,16 +1,17 @@ import os +import pytest import stat import subprocess +import sys import time -from ipython_genutils.testing.decorators import skip_win32, onlyif from notebook import DEFAULT_NOTEBOOK_PORT from .launchnotebook import UNIXSocketNotebookTestBase from ..utils import urlencode_unix_socket, urlencode_unix_socket_path -@skip_win32 +@pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows") def test_shutdown_sock_server_integration(): sock = UNIXSocketNotebookTestBase.sock url = urlencode_unix_socket(sock).encode() @@ -87,7 +88,7 @@ raise AssertionError('expected all servers to be stopped') -@onlyif(bool(os.environ.get('RUN_NB_INTEGRATION_TESTS', False)), 'for local testing') +@pytest.mark.skipif(not bool(os.environ.get('RUN_NB_INTEGRATION_TESTS', False)), reason="for local testing") def test_stop_multi_integration(): """Tests lifecycle behavior for mixed-mode server types w/ default ports. @@ -137,7 +138,7 @@ p3.wait() -@skip_win32 +@pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows") def test_launch_socket_collision(): """Tests UNIX socket in-use detection for lifecycle correctness.""" sock = UNIXSocketNotebookTestBase.sock diff -Nru jupyter-notebook-6.1.6/notebook/_version.py jupyter-notebook-6.2.0/notebook/_version.py --- jupyter-notebook-6.1.6/notebook/_version.py 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/notebook/_version.py 2021-01-13 16:30:49.000000000 +0000 @@ -9,5 +9,5 @@ # Next beta/alpha/rc release: The version number for beta is X.Y.ZbN **without dots**. -version_info = (6, 1, 6, '') +version_info = (6, 2, 0) __version__ = '.'.join(map(str, version_info[:3])) + ''.join(version_info[3:]) diff -Nru jupyter-notebook-6.1.6/setup.py jupyter-notebook-6.2.0/setup.py --- jupyter-notebook-6.1.6/setup.py 2020-12-22 19:15:16.000000000 +0000 +++ jupyter-notebook-6.2.0/setup.py 2021-01-13 16:30:49.000000000 +0000 @@ -98,7 +98,7 @@ zip_safe = False, install_requires = [ 'jinja2', - 'tornado>=5.0', + 'tornado>=6.1', # pyzmq>=17 is not technically necessary, # but hopefully avoids incompatibilities with Tornado 5. April 2018 'pyzmq>=17', @@ -110,7 +110,7 @@ 'nbformat', 'nbconvert', 'ipykernel', # bless IPython kernel for now - 'Send2Trash', + 'Send2Trash>=1.5.0', 'terminado>=0.8.3', 'prometheus_client' ],