From ce64f1654a044bb2b2dc30ef0b5e76e853adc4e37d3aec55a541fe7befa12351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mark=C3=A9ta=20Machov=C3=A1?= Date: Wed, 23 Dec 2020 09:35:37 +0000 Subject: [PATCH] Accepting request 857913 from home:bnavigator:branches:devel:languages:python:jupyter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update to 6.1.5 * Fix open redirect vulnerability GHSA-c7vm-f5p4-8fqh (CVE to be assigned) - Changes for 6.1.4 * Fix broken links to jupyter documentation * Add additional entries to troubleshooting section * Revert change in page alignment * Bug fix: remove double encoding in download files * Fix typo for Check in zh_CN * Require a file name in the "Save As" dialog - Changes for 6.1.3 * Title new buttons with label if action undefined - Changes for 6.1.2 * Fix russian message format for delete/duplicate actions * Remove unnecessary import of bind_unix_socket * Tooltip style scope fix - Changes for 6.1.1 * Prevent inclusion of requests_unixsocket on Windows - Changes for 6.1.0 Please note that this repository is currently maintained by a skeleton crew of maintainers from the Jupyter community. For our approach moving forward, please see this notice from the README. Thank you. * Remove deprecated encoding parameter for Python 3.9 compatibility. * Add support for async kernel management * Fix typo in password_required help message * Gateway only: Ensure launch and request timeouts are in sync * Update Markdown Cells example to HTML5 video tag * Integrated LoginWidget into edit to enable users to logout from the t… * Update message about minimum Tornado version * Logged notebook type * Added nl language * Add UNIX socket support to notebook server. * Update CodeMirror dependency * Tree added download multiple files * Toolbar buttons tooltip: show help instead of label * Remove unnecessary import of requests_unixsocket * Add ability to cull terminals and track last activity * Code refactoring notebook.js * Install terminado for docs build * Convert notifications JS test to selenium * Add cell attachments to markdown example * Add Japanese document * Migrate Move multiselection test to selenium * Use cmdtrl-enter to run a cell * Fix broken "Raw cell MIME type" dialog * Make a notebook writable after successful save-as * Add actual watch script * Added --autoreload flag to NotebookApp * Enable check_origin on gateway websocket communication * Restore detection of missing terminado package * Culling: ensure last_activity attr exists before use * Added functionality to allow filter kernels by Jupyter Enterprise Gat… * 'Play' icon for run-cell toolbar button * Bump minimum version of jQuery to 3.5.0 * Remove old JS markdown tests, add a new one in selenium * Add support for more RTL languages * Make markdown cells stay RTL in edit mode * Unforce RTL output display * Fixed multicursor backspacing * Implemented Split Cell for multicursor * Alignment issue [FIXED] * MathJax: Support for \gdef * Another (Minor) Duplicate Code Reduction * Update readme regarding maintenance * Document contents chunks * Backspace deletes empty line * The dropdown submenu at notebook page is not keyboard accessible * Tooltips visible through keyboard navigation for specified buttons * Fix for recursive symlink * Fix for the terminal shutdown issue * Add japanese translation files * Workaround for socket permission errors on Cygwin * Implement optional markdown header and footer files * Remove double link when using custom_display_url * Respect cell.is_editable during find-and-replace * Fix exception causes all over the codebase (:ghpull:`5556` * Improve login shell heuristics * Added support for JUPYTER_TOKEN_FILE * Kill notebook itself when server cull idle kernel * Implement password hashing with bcrypt * Fix broken links * Russian internationalization support * Add a metadata tag to override notebook direction (ltr/rtl) * Paste two images from clipboard in markdown cell * Add keyboard shortcuts to menu dropdowns * Update codemirror to 5.56.0+components1 - refresh remove_nose.patch based on gh#jupyter/notebook#5826 - move the entrypoints to the flavored python package and make them alternatives - tag language files OBS-URL: https://build.opensuse.org/request/show/857913 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:jupyter/python-notebook?expand=0&rev=25 --- notebook-6.0.3.tar.gz | 3 - notebook-6.1.5.tar.gz | 3 + python-notebook.changes | 100 ++++++++ python-notebook.spec | 88 +++++-- remove_nose.patch | 548 ++++++++++++++++++++++++---------------- 5 files changed, 502 insertions(+), 240 deletions(-) delete mode 100644 notebook-6.0.3.tar.gz create mode 100644 notebook-6.1.5.tar.gz diff --git a/notebook-6.0.3.tar.gz b/notebook-6.0.3.tar.gz deleted file mode 100644 index f18566c..0000000 --- a/notebook-6.0.3.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:47a9092975c9e7965ada00b9a20f0cf637d001db60d241d479f53c0be117ad48 -size 14048151 diff --git a/notebook-6.1.5.tar.gz b/notebook-6.1.5.tar.gz new file mode 100644 index 0000000..58fb3c8 --- /dev/null +++ b/notebook-6.1.5.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3db37ae834c5f3b6378381229d0e5dfcbfb558d08c8ce646b1ad355147f5e91d +size 13925632 diff --git a/python-notebook.changes b/python-notebook.changes index c01087d..f5154a4 100644 --- a/python-notebook.changes +++ b/python-notebook.changes @@ -1,3 +1,103 @@ +------------------------------------------------------------------- +Mon Dec 21 09:59:15 UTC 2020 - Benjamin Greiner + +- Update to 6.1.5 + * Fix open redirect vulnerability GHSA-c7vm-f5p4-8fqh + (CVE to be assigned) +- Changes for 6.1.4 + * Fix broken links to jupyter documentation + * Add additional entries to troubleshooting section + * Revert change in page alignment + * Bug fix: remove double encoding in download files + * Fix typo for Check in zh_CN + * Require a file name in the "Save As" dialog +- Changes for 6.1.3 + * Title new buttons with label if action undefined +- Changes for 6.1.2 + * Fix russian message format for delete/duplicate actions + * Remove unnecessary import of bind_unix_socket + * Tooltip style scope fix +- Changes for 6.1.1 + * Prevent inclusion of requests_unixsocket on Windows +- Changes for 6.1.0 + Please note that this repository is currently maintained by a + skeleton crew of maintainers from the Jupyter community. For our + approach moving forward, please see this notice from the README. + Thank you. + * Remove deprecated encoding parameter for Python 3.9 + compatibility. + * Add support for async kernel management + * Fix typo in password_required help message + * Gateway only: Ensure launch and request timeouts are in sync + * Update Markdown Cells example to HTML5 video tag + * Integrated LoginWidget into edit to enable users to logout + from the t… + * Update message about minimum Tornado version + * Logged notebook type + * Added nl language + * Add UNIX socket support to notebook server. + * Update CodeMirror dependency + * Tree added download multiple files + * Toolbar buttons tooltip: show help instead of label + * Remove unnecessary import of requests_unixsocket + * Add ability to cull terminals and track last activity + * Code refactoring notebook.js + * Install terminado for docs build + * Convert notifications JS test to selenium + * Add cell attachments to markdown example + * Add Japanese document + * Migrate Move multiselection test to selenium + * Use cmdtrl-enter to run a cell + * Fix broken "Raw cell MIME type" dialog + * Make a notebook writable after successful save-as + * Add actual watch script + * Added --autoreload flag to NotebookApp + * Enable check_origin on gateway websocket communication + * Restore detection of missing terminado package + * Culling: ensure last_activity attr exists before use + * Added functionality to allow filter kernels by Jupyter + Enterprise Gat… + * 'Play' icon for run-cell toolbar button + * Bump minimum version of jQuery to 3.5.0 + * Remove old JS markdown tests, add a new one in selenium + * Add support for more RTL languages + * Make markdown cells stay RTL in edit mode + * Unforce RTL output display + * Fixed multicursor backspacing + * Implemented Split Cell for multicursor + * Alignment issue [FIXED] + * MathJax: Support for \gdef + * Another (Minor) Duplicate Code Reduction + * Update readme regarding maintenance + * Document contents chunks + * Backspace deletes empty line + * The dropdown submenu at notebook page is not keyboard + accessible + * Tooltips visible through keyboard navigation for specified + buttons + * Fix for recursive symlink + * Fix for the terminal shutdown issue + * Add japanese translation files + * Workaround for socket permission errors on Cygwin + * Implement optional markdown header and footer files + * Remove double link when using custom_display_url + * Respect cell.is_editable during find-and-replace + * Fix exception causes all over the codebase (:ghpull:`5556` + * Improve login shell heuristics + * Added support for JUPYTER_TOKEN_FILE + * Kill notebook itself when server cull idle kernel + * Implement password hashing with bcrypt + * Fix broken links + * Russian internationalization support + * Add a metadata tag to override notebook direction (ltr/rtl) + * Paste two images from clipboard in markdown cell + * Add keyboard shortcuts to menu dropdowns + * Update codemirror to 5.56.0+components1 +- refresh remove_nose.patch based on gh#jupyter/notebook#5826 +- move the entrypoints to the flavored python package and make them + alternatives +- tag language files + ------------------------------------------------------------------- Thu Oct 22 18:44:45 UTC 2020 - Matej Cepl diff --git a/python-notebook.spec b/python-notebook.spec index f21feb4..1bef08f 100644 --- a/python-notebook.spec +++ b/python-notebook.spec @@ -27,7 +27,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-notebook%{psuffix} -Version: 6.0.3 +Version: 6.1.5 Release: 0 Summary: Jupyter Notebook interface License: BSD-3-Clause @@ -35,8 +35,7 @@ Group: Development/Languages/Python URL: https://github.com/jupyter/notebook Source0: https://files.pythonhosted.org/packages/source/n/notebook/notebook-%{version}.tar.gz Source100: python-notebook-rpmlintrc -# PATCH-FIX-UPSTREAM remove_nose.patch gh#jupyter/notebook#4753 mcepl@suse.com -# Port the test suite to pytest from nose +# PATCH-FIX-UPSTREAM remove_nose.patch gh#jupyter/notebook#5826 -- Port the test suite to pytest from nose Patch0: remove_nose.patch BuildRequires: %{python_module jupyter-core >= 4.4.0} BuildRequires: %{python_module setuptools} @@ -44,15 +43,16 @@ BuildRequires: python-rpm-macros Requires: jupyter-notebook = %{version} Requires: python-Jinja2 Requires: python-Send2Trash +Requires: python-argon2-cffi Requires: python-ipykernel Requires: python-ipython_genutils -Requires: python-jupyter-client >= 5.3.1 -Requires: python-jupyter-core >= 4.4.0 +Requires: python-jupyter-client >= 5.3.4 +Requires: python-jupyter-core >= 4.6.1 Requires: python-nbconvert Requires: python-nbformat Requires: python-prometheus_client Requires: python-pyzmq >= 17 -Requires: python-terminado >= 0.8.1 +Requires: python-terminado >= 0.8.3 Requires: python-tornado >= 5 Requires: python-traitlets >= 4.2.1 Recommends: python-ipywidgets @@ -68,18 +68,23 @@ BuildRequires: jupyter-notebook-filesystem %if %{with test} BuildRequires: %{python_module Jinja2} BuildRequires: %{python_module Send2Trash} +BuildRequires: %{python_module argon2-cffi} BuildRequires: %{python_module attrs >= 17.4.0} BuildRequires: %{python_module ipykernel} BuildRequires: %{python_module ipython_genutils} -BuildRequires: %{python_module jupyter-client >= 5.3.1} -BuildRequires: %{python_module jupyter-core >= 4.4.0} +BuildRequires: %{python_module jupyter-client >= 5.3.4} +BuildRequires: %{python_module jupyter-core >= 4.6.1} BuildRequires: %{python_module nbconvert} BuildRequires: %{python_module nbformat} +BuildRequires: %{python_module nbval} +# Some dependency loop involving the jupyter-notebook subpackage could pull in an old version otherwise! +BuildRequires: %{python_module notebook = %{version}} BuildRequires: %{python_module prometheus_client} BuildRequires: %{python_module pytest} BuildRequires: %{python_module pyzmq >= 17} +BuildRequires: %{python_module requests-unixsocket} BuildRequires: %{python_module requests} -BuildRequires: %{python_module terminado >= 0.8.1} +BuildRequires: %{python_module terminado >= 0.8.3} BuildRequires: %{python_module tornado >= 5} BuildRequires: %{python_module traitlets >= 4.2.1} BuildRequires: pandoc @@ -93,10 +98,8 @@ interactive computing. This package provides the python interface. %package lang -# FIXME: consider using %%lang_package macro Summary: Translations for the Jupyter Notebook Group: System/Localization -Requires: jupyter-notebook-lang = %{version} Requires: python-notebook = %{version} Provides: python-jupyter_notebook-lang = %{version} Provides: python-notebook-lang-all = %{version} @@ -111,8 +114,8 @@ This package provides the Python module translations. Summary: Jupyter Notebook interface Group: Development/Languages/Python Requires: jupyter-ipykernel -Requires: jupyter-jupyter-client >= 5.2.0 -Requires: jupyter-jupyter-core >= 4.4.0 +Requires: jupyter-jupyter-client >= 5.3.4 +Requires: jupyter-jupyter-core >= 4.6.1 Requires: jupyter-nbconvert Requires: jupyter-nbformat Requires: jupyter-notebook-filesystem @@ -128,7 +131,6 @@ interactive computing. This package provides the jupyter components. %package -n jupyter-notebook-lang -# FIXME: consider using %%lang_package macro Summary: Translations for the Jupyter Notebook Group: System/Localization Requires: jupyter-notebook = %{version} @@ -178,33 +180,71 @@ for x in 16 24 32 48 64 128 256 512 ; do mkdir -p %{buildroot}%{_datadir}/icons/hicolor/${x}x${x}/apps/ cp docs/resources/ipynb.iconset/icon_${x}x${x}.png %{buildroot}%{_datadir}/icons/hicolor/${x}x${x}/apps/JupyterNotebook.png done + +%{python_expand # the structure is not compatible with (python_)find_lang. Roll our own. +find %{buildroot}%{$python_sitelib}/notebook/i18n -type f -o -type l | grep -v '__init__' > lang-files +sed -E ' + s:%{buildroot}:: + s:(.*/notebook/i18n/)([^/_]+)(.*(mo|po|json)$):%lang(\2) \1\2\3: +' > %{$python_prefix}-notebook.lang < lang-files +sed -E ' + s:%{buildroot}:: + s:(.*/notebook/i18n/)([^/_]+)(.*(mo|po|json)$):%exclude \1\2\3: +' > %{$python_prefix}-notebook.lang-exclude < lang-files +find %{buildroot}%{$python_sitelib}/notebook/i18n -type d -mindepth 1 | grep -v '__pycache__' > lang-dirs +sed -E ' + s:%{buildroot}:: + s:(.*):%dir \1: +' >> %{$python_prefix}-notebook.lang < lang-dirs +sed -E ' + s:%{buildroot}:: + s:(.*):%exclude %dir \1: +' >> %{$python_prefix}-notebook.lang-exclude < lang-dirs +} + +%python_clone -a %{buildroot}%{_bindir}/jupyter-bundlerextension +%python_clone -a %{buildroot}%{_bindir}/jupyter-nbextension +%python_clone -a %{buildroot}%{_bindir}/jupyter-notebook +%python_clone -a %{buildroot}%{_bindir}/jupyter-serverextension + %endif %if %{with test} %check export LANG=en_US.UTF-8 -%pytest +# test_launch_socket_collision: fails because there are still servers listening +pythonall_donttest="test_launch_socket_collision" +%{python_expand # these tests call the wrong interpreter somewhere deep in the stack +if [ "$python_" != "python3_" -a "%{$python_provides}" != "python3" ]; then + python_$python_donttest+=" or (test_kernels_api and (test_connection or test_culling))" +fi +} +%pytest -v -k "not (${pythonall_donttest} ${python_$python_donttest})" %endif %if !%{with test} -%files %{python_files} + +%post +%python_install_alternative jupyter-notebook jupyter-bundlerextension jupyter-nbextension jupyter-serverextension + +%postun +%python_uninstall_alternative jupyter-notebook + +%files %{python_files} -f %{python_prefix}-notebook.lang-exclude %doc README.md %license LICENSE %{python_sitelib}/notebook-*-py*.egg-info %{python_sitelib}/notebook/ -%exclude %{python_sitelib}/notebook/i18n/*/ +%python_alternative %{_bindir}/jupyter-bundlerextension +%python_alternative %{_bindir}/jupyter-nbextension +%python_alternative %{_bindir}/jupyter-notebook +%python_alternative %{_bindir}/jupyter-serverextension -%files %{python_files lang} +%files %{python_files lang} -f %{python_prefix}-notebook.lang %license LICENSE -%lang(fr_FR) %{python_sitelib}/notebook/i18n/fr_FR/ -%lang(zh_CN) %{python_sitelib}/notebook/i18n/zh_CN/ %files -n jupyter-notebook %license LICENSE -%{_bindir}/jupyter-bundlerextension -%{_bindir}/jupyter-nbextension -%{_bindir}/jupyter-notebook -%{_bindir}/jupyter-serverextension %{_datadir}/icons/hicolor/*/apps/JupyterNotebook.* %files -n jupyter-notebook-lang diff --git a/remove_nose.patch b/remove_nose.patch index 10a6afa..ffd2765 100644 --- a/remove_nose.patch +++ b/remove_nose.patch @@ -1,75 +1,74 @@ ---- - notebook/auth/tests/test_security.py | 17 ++++---- - notebook/services/contents/tests/test_fileio.py | 41 ++++++++++---------- - notebook/services/contents/tests/test_manager.py | 3 - - notebook/tests/launchnotebook.py | 8 ++-- - notebook/tests/test_gateway.py | 19 ++++----- - notebook/tests/test_i18n.py | 9 +--- - notebook/tests/test_notebookapp.py | 41 +++++++++----------- - notebook/tests/test_paths.py | 6 +-- - notebook/tests/test_serialize.py | 6 +-- - notebook/tests/test_utils.py | 45 ++++++++++------------- - setup.cfg | 9 ---- - 11 files changed, 90 insertions(+), 114 deletions(-) +From 1c3dbcc6bf73c71e2cb8f1f1da846f141cc68d58 Mon Sep 17 00:00:00 2001 +From: pgajdos +Date: Thu, 22 Oct 2020 15:34:17 +0200 +Subject: [PATCH 1/6] do not use nose for testing ---- a/notebook/auth/tests/test_security.py -+++ b/notebook/auth/tests/test_security.py -@@ -1,25 +1,24 @@ +--- + notebook/auth/tests/test_security.py | 13 ++--- + .../services/contents/tests/test_fileio.py | 56 +++++++++---------- + .../services/contents/tests/test_manager.py | 15 ++--- + notebook/tests/test_gateway.py | 33 ++++++----- + notebook/tests/test_i18n.py | 9 +-- + notebook/tests/test_nbextensions.py | 9 +-- + notebook/tests/test_notebookapp.py | 38 ++++++------- + notebook/tests/test_paths.py | 7 ++- + notebook/tests/test_serialize.py | 6 +- + notebook/tests/test_utils.py | 44 +++++++-------- + 10 files changed, 109 insertions(+), 121 deletions(-) + +Index: notebook-6.1.5/notebook/auth/tests/test_security.py +=================================================================== +--- notebook-6.1.5.orig/notebook/auth/tests/test_security.py ++++ notebook-6.1.5/notebook/auth/tests/test_security.py +@@ -1,22 +1,21 @@ # coding: utf-8 - from ..security import passwd, passwd_check, salt_len + from ..security import passwd, passwd_check -import nose.tools as nt def test_passwd_structure(): p = passwd('passphrase') - algorithm, salt, hashed = p.split(':') -- nt.assert_equal(algorithm, 'sha1') -- nt.assert_equal(len(salt), salt_len) -- nt.assert_equal(len(hashed), 40) -+ assert algorithm == 'sha1' -+ assert len(salt) == salt_len -+ assert len(hashed) == 40 + algorithm, hashed = p.split(':') +- nt.assert_equal(algorithm, 'argon2') +- nt.assert_true(hashed.startswith('$argon2id$')) ++ assert algorithm == 'argon2' ++ assert hashed.startswith('$argon2id$') def test_roundtrip(): p = passwd('passphrase') - nt.assert_equal(passwd_check(p, 'passphrase'), True) -+ assert passwd_check(p, 'passphrase') ++ assert passwd_check(p, 'passphrase') == True def test_bad(): p = passwd('passphrase') - nt.assert_equal(passwd_check(p, p), False) - nt.assert_equal(passwd_check(p, 'a:b:c:d'), False) - nt.assert_equal(passwd_check(p, 'a:b'), False) -+ assert not passwd_check(p, p) -+ assert not passwd_check(p, 'a:b:c:d') -+ assert not passwd_check(p, 'a:b') ++ assert passwd_check(p, p) == False ++ assert passwd_check(p, 'a:b:c:d') == False ++ assert passwd_check(p, 'a:b') == False def test_passwd_check_unicode(): # GH issue #4524 - phash = u'sha1:23862bc21dd3:7a415a95ae4580582e314072143d9c382c491e4f' -- assert passwd_check(phash, u"łe¶ŧ←↓→") -\ No newline at end of file -+ assert passwd_check(phash, u"łe¶ŧ←↓→") ---- a/notebook/services/contents/tests/test_fileio.py -+++ b/notebook/services/contents/tests/test_fileio.py -@@ -8,7 +8,7 @@ import io as stdlib_io +Index: notebook-6.1.5/notebook/services/contents/tests/test_fileio.py +=================================================================== +--- notebook-6.1.5.orig/notebook/services/contents/tests/test_fileio.py ++++ notebook-6.1.5/notebook/services/contents/tests/test_fileio.py +@@ -6,11 +6,11 @@ + + import io as stdlib_io import os.path ++import unittest ++import pytest import stat ++import sys -import nose.tools as nt -+import pytest - - from ipython_genutils.testing.decorators import skip_win32 +- +-from ipython_genutils.testing.decorators import skip_win32 from ..fileio import atomic_writing -@@ -24,7 +24,7 @@ def test_atomic_writing(): - f1 = os.path.join(td, 'penguin') - with stdlib_io.open(f1, 'w') as f: - f.write(u'Before') -- -+ - if os.name != 'nt': - os.chmod(f1, 0o701) - orig_mode = stat.S_IMODE(os.stat(f1).st_mode) -@@ -39,32 +39,32 @@ def test_atomic_writing(): + + from ipython_genutils.tempdir import TemporaryDirectory +@@ -39,24 +39,24 @@ def test_atomic_writing(): # OSError: The user lacks the privilege (Windows) have_symlink = False @@ -98,140 +97,169 @@ if have_symlink: # Check that writing over a file preserves a symlink - with atomic_writing(f2) as f: +@@ -64,33 +64,35 @@ def test_atomic_writing(): f.write(u'written from symlink') -- -+ + with stdlib_io.open(f1, 'r') as f: - nt.assert_equal(f.read(), u'written from symlink') + assert f.read() == u'written from symlink' - def _save_umask(): - global umask -@@ -75,57 +75,58 @@ def _restore_umask(): - os.umask(umask) - - @skip_win32 +-def _save_umask(): +- global umask +- umask = os.umask(0) +- os.umask(umask) +- +-def _restore_umask(): +- os.umask(umask) +- +-@skip_win32 -@nt.with_setup(_save_umask, _restore_umask) - def test_atomic_writing_umask(): -+ _save_umask() - with TemporaryDirectory() as td: - os.umask(0o022) - f1 = os.path.join(td, '1') - with atomic_writing(f1) as f: - f.write(u'1') - mode = stat.S_IMODE(os.stat(f1).st_mode) +-def test_atomic_writing_umask(): +- with TemporaryDirectory() as td: +- os.umask(0o022) +- f1 = os.path.join(td, '1') +- with atomic_writing(f1) as f: +- f.write(u'1') +- mode = stat.S_IMODE(os.stat(f1).st_mode) - nt.assert_equal(mode, 0o644, '{:o} != 644'.format(mode)) -+ assert mode, 0o644 == '{:o} != 644'.format(mode) - - os.umask(0o057) - f2 = os.path.join(td, '2') - with atomic_writing(f2) as f: - f.write(u'2') - mode = stat.S_IMODE(os.stat(f2).st_mode) +- +- os.umask(0o057) +- f2 = os.path.join(td, '2') +- with atomic_writing(f2) as f: +- f.write(u'2') +- mode = stat.S_IMODE(os.stat(f2).st_mode) - nt.assert_equal(mode, 0o620, '{:o} != 620'.format(mode)) -+ assert mode, 0o620 == '{:o} != 620'.format(mode) -+ _restore_umask() ++class TestWithSetUmask(unittest.TestCase): ++ def setUp(self): ++ # save umask ++ global umask ++ umask = os.umask(0) ++ os.umask(umask) ++ ++ def tearDown(self): ++ # restore umask ++ os.umask(umask) ++ ++ @pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows") ++ def test_atomic_writing_umask(self): ++ with TemporaryDirectory() as td: ++ os.umask(0o022) ++ f1 = os.path.join(td, '1') ++ with atomic_writing(f1) as f: ++ f.write(u'1') ++ mode = stat.S_IMODE(os.stat(f1).st_mode) ++ assert mode == 0o644 ++ ++ os.umask(0o057) ++ f2 = os.path.join(td, '2') ++ with atomic_writing(f2) as f: ++ f.write(u'2') ++ mode = stat.S_IMODE(os.stat(f2).st_mode) ++ assert mode == 0o620 def test_atomic_writing_newlines(): - with TemporaryDirectory() as td: - path = os.path.join(td, 'testfile') -- -+ - lf = u'a\nb\nc\n' - plat = lf.replace(u'\n', os.linesep) - crlf = lf.replace(u'\n', u'\r\n') -- -+ - # test default - with stdlib_io.open(path, 'w') as f: +@@ -106,21 +108,21 @@ def test_atomic_writing_newlines(): f.write(lf) with stdlib_io.open(path, 'r', newline='') as f: read = f.read() - nt.assert_equal(read, plat) -- + assert read == plat -+ + # test newline=LF with stdlib_io.open(path, 'w', newline='\n') as f: f.write(lf) with stdlib_io.open(path, 'r', newline='') as f: read = f.read() - nt.assert_equal(read, lf) -- + assert read == lf -+ + # test newline=CRLF with atomic_writing(path, newline='\r\n') as f: f.write(lf) with stdlib_io.open(path, 'r', newline='') as f: read = f.read() - nt.assert_equal(read, crlf) -- + assert read == crlf -+ + # test newline=no convert text = u'crlf\r\ncr\rlf\n' - with atomic_writing(path, newline='') as f: +@@ -128,4 +130,4 @@ def test_atomic_writing_newlines(): f.write(text) with stdlib_io.open(path, 'r', newline='') as f: read = f.read() - nt.assert_equal(read, text) + assert read == text ---- a/notebook/services/contents/tests/test_manager.py -+++ b/notebook/services/contents/tests/test_manager.py -@@ -8,9 +8,8 @@ import time +Index: notebook-6.1.5/notebook/services/contents/tests/test_manager.py +=================================================================== +--- notebook-6.1.5.orig/notebook/services/contents/tests/test_manager.py ++++ notebook-6.1.5/notebook/services/contents/tests/test_manager.py +@@ -8,16 +8,14 @@ import time from contextlib import contextmanager from itertools import combinations -from nose import SkipTest from tornado.web import HTTPError -from unittest import TestCase -+from unittest import TestCase, SkipTest ++from unittest import TestCase, skipIf from tempfile import NamedTemporaryFile from nbformat import v4 as nbformat ---- a/notebook/tests/launchnotebook.py -+++ b/notebook/tests/launchnotebook.py -@@ -68,7 +68,7 @@ class NotebookTestBase(TestCase): - cls.notebook_thread.join(timeout=MAX_WAITTIME) - if cls.notebook_thread.is_alive(): - raise TimeoutError("Undead notebook server") -- -+ - @classmethod - def auth_headers(cls): - headers = {} -@@ -79,7 +79,7 @@ class NotebookTestBase(TestCase): - @classmethod - def request(cls, verb, path, **kwargs): - """Send a request to my server -- -+ - with authentication and everything. - """ - headers = kwargs.setdefault('headers', {}) -@@ -104,7 +104,7 @@ class NotebookTestBase(TestCase): - @classmethod - def get_argv(cls): - return [] -- -+ - @classmethod - def setup_class(cls): - cls.tmp_dir = TemporaryDirectory() -@@ -116,7 +116,7 @@ class NotebookTestBase(TestCase): - if e.errno != errno.EEXIST: - raise - return path -- -+ - cls.home_dir = tmp('home') - data_dir = cls.data_dir = tmp('data') - config_dir = cls.config_dir = tmp('config') ---- a/notebook/tests/test_gateway.py -+++ b/notebook/tests/test_gateway.py + + from ipython_genutils.tempdir import TemporaryDirectory + from traitlets import TraitError +-from ipython_genutils.testing import decorators as dec + + from ..filemanager import FileContentsManager + +@@ -108,7 +106,7 @@ class TestFileContentsManager(TestCase): + self.assertEqual(cp_dir, os.path.join(root, cpm.checkpoint_dir, cp_name)) + self.assertEqual(cp_subdir, os.path.join(root, subd, cpm.checkpoint_dir, cp_name)) + +- @dec.skipif(sys.platform == 'win32' and sys.version_info[0] < 3) ++ @skipIf(sys.platform == 'win32' and sys.version_info[0] < 3, "will not run on windows") + def test_bad_symlink(self): + with TemporaryDirectory() as td: + cm = FileContentsManager(root_dir=td) +@@ -129,7 +127,7 @@ class TestFileContentsManager(TestCase): + # broken symlinks should still be shown in the contents manager + self.assertTrue('bad symlink' in contents) + +- @dec.skipif(sys.platform == 'win32') ++ @skipIf(sys.platform == 'win32', "will not run on windows") + def test_recursive_symlink(self): + with TemporaryDirectory() as td: + cm = FileContentsManager(root_dir=td) +@@ -149,7 +147,7 @@ class TestFileContentsManager(TestCase): + # recursive symlinks should not be shown in the contents manager + self.assertNotIn('recursive', contents) + +- @dec.skipif(sys.platform == 'win32' and sys.version_info[0] < 3) ++ @skipIf(sys.platform == 'win32' and sys.version_info[0] < 3, "will not run on windows") + def test_good_symlink(self): + with TemporaryDirectory() as td: + cm = FileContentsManager(root_dir=td) +@@ -169,13 +167,10 @@ class TestFileContentsManager(TestCase): + [symlink_model, file_model], + ) + +- def test_403(self): +- if hasattr(os, 'getuid'): +- if os.getuid() == 0: +- raise SkipTest("Can't test permissions as root") +- if sys.platform.startswith('win'): +- raise SkipTest("Can't test permissions on Windows") + ++ @skipIf(hasattr(os, 'getuid') and os.getuid() == 0, "Can't test permissions as root") ++ @skipIf(sys.platform.startswith('win'), "Can't test permissions on Windows") ++ def test_403(self): + with TemporaryDirectory() as td: + cm = FileContentsManager(root_dir=td) + model = cm.new_untitled(type='file') +Index: notebook-6.1.5/notebook/tests/test_gateway.py +=================================================================== +--- notebook-6.1.5.orig/notebook/tests/test_gateway.py ++++ notebook-6.1.5/notebook/tests/test_gateway.py @@ -6,7 +6,6 @@ from datetime import datetime from io import StringIO from unittest.mock import patch @@ -249,8 +277,8 @@ model = generate_model(name) running_kernels[model.get('id')] = model # Register model as a running kernel response_buf = StringIO(json.dumps(model)) -@@ -159,17 +158,17 @@ class TestGateway(NotebookTestBase): - return argv +@@ -164,19 +163,19 @@ class TestGateway(NotebookTestBase): + super(TestGateway, self).setUp() def test_gateway_options(self): - nt.assert_equal(self.notebook.gateway_config.gateway_enabled, True) @@ -258,11 +286,15 @@ - nt.assert_equal(self.notebook.gateway_config.http_user, TestGateway.mock_http_user) - nt.assert_equal(self.notebook.gateway_config.connect_timeout, self.notebook.gateway_config.connect_timeout) - nt.assert_equal(self.notebook.gateway_config.connect_timeout, 44.4) -+ assert self.notebook.gateway_config.gateway_enabled +- nt.assert_equal(self.notebook.gateway_config.request_timeout, 96.0) +- nt.assert_equal(os.environ['KERNEL_LAUNCH_TIMEOUT'], str(96)) # Ensure KLT gets set from request-timeout ++ assert self.notebook.gateway_config.gateway_enabled == True + assert self.notebook.gateway_config.url == TestGateway.mock_gateway_url + assert self.notebook.gateway_config.http_user == TestGateway.mock_http_user + assert self.notebook.gateway_config.connect_timeout == self.notebook.gateway_config.connect_timeout + assert self.notebook.gateway_config.connect_timeout == 44.4 ++ assert self.notebook.gateway_config.request_timeout == 96.0 ++ assert os.environ['KERNEL_LAUNCH_TIMEOUT'] == str(96) # Ensure KLT gets set from request-timeout def test_gateway_class_mappings(self): # Ensure appropriate class mappings are in place. @@ -275,8 +307,35 @@ def test_gateway_get_kernelspecs(self): # Validate that kernelspecs come from gateway. ---- a/notebook/tests/test_i18n.py -+++ b/notebook/tests/test_i18n.py +@@ -185,19 +184,19 @@ class TestGateway(NotebookTestBase): + self.assertEqual(response.status_code, 200) + content = json.loads(response.content.decode('utf-8')) + kspecs = content.get('kernelspecs') +- self.assertEqual(len(kspecs), 2) +- self.assertEqual(kspecs.get('kspec_bar').get('name'), 'kspec_bar') ++ assert len(kspecs) == 2 ++ assert kspecs.get('kspec_bar').get('name') == 'kspec_bar' + + def test_gateway_get_named_kernelspec(self): + # Validate that a specific kernelspec can be retrieved from gateway. + with mocked_gateway: + response = self.request('GET', '/api/kernelspecs/kspec_foo') +- self.assertEqual(response.status_code, 200) ++ assert response.status_code == 200 + kspec_foo = json.loads(response.content.decode('utf-8')) +- self.assertEqual(kspec_foo.get('name'), 'kspec_foo') ++ assert kspec_foo.get('name') == 'kspec_foo' + + response = self.request('GET', '/api/kernelspecs/no_such_spec') +- self.assertEqual(response.status_code, 404) ++ assert response.status_code == 404 + + def test_gateway_session_lifecycle(self): + # Validate session lifecycle functions; create and delete. +Index: notebook-6.1.5/notebook/tests/test_i18n.py +=================================================================== +--- notebook-6.1.5.orig/notebook/tests/test_i18n.py ++++ notebook-6.1.5/notebook/tests/test_i18n.py @@ -1,10 +1,7 @@ -import nose.tools as nt - @@ -286,21 +345,64 @@ palh = i18n.parse_accept_lang_header - nt.assert_equal(palh(''), []) - nt.assert_equal(palh('zh-CN,en-GB;q=0.7,en;q=0.3'), -- ['en', 'en_GB', 'zh_CN']) +- ['en', 'en_GB', 'zh', 'zh_CN']) - nt.assert_equal(palh('nl,fr;q=0'), ['nl']) + assert palh('') == [] -+ assert palh('zh-CN,en-GB;q=0.7,en;q=0.3') == ['en', 'en_GB', 'zh_CN'] ++ assert palh('zh-CN,en-GB;q=0.7,en;q=0.3') == ['en', 'en_GB', 'zh', 'zh_CN'] + assert palh('nl,fr;q=0') == ['nl'] ---- a/notebook/tests/test_notebookapp.py -+++ b/notebook/tests/test_notebookapp.py -@@ -4,14 +4,11 @@ import getpass - import logging +Index: notebook-6.1.5/notebook/tests/test_nbextensions.py +=================================================================== +--- notebook-6.1.5.orig/notebook/tests/test_nbextensions.py ++++ notebook-6.1.5/notebook/tests/test_nbextensions.py +@@ -6,6 +6,7 @@ + + import glob import os - import re --import signal --from subprocess import Popen, PIPE, STDOUT --import sys - from tempfile import NamedTemporaryFile ++import pytest + import sys + import tarfile + import zipfile +@@ -321,7 +322,7 @@ class TestInstallNBExtension(TestCase): + assert check_nbextension([f], user=True) + assert not check_nbextension([f, pjoin('dne', f)], user=True) + +- @dec.skip_win32 ++ @pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows") + def test_install_symlink(self): + with TemporaryDirectory() as d: + f = u'ƒ.js' +@@ -333,7 +334,7 @@ class TestInstallNBExtension(TestCase): + link = os.readlink(dest) + self.assertEqual(link, src) + +- @dec.skip_win32 ++ @pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows") + def test_overwrite_broken_symlink(self): + with TemporaryDirectory() as d: + f = u'ƒ.js' +@@ -349,7 +350,7 @@ class TestInstallNBExtension(TestCase): + link = os.readlink(dest) + self.assertEqual(link, src2) + +- @dec.skip_win32 ++ @pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows") + def test_install_symlink_destination(self): + with TemporaryDirectory() as d: + f = u'ƒ.js' +@@ -362,7 +363,7 @@ class TestInstallNBExtension(TestCase): + link = os.readlink(dest) + self.assertEqual(link, src) + +- @dec.skip_win32 ++ @pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows") + def test_install_symlink_bad(self): + with self.assertRaises(ValueError): + install_nbextension("http://example.com/foo.js", symlink=True) +Index: notebook-6.1.5/notebook/tests/test_notebookapp.py +=================================================================== +--- notebook-6.1.5.orig/notebook/tests/test_notebookapp.py ++++ notebook-6.1.5/notebook/tests/test_notebookapp.py +@@ -11,7 +11,7 @@ from tempfile import NamedTemporaryFile from unittest.mock import patch @@ -309,7 +411,7 @@ from traitlets.tests.utils import check_help_all_output -@@ -37,11 +34,11 @@ def test_server_info_file(): +@@ -37,11 +37,11 @@ def test_server_info_file(): nbapp.initialize(argv=[]) nbapp.write_server_info_file() servers = get_servers() @@ -325,7 +427,7 @@ # The ENOENT error should be silenced. nbapp.remove_server_info_file() -@@ -49,43 +46,43 @@ def test_server_info_file(): +@@ -49,43 +49,43 @@ def test_server_info_file(): def test_nb_dir(): with TemporaryDirectory() as td: app = NotebookApp(notebook_dir=td) @@ -376,7 +478,7 @@ app.start() assert os.path.exists(os.path.join(td, 'jupyter_notebook_config.py')) -@@ -100,7 +97,7 @@ def test_pep440_version(): +@@ -100,7 +100,7 @@ def test_pep440_version(): '1.2.3.dev1.post2', ]: def loc(): @@ -385,7 +487,7 @@ raise_on_bad_version(version) yield loc -@@ -136,7 +133,7 @@ def test_notebook_password(): +@@ -136,13 +136,13 @@ def test_notebook_password(): app.start() nb = NotebookApp() nb.load_config_file() @@ -393,9 +495,21 @@ + assert nb.password != '' passwd_check(nb.password, password) - class TestingStopApp(notebookapp.NbserverStopApp): -@@ -171,17 +168,17 @@ def test_notebook_stop(): - app = TestingStopApp() +-class TestingStopApp(notebookapp.NbserverStopApp): ++class MyStopApp(notebookapp.NbserverStopApp): + """For testing the logic of NbserverStopApp.""" + def __init__(self, **kwargs): +- super(TestingStopApp, self).__init__(**kwargs) ++ super(MyStopApp, self).__init__(**kwargs) + self.servers_shut_down = [] + + def shutdown_server(self, server): +@@ -168,20 +168,20 @@ def test_notebook_stop(): + + # test stop with a match + with mock_servers: +- app = TestingStopApp() ++ app = MyStopApp() app.initialize(['105']) app.start() - nt.assert_equal(len(app.servers_shut_down), 1) @@ -405,7 +519,8 @@ # test no match with mock_servers, patch('os.kill') as os_kill: - app = TestingStopApp() +- app = TestingStopApp() ++ app = MyStopApp() app.initialize(['999']) - with nt.assert_raises(SystemExit) as exc: + with pytest.raises(SystemExit) as exc: @@ -417,32 +532,39 @@ class NotebookAppTests(NotebookTestBase): ---- a/notebook/tests/test_paths.py -+++ b/notebook/tests/test_paths.py -@@ -1,6 +1,4 @@ -- +Index: notebook-6.1.5/notebook/tests/test_paths.py +=================================================================== +--- notebook-6.1.5.orig/notebook/tests/test_paths.py ++++ notebook-6.1.5/notebook/tests/test_paths.py +@@ -1,6 +1,5 @@ + import re -from nose.tools import assert_regex, assert_not_regex from notebook.base.handlers import path_regex - -@@ -16,7 +14,7 @@ def test_path_regex(): + from notebook.utils import url_path_join +@@ -19,7 +18,7 @@ def test_path_regex(): '/x/foo/bar', '/x/foo/bar.txt', ): - assert_regex(path, path_pat) -+ assert re.search(path_pat, path) ++ assert re.match(path_pat, path) def test_path_regex_bad(): for path in ( -@@ -29,4 +27,4 @@ def test_path_regex_bad(): +@@ -32,7 +31,7 @@ def test_path_regex_bad(): '/y', '/y/x/foo', ): - assert_not_regex(path, path_pat) -+ assert not re.search(path_pat, path) ---- a/notebook/tests/test_serialize.py -+++ b/notebook/tests/test_serialize.py ++ assert not re.match(path_pat, path) + + + class RedirectTestCase(NotebookTestBase): +Index: notebook-6.1.5/notebook/tests/test_serialize.py +=================================================================== +--- notebook-6.1.5.orig/notebook/tests/test_serialize.py ++++ notebook-6.1.5/notebook/tests/test_serialize.py @@ -2,8 +2,6 @@ import os @@ -467,18 +589,22 @@ msg2 = deserialize_binary_message(bmsg) - nt.assert_equal(msg2, msg) + assert msg2 == msg ---- a/notebook/tests/test_utils.py -+++ b/notebook/tests/test_utils.py -@@ -6,8 +6,6 @@ +Index: notebook-6.1.5/notebook/tests/test_utils.py +=================================================================== +--- notebook-6.1.5.orig/notebook/tests/test_utils.py ++++ notebook-6.1.5/notebook/tests/test_utils.py +@@ -5,8 +5,9 @@ + import ctypes import os ++import sys -import nose.tools as nt -- ++import pytest + from traitlets.tests.utils import check_help_all_output from notebook.utils import url_escape, url_unescape, is_hidden, is_file_hidden - from ipython_genutils.py3compat import cast_unicode -@@ -26,60 +24,60 @@ def test_url_escape(): +@@ -26,62 +27,61 @@ def test_url_escape(): # changes path or notebook name with special characters to url encoding # these tests specifically encode paths with spaces path = url_escape('/this is a test/for spaces/') @@ -491,14 +617,12 @@ path = url_escape('/path with a/notebook and space.ipynb') - nt.assert_equal(path, '/path%20with%20a/notebook%20and%20space.ipynb') -- + assert path == '/path%20with%20a/notebook%20and%20space.ipynb' -+ + path = url_escape('/ !@$#%^&* / test %^ notebook @#$ name.ipynb') - nt.assert_equal(path, - '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb') -+ assert path == \ -+ '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb' ++ assert path == '/%20%21%40%24%23%25%5E%26%2A%20/%20test%20%25%5E%20notebook%20%40%23%24%20name.ipynb' def test_url_unescape(): @@ -527,25 +651,25 @@ os.makedirs(subdir1) - nt.assert_equal(is_hidden(subdir1, root), False) - nt.assert_equal(is_file_hidden(subdir1), False) -+ assert not is_hidden(subdir1, root) -+ assert not is_file_hidden(subdir1) ++ assert is_hidden(subdir1, root) == False ++ assert is_file_hidden(subdir1) == False subdir2 = os.path.join(root, '.subdir2') os.makedirs(subdir2) - nt.assert_equal(is_hidden(subdir2, root), True) - nt.assert_equal(is_file_hidden(subdir2), True)# -+ assert is_hidden(subdir2, root) -+ assert is_file_hidden(subdir2)# ++ assert is_hidden(subdir2, root) == True ++ assert is_file_hidden(subdir2) == True # root dir is always visible - nt.assert_equal(is_hidden(subdir2, subdir2), False) -+ assert not is_hidden(subdir2, subdir2) ++ assert is_hidden(subdir2, subdir2) == False subdir34 = os.path.join(root, 'subdir3', '.subdir4') os.makedirs(subdir34) - nt.assert_equal(is_hidden(subdir34, root), True) - nt.assert_equal(is_hidden(subdir34), True) -+ assert is_hidden(subdir34, root) -+ assert is_hidden(subdir34) ++ assert is_hidden(subdir34, root) == True ++ assert is_hidden(subdir34) == True subdir56 = os.path.join(root, '.subdir5', 'subdir6') os.makedirs(subdir56) @@ -553,30 +677,28 @@ - nt.assert_equal(is_hidden(subdir56), True) - nt.assert_equal(is_file_hidden(subdir56), False) - nt.assert_equal(is_file_hidden(subdir56, os.stat(subdir56)), False) -+ assert is_hidden(subdir56, root) -+ assert is_hidden(subdir56) -+ assert not is_file_hidden(subdir56) -+ assert not is_file_hidden(subdir56, os.stat(subdir56)) ++ assert is_hidden(subdir56, root) == True ++ assert is_hidden(subdir56) == True ++ assert is_file_hidden(subdir56) == False ++ assert is_file_hidden(subdir56, os.stat(subdir56)) == False - @skip_if_not_win32 +-@skip_if_not_win32 ++@pytest.mark.skipif(sys.platform != "win32", reason="run on windows only") def test_is_hidden_win32(): -@@ -92,4 +90,3 @@ def test_is_hidden_win32(): - print(r) - assert is_hidden(subdir1, root) - assert is_file_hidden(subdir1) -- ---- a/setup.cfg -+++ b/setup.cfg -@@ -3,12 +3,3 @@ universal=0 - - [metadata] - license_file = LICENSE -- --[nosetests] --warningfilters=module |.* |DeprecationWarning |notebook.* -- default |.* | Warning | notebook.* -- ignore |.*metadata.* |DeprecationWarning |notebook.* -- ignore |.*schema.* |UserWarning |nbfor.* -- ignore |The 'warn' method is deprecated, use 'warning' instead | DeprecationWarning |notebook.* -- error |encodestring\(\) is a deprecated alias, use encodebytes\(\)| DeprecationWarning | notebook.* -- error |decodestring\(\) is a .*| DeprecationWarning | notebook.* + with TemporaryDirectory() as root: + root = cast_unicode(root) +Index: notebook-6.1.5/setup.py +=================================================================== +--- notebook-6.1.5.orig/setup.py ++++ notebook-6.1.5/setup.py +@@ -116,8 +116,8 @@ for more information. + 'prometheus_client' + ], + extras_require = { +- 'test': ['nose', 'coverage', 'requests', 'nose_warnings_filters', +- 'nbval', 'nose-exclude', 'selenium', 'pytest', 'pytest-cov'], ++ 'test': ['coverage', 'requests', ++ 'nbval', 'selenium', 'pytest', 'pytest-cov'], + 'docs': ['sphinx', 'nbsphinx', 'sphinxcontrib_github_alt'], + 'test:sys_platform != "win32"': ['requests-unixsocket'], + },