Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-jupyter-server for
openSUSE:Factory checked in at 2022-01-04 19:37:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jupyter-server (Old)
and /work/SRC/openSUSE:Factory/.python-jupyter-server.new.1896 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-jupyter-server"
Tue Jan 4 19:37:38 2022 rev:20 rq:943591 version:1.13.1
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-jupyter-server/python-jupyter-server.changes
2021-11-17 01:14:46.966184007 +0100
+++
/work/SRC/openSUSE:Factory/.python-jupyter-server.new.1896/python-jupyter-server.changes
2022-01-04 19:37:48.933955647 +0100
@@ -1,0 +2,29 @@
+Fri Dec 31 15:55:54 UTC 2021 - Ben Greiner <[email protected]>
+
+- Update to 1.13.1
+ * nudge both the shell and control channels #636 (@Zsailer)
+ * Persistent session storage #614 (@Zsailer)
+ * Nudge on the control channel instead of the shell #628
+ (@JohanMabille)
+ * Clean up downstream tests #629 (@blink1073)
+ * Clean up version info handling #620 (@blink1073)
+ * Await _finish_kernel_start #617 (@jtpio)
+ * Update to Python 3.10 in the CI workflows #618 (@jtpio)
+ * Use maintainer-tools base setup action #616 (@blink1073)
+ * Consistent logging method #607 (@mwakaba2)
+ * Use pending kernels #593 (@blink1073)
+ * Set xsrf cookie on base url #612 (@minrk)
+ * Update jpserver_extensions trait to work with traitlets 5.x
+ #610 (@Zsailer)
+ * Fix allow_origin_pat property to properly parse regex #603
+ (@havok2063)
+ * Enforce labels on PRs #613 (@blink1073)
+ * Normalize file name and path in test_api #608 (@toonn)
+
+-------------------------------------------------------------------
+Mon Nov 15 18:42:07 UTC 2021 - Ben Greiner <[email protected]>
+
+- Multibuild :test flavor, avoid possible buildcycles
+- Fix libalternatives in test flavor
+
+-------------------------------------------------------------------
Old:
----
jupyter_server-1.11.2.tar.gz
New:
----
_multibuild
jupyter_server-1.13.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-jupyter-server.spec ++++++
--- /var/tmp/diff_new_pack.k5UPm6/_old 2022-01-04 19:37:49.509956401 +0100
+++ /var/tmp/diff_new_pack.k5UPm6/_new 2022-01-04 19:37:49.513956406 +0100
@@ -1,5 +1,5 @@
#
-# spec file for package python-jupyter-server
+# spec file
#
# Copyright (c) 2021 SUSE LLC
#
@@ -17,6 +17,15 @@
#
+%global flavor @BUILD_FLAVOR@%{nil}
+%if "%{flavor}" == "test"
+%define psuffix -test
+%bcond_without test
+%else
+%define psuffix %{nil}
+%bcond_with test
+%endif
+
%if 0%{?suse_version} > 1500
%bcond_without libalternatives
%else
@@ -25,8 +34,8 @@
%{?!python_module:%define python_module() python3-%{**}}
%define skip_python2 1
-Name: python-jupyter-server
-Version: 1.11.2
+Name: python-jupyter-server%{psuffix}
+Version: 1.13.1
Release: 0
Summary: The backend to Jupyter web applications
License: BSD-3-Clause
@@ -34,23 +43,9 @@
URL: https://github.com/jupyter-server/jupyter_server
# need the release tarball for the static stylesheets
Source:
https://github.com/jupyter-server/jupyter_server/releases/download/v%{version}/jupyter_server-%{version}.tar.gz
-BuildRequires: %{python_module Jinja2}
-BuildRequires: %{python_module Send2Trash}
-BuildRequires: %{python_module anyio >= 3.1.0}
-BuildRequires: %{python_module argon2-cffi}
-BuildRequires: %{python_module ipython_genutils}
-BuildRequires: %{python_module jupyter-client >= 6.1.1}
-BuildRequires: %{python_module jupyter-core >= 4.4.0}
BuildRequires: %{python_module jupyter_packaging}
-BuildRequires: %{python_module nbconvert}
-BuildRequires: %{python_module nbformat}
-BuildRequires: %{python_module prometheus_client}
-BuildRequires: %{python_module pyzmq >= 17}
BuildRequires: %{python_module setuptools}
-BuildRequires: %{python_module terminado >= 0.8.3}
-BuildRequires: %{python_module tornado >= 6.1}
-BuildRequires: %{python_module traitlets >= 4.2.1}
-BuildRequires: %{python_module websocket-client}
+
# We need the full stdlib
BuildRequires: %{pythons}
BuildRequires: fdupes
@@ -79,14 +74,9 @@
%endif
Provides: python-jupyter_server = %{version}-%{release}
Obsoletes: python-jupyter_server < %{version}-%{release}
-# SECTION extras_require test
-BuildRequires: %{python_module ipykernel}
-BuildRequires: %{python_module pytest >= 6}
-BuildRequires: %{python_module pytest-console-scripts}
-BuildRequires: %{python_module pytest-mock}
-BuildRequires: %{python_module pytest-tornasync}
-BuildRequires: %{python_module requests}
-# /SECTION
+%if %{with test}
+BuildRequires: %{python_module jupyter-server-test = %{version}}
+%endif
%if "%{python_flavor}" == "python3" || "%{python_provides}" == "python3"
Provides: jupyter-jupyter-server = %{version}-%{release}
Obsoletes: jupyter-jupyter-server < %{version}-%{release}
@@ -117,6 +107,7 @@
%prep
%setup -q -n jupyter_server-%{version}
+%if ! %{with test}
%build
%python_build
@@ -124,11 +115,22 @@
%python_install
%python_clone -a %{buildroot}%{_bindir}/jupyter-server
%python_expand %fdupes %{buildroot}%{$python_sitelib}
+%endif
+%if %{with test}
%check
%{python_expand # provide u-a entrypoints in the correct flavor version --
installed packages and jupyter-server
-mkdir build/testbin
-for bin in %{_bindir}/*-%{$python_bin_suffix}
%{buildroot}%{_bindir}/*-%{$python_bin_suffix} ; do
+mkdir -p build/xdgflavorconfig
+export XDG_CONFIG_HOME=$PWD/build/xdgflavorconfig
+if [ -d /usr/share/libalternatives/ ]; then
+ for b in /usr/share/libalternatives/*; do
+ if [ -e "${b}/%{$python_version_nodots}.conf" ]; then
+ alts -n $(basename ${b}) -p %{$python_version_nodots}
+ fi
+ done
+fi
+mkdir -p build/testbin
+for bin in %{_bindir}/*-%{$python_bin_suffix}; do
# four percent into 1 by rpm/python expansions
ln -s ${bin} build/testbin/$(basename ${bin%%%%-%{$python_bin_suffix}})
done
@@ -140,7 +142,9 @@
echo "You might need to delete ~/.local/share/jupyter in order to avoid
test failures."
fi
%pytest jupyter_server
+%endif
+%if ! %{with test}
%pre
# If libalternatives is used: Removing old update-alternatives entries.
%python_libalternatives_reset_alternative jupyter-server
@@ -160,5 +164,6 @@
%files %{python_files test}
%license COPYING.md
+%endif
%changelog
++++++ _multibuild ++++++
<multibuild>
<package>test</package>
</multibuild>
(No newline at EOF)
++++++ jupyter_server-1.11.2.tar.gz -> jupyter_server-1.13.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.11.2/CHANGELOG.md
new/jupyter_server-1.13.1/CHANGELOG.md
--- old/jupyter_server-1.11.2/CHANGELOG.md 2021-11-01 21:22:56.000000000
+0100
+++ new/jupyter_server-1.13.1/CHANGELOG.md 2021-12-09 20:36:05.000000000
+0100
@@ -4,6 +4,94 @@
<!-- <START NEW CHANGELOG ENTRY> -->
+## 1.13.1
+
+([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.13.0...affd5d9a2e6d718baa2185518256f51921fd4484))
+
+### Bugs fixed
+
+- nudge both the shell and control channels
[#636](https://github.com/jupyter-server/jupyter_server/pull/636)
([@Zsailer](https://github.com/Zsailer))
+
+### Maintenance and upkeep improvements
+
+- Fix macos pypy check
[#632](https://github.com/jupyter-server/jupyter_server/pull/632)
([@blink1073](https://github.com/blink1073))
+
+### Contributors to this release
+
+([GitHub contributors page for this
release](https://github.com/jupyter-server/jupyter_server/graphs/contributors?from=2021-12-06&to=2021-12-09&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2021-12-06..2021-12-09&type=Issues)
|
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2021-12-06..2021-12-09&type=Issues)
|
[@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AZsailer+updated%3A2021-12-06..2021-12-09&type=Issues)
+
+<!-- <END NEW CHANGELOG ENTRY> -->
+
+## 1.13.0
+
+([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.12.1...b51969f16f04375d52cb029d72f90174141c760d))
+
+### Enhancements made
+
+- Persistent session storage
[#614](https://github.com/jupyter-server/jupyter_server/pull/614)
([@Zsailer](https://github.com/Zsailer))
+
+### Bugs fixed
+
+- Nudge on the control channel instead of the shell
[#628](https://github.com/jupyter-server/jupyter_server/pull/628)
([@JohanMabille](https://github.com/JohanMabille))
+
+### Maintenance and upkeep improvements
+
+- Clean up downstream tests
[#629](https://github.com/jupyter-server/jupyter_server/pull/629)
([@blink1073](https://github.com/blink1073))
+- Clean up version info handling
[#620](https://github.com/jupyter-server/jupyter_server/pull/620)
([@blink1073](https://github.com/blink1073))
+
+### Contributors to this release
+
+([GitHub contributors page for this
release](https://github.com/jupyter-server/jupyter_server/graphs/contributors?from=2021-11-26&to=2021-12-06&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2021-11-26..2021-12-06&type=Issues)
|
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2021-11-26..2021-12-06&type=Issues)
|
[@echarles](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Aecharles+updated%3A2021-11-26..2021-12-06&type=Issues)
|
[@JohanMabille](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AJohanMabille+updated%3A2021-11-26..2021-12-06&type=Issues)
|
[@jtpio](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ajtpio+updated%3A2021-11-26..2021-12-06&type=Issues)
|
[@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AZsailer+updated%3A2021-11-26..2021-12-06&type=Issues)
+
+## 1.12.1
+
+([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.12.0...ead83374b3b874bdf4ea47fca5aee1ecb5940a85))
+
+### Bugs fixed
+
+- Await `_finish_kernel_start`
[#617](https://github.com/jupyter-server/jupyter_server/pull/617)
([@jtpio](https://github.com/jtpio))
+
+### Maintenance and upkeep improvements
+
+- Update to Python 3.10 in the CI workflows
[#618](https://github.com/jupyter-server/jupyter_server/pull/618)
([@jtpio](https://github.com/jtpio))
+- Use `maintainer-tools` base setup action
[#616](https://github.com/jupyter-server/jupyter_server/pull/616)
([@blink1073](https://github.com/blink1073))
+
+### Contributors to this release
+
+([GitHub contributors page for this
release](https://github.com/jupyter-server/jupyter_server/graphs/contributors?from=2021-11-23&to=2021-11-26&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2021-11-23..2021-11-26&type=Issues)
|
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2021-11-23..2021-11-26&type=Issues)
|
[@jtpio](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ajtpio+updated%3A2021-11-23..2021-11-26&type=Issues)
+
+## 1.12.0
+
+([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.11.2...758dba6f8873f60c1ca41057b4be108da5a6ff1a))
+
+### Enhancements made
+
+- Consistent logging method
[#607](https://github.com/jupyter-server/jupyter_server/pull/607)
([@mwakaba2](https://github.com/mwakaba2))
+- Use pending kernels
[#593](https://github.com/jupyter-server/jupyter_server/pull/593)
([@blink1073](https://github.com/blink1073))
+
+### Bugs fixed
+
+- Set `xsrf` cookie on base url
[#612](https://github.com/jupyter-server/jupyter_server/pull/612)
([@minrk](https://github.com/minrk))
+- Update `jpserver_extensions` trait to work with `traitlets` 5.x
[#610](https://github.com/jupyter-server/jupyter_server/pull/610)
([@Zsailer](https://github.com/Zsailer))
+- Fix `allow_origin_pat` property to properly parse regex
[#603](https://github.com/jupyter-server/jupyter_server/pull/603)
([@havok2063](https://github.com/havok2063))
+
+### Maintenance and upkeep improvements
+
+- Enforce labels on PRs
[#613](https://github.com/jupyter-server/jupyter_server/pull/613)
([@blink1073](https://github.com/blink1073))
+- Normalize file name and path in `test_api`
[#608](https://github.com/jupyter-server/jupyter_server/pull/608)
([@toonn](https://github.com/toonn))
+
+### Contributors to this release
+
+([GitHub contributors page for this
release](https://github.com/jupyter-server/jupyter_server/graphs/contributors?from=2021-11-01&to=2021-11-23&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2021-11-01..2021-11-23&type=Issues)
|
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2021-11-01..2021-11-23&type=Issues)
|
[@havok2063](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ahavok2063+updated%3A2021-11-01..2021-11-23&type=Issues)
|
[@minrk](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Aminrk+updated%3A2021-11-01..2021-11-23&type=Issues)
|
[@mwakaba2](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Amwakaba2+updated%3A2021-11-01..2021-11-23&type=Issues)
|
[@toonn](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Atoonn+updated%3A2021-11-01..2021-11-23&type=Issues)
|
[@welcome](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Awelcome+updated%
3A2021-11-01..2021-11-23&type=Issues) |
[@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AZsailer+updated%3A2021-11-01..2021-11-23&type=Issues)
+
## 1.11.2
([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.11.1...fda4cc5a96703bb4e871a5a622ef6031c7f6385b))
@@ -27,8 +115,6 @@
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2021-10-04..2021-11-01&type=Issues)
|
[@dependabot](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Adependabot+updated%3A2021-10-04..2021-11-01&type=Issues)
|
[@kevin-bates](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Akevin-bates+updated%3A2021-10-04..2021-11-01&type=Issues)
|
[@stdll00](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Astdll00+updated%3A2021-10-04..2021-11-01&type=Issues)
|
[@welcome](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Awelcome+updated%3A2021-10-04..2021-11-01&type=Issues)
|
[@Wh1isper](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AWh1isper+updated%3A2021-10-04..2021-11-01&type=Issues)
|
[@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AZs
ailer+updated%3A2021-10-04..2021-11-01&type=Issues)
-<!-- <END NEW CHANGELOG ENTRY> -->
-
## 1.11.1
([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.11.0...f4c3889658c1daad1d8966438d1f1b98b3f60641))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.11.2/PKG-INFO
new/jupyter_server-1.13.1/PKG-INFO
--- old/jupyter_server-1.11.2/PKG-INFO 2021-11-01 21:23:33.281527500 +0100
+++ new/jupyter_server-1.13.1/PKG-INFO 2021-12-09 20:36:35.956748700 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: jupyter_server
-Version: 1.11.2
+Version: 1.13.1
Summary: The backend???i.e. core services, APIs, and REST endpoints???to
Jupyter web applications.
Home-page: https://jupyter.org
Author: Jupyter Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.11.2/jupyter_server/_version.py
new/jupyter_server-1.13.1/jupyter_server/_version.py
--- old/jupyter_server-1.11.2/jupyter_server/_version.py 2021-11-01
21:23:18.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/_version.py 2021-12-09
20:36:21.000000000 +0100
@@ -2,15 +2,5 @@
store the current version info of the server.
"""
-import re
-
-# Version string must appear intact for tbump versioning
-__version__ = "1.11.2"
-
-# Build up version_info tuple for backwards compatibility
-pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
-match = re.match(pattern, __version__)
-parts = [int(match[part]) for part in ["major", "minor", "patch"]]
-if match["rest"]:
- parts.append(match["rest"])
-version_info = tuple(parts)
+version_info = (1, 13, 1, "", "")
+__version__ = ".".join(map(str, version_info[:3])) + "".join(version_info[3:])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.11.2/jupyter_server/auth/login.py
new/jupyter_server-1.13.1/jupyter_server/auth/login.py
--- old/jupyter_server-1.11.2/jupyter_server/auth/login.py 2021-11-01
21:22:56.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/auth/login.py 2021-12-09
20:36:05.000000000 +0100
@@ -53,7 +53,7 @@
if self.allow_origin:
allow = self.allow_origin == origin
elif self.allow_origin_pat:
- allow = bool(self.allow_origin_pat.match(origin))
+ allow = bool(re.match(self.allow_origin_pat, origin))
if not allow:
# not allowed, use default
self.log.warning("Not allowing login redirect to %r" % url)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/base/handlers.py
new/jupyter_server-1.13.1/jupyter_server/base/handlers.py
--- old/jupyter_server-1.11.2/jupyter_server/base/handlers.py 2021-11-01
21:22:56.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/base/handlers.py 2021-12-09
20:36:05.000000000 +0100
@@ -309,7 +309,7 @@
self.set_header("Access-Control-Allow-Origin", self.allow_origin)
elif self.allow_origin_pat:
origin = self.get_origin()
- if origin and self.allow_origin_pat.match(origin):
+ if origin and re.match(self.allow_origin_pat, origin):
self.set_header("Access-Control-Allow-Origin", origin)
elif self.token_authenticated and "Access-Control-Allow-Origin" not in
self.settings.get(
"headers", {}
@@ -382,7 +382,7 @@
if self.allow_origin:
allow = self.allow_origin == origin
elif self.allow_origin_pat:
- allow = bool(self.allow_origin_pat.match(origin))
+ allow = bool(re.match(self.allow_origin_pat, origin))
else:
# No CORS headers deny the request
allow = False
@@ -427,7 +427,7 @@
if self.allow_origin:
allow = self.allow_origin == origin
elif self.allow_origin_pat:
- allow = bool(self.allow_origin_pat.match(origin))
+ allow = bool(re.match(self.allow_origin_pat, origin))
else:
# No CORS settings, deny the request
allow = False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/base/zmqhandlers.py
new/jupyter_server-1.13.1/jupyter_server/base/zmqhandlers.py
--- old/jupyter_server-1.11.2/jupyter_server/base/zmqhandlers.py
2021-11-01 21:22:56.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/base/zmqhandlers.py
2021-12-09 20:36:05.000000000 +0100
@@ -3,6 +3,7 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import json
+import re
import struct
import sys
from urllib.parse import urlparse
@@ -139,7 +140,7 @@
if self.allow_origin:
allow = self.allow_origin == origin
elif self.allow_origin_pat:
- allow = bool(self.allow_origin_pat.match(origin))
+ allow = bool(re.match(self.allow_origin_pat, origin))
else:
# No CORS headers deny the request
allow = False
@@ -176,6 +177,10 @@
self.ping_callback.stop()
return
+ if self.ws_connection.client_terminated:
+ self.close()
+ return
+
# check for timeout on pong. Make sure that we really have sent a
recent ping in
# case the machine with both server and client has been suspended
since the last ping.
now = ioloop.IOLoop.current().time()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/pytest_plugin.py
new/jupyter_server-1.13.1/jupyter_server/pytest_plugin.py
--- old/jupyter_server-1.11.2/jupyter_server/pytest_plugin.py 2021-11-01
21:22:56.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/pytest_plugin.py 2021-12-09
20:36:05.000000000 +0100
@@ -416,13 +416,16 @@
@pytest.fixture
def jp_kernelspecs(jp_data_dir):
"""Configures some sample kernelspecs in the Jupyter data directory."""
- spec_names = ["sample", "sample 2"]
+ spec_names = ["sample", "sample 2", "bad"]
for name in spec_names:
sample_kernel_dir = jp_data_dir.joinpath("kernels", name)
sample_kernel_dir.mkdir(parents=True)
# Create kernel json file
sample_kernel_file = sample_kernel_dir.joinpath("kernel.json")
- sample_kernel_file.write_text(json.dumps(sample_kernel_json))
+ kernel_json = sample_kernel_json.copy()
+ if name == "bad":
+ kernel_json["argv"] = ["non_existent_path"]
+ sample_kernel_file.write_text(json.dumps(kernel_json))
# Create resources text
sample_kernel_resources = sample_kernel_dir.joinpath("resource.txt")
sample_kernel_resources.write_text(some_resource)
@@ -474,12 +477,24 @@
terminal_cleanup =
jp_serverapp.web_app.settings["terminal_manager"].terminate_all
kernel_cleanup = jp_serverapp.kernel_manager.shutdown_all
if asyncio.iscoroutinefunction(terminal_cleanup):
- await terminal_cleanup()
+ try:
+ await terminal_cleanup()
+ except Exception as e:
+ print(e)
else:
- terminal_cleanup()
+ try:
+ await terminal_cleanup()
+ except Exception as e:
+ print(e)
if asyncio.iscoroutinefunction(kernel_cleanup):
- await kernel_cleanup()
+ try:
+ await kernel_cleanup()
+ except Exception as e:
+ print(e)
else:
- kernel_cleanup()
+ try:
+ kernel_cleanup()
+ except Exception as e:
+ print(e)
return _
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.11.2/jupyter_server/serverapp.py
new/jupyter_server-1.13.1/jupyter_server/serverapp.py
--- old/jupyter_server-1.11.2/jupyter_server/serverapp.py 2021-11-01
21:22:56.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/serverapp.py 2021-12-09
20:36:05.000000000 +0100
@@ -356,6 +356,10 @@
# allow custom overrides for the tornado web app.
settings.update(settings_overrides)
+
+ if base_url and "xsrf_cookie_kwargs" not in settings:
+ # default: set xsrf cookie on base_url
+ settings["xsrf_cookie_kwargs"] = {"path": base_url}
return settings
def init_handlers(self, default_services, settings):
@@ -558,7 +562,7 @@
pass
def start(self):
- servers = list(list_running_servers(self.runtime_dir))
+ servers = list(list_running_servers(self.runtime_dir, log=self.log))
if not servers:
self.exit("There are no running servers (per %s)" %
self.runtime_dir)
for server in servers:
@@ -619,7 +623,7 @@
)
def start(self):
- serverinfo_list = list(list_running_servers(self.runtime_dir))
+ serverinfo_list = list(list_running_servers(self.runtime_dir,
log=self.log))
if self.jsonlist:
print(json.dumps(serverinfo_list, indent=2))
elif self.json:
@@ -1590,7 +1594,8 @@
self.server_extensions = change["new"]
jpserver_extensions = Dict(
- {},
+ default_value={},
+ value_trait=Bool(),
config=True,
help=(
_i18n(
@@ -2682,7 +2687,7 @@
self.io_loop.add_callback(self._stop)
-def list_running_servers(runtime_dir=None):
+def list_running_servers(runtime_dir=None, log=None):
"""Iterate over the server info files of running Jupyter servers.
Given a runtime directory, find jpserver-* files in the security directory,
@@ -2709,8 +2714,9 @@
# If the process has died, try to delete its info file
try:
os.unlink(os.path.join(runtime_dir, file_name))
- except OSError:
- pass # TODO: This should warn or log or something
+ except OSError as e:
+ if log:
+ log.warning(_i18n("Deleting server info file failed:
%s.") % e)
# -----------------------------------------------------------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/services/kernels/handlers.py
new/jupyter_server-1.13.1/jupyter_server/services/kernels/handlers.py
--- old/jupyter_server-1.11.2/jupyter_server/services/kernels/handlers.py
2021-11-01 21:22:56.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/services/kernels/handlers.py
2021-12-09 20:36:05.000000000 +0100
@@ -5,8 +5,8 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import json
-import logging
from textwrap import dedent
+from traceback import format_tb
from ipython_genutils.py3compat import cast_unicode
from jupyter_client import protocol_version as client_protocol_version
@@ -79,7 +79,10 @@
try:
await km.restart_kernel(kernel_id)
except Exception as e:
- self.log.error("Exception restarting kernel", exc_info=True)
+ message = "Exception restarting kernel"
+ self.log.error(message, exc_info=True)
+ traceback = format_tb(e.__traceback__)
+ self.write(json.dumps(dict(message=message,
traceback=traceback)))
self.set_status(500)
else:
model = await ensure_async(km.kernel_model(kernel_id))
@@ -128,7 +131,7 @@
def nudge(self):
"""Nudge the zmq connections with kernel_info_requests
Returns a Future that will resolve when we have received
- a shell reply and at least one iopub message,
+ a shell or control reply and at least one iopub message,
ensuring that zmq subscriptions are established,
sockets are fully connected, and kernel is responsive.
Keeps retrying kernel_info_request until these are both received.
@@ -146,10 +149,12 @@
f = Future()
f.set_result(None)
return f
-
# Use a transient shell channel to prevent leaking
# shell responses to the front-end.
shell_channel = kernel.connect_shell()
+ # Use a transient control channel to prevent leaking
+ # control responses to the front-end.
+ control_channel = kernel.connect_control()
# The IOPub used by the client, whose subscriptions we are verifying.
iopub_channel = self.channels["iopub"]
@@ -171,6 +176,8 @@
iopub_channel.stop_on_recv()
if not shell_channel.closed():
shell_channel.close()
+ if not control_channel.closed():
+ control_channel.close()
# trigger cleanup when both message futures are resolved
both_done.add_done_callback(cleanup)
@@ -181,6 +188,12 @@
self.log.debug("Nudge: resolving shell future: %s",
self.kernel_id)
info_future.set_result(None)
+ def on_control_reply(msg):
+ self.log.debug("Nudge: control info reply received: %s",
self.kernel_id)
+ if not info_future.done():
+ self.log.debug("Nudge: resolving control future: %s",
self.kernel_id)
+ info_future.set_result(None)
+
def on_iopub(msg):
self.log.debug("Nudge: IOPub received: %s", self.kernel_id)
if not iopub_future.done():
@@ -190,6 +203,7 @@
iopub_channel.on_recv(on_iopub)
shell_channel.on_recv(on_shell_reply)
+ control_channel.on_recv(on_control_reply)
loop = IOLoop.current()
# Nudge the kernel with kernel info requests until we get an IOPub
message
@@ -215,10 +229,17 @@
finish()
return
+ # check for closed zmq socket
+ if control_channel.closed():
+ self.log.debug("Nudge: cancelling on closed zmq socket: %s",
self.kernel_id)
+ finish()
+ return
+
if not both_done.done():
log = self.log.warning if count % 10 == 0 else self.log.debug
log("Nudge: attempt %s on kernel %s" % (count, self.kernel_id))
self.session.send(shell_channel, "kernel_info_request")
+ self.session.send(control_channel, "kernel_info_request")
nonlocal nudge_handle
nudge_handle = loop.call_later(0.5, nudge, count)
@@ -326,6 +347,15 @@
# We don't want to wait forever, because browsers don't take it well
when
# servers never respond to websocket connection requests.
kernel = self.kernel_manager.get_kernel(self.kernel_id)
+
+ if hasattr(kernel, "ready"):
+ try:
+ await kernel.ready
+ except Exception as e:
+ kernel.execution_state = "dead"
+ kernel.reason = str(e)
+ raise web.HTTPError(500, str(e)) from e
+
self.session.key = kernel.session.key
future = self.request_kernel_info()
@@ -446,6 +476,7 @@
def _on_zmq_reply(self, stream, msg_list):
idents, fed_msg_list = self.session.feed_identities(msg_list)
msg = self.session.deserialize(fed_msg_list)
+
parent = msg["parent_header"]
def write_stderr(error_message):
@@ -624,11 +655,11 @@
self.write_message(json.dumps(msg, default=json_default))
def on_kernel_restarted(self):
- logging.warn("kernel %s restarted", self.kernel_id)
+ self.log.warning("kernel %s restarted", self.kernel_id)
self._send_status_message("restarting")
def on_restart_failed(self):
- logging.error("kernel %s restarted failed!", self.kernel_id)
+ self.log.error("kernel %s restarted failed!", self.kernel_id)
self._send_status_message("dead")
def _on_error(self, msg):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/services/kernels/kernelmanager.py
new/jupyter_server-1.13.1/jupyter_server/services/kernels/kernelmanager.py
--- old/jupyter_server-1.11.2/jupyter_server/services/kernels/kernelmanager.py
2021-11-01 21:22:56.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/services/kernels/kernelmanager.py
2021-12-09 20:36:05.000000000 +0100
@@ -5,6 +5,7 @@
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
+import asyncio
import os
from collections import defaultdict
from datetime import datetime
@@ -209,16 +210,16 @@
kwargs["kernel_id"] = kernel_id
kernel_id = await
ensure_async(self.pinned_superclass.start_kernel(self, **kwargs))
self._kernel_connections[kernel_id] = 0
- self._kernel_ports[kernel_id] = self._kernels[kernel_id].ports
- self.start_watching_activity(kernel_id)
+ fut = asyncio.ensure_future(self._finish_kernel_start(kernel_id))
+ if not getattr(self, "use_pending_kernels", None):
+ await fut
+ # add busy/activity markers:
+ kernel = self.get_kernel(kernel_id)
+ kernel.execution_state = "starting"
+ kernel.reason = ""
+ kernel.last_activity = utcnow()
self.log.info("Kernel started: %s" % kernel_id)
self.log.debug("Kernel args: %r" % kwargs)
- # register callback for failed auto-restart
- self.add_restart_callback(
- kernel_id,
- lambda: self._handle_kernel_died(kernel_id),
- "dead",
- )
# Increase the metric of number of kernels running
# for the relevant kernel type by 1
@@ -233,6 +234,24 @@
return kernel_id
+ async def _finish_kernel_start(self, kernel_id):
+ km = self.get_kernel(kernel_id)
+ if hasattr(km, "ready"):
+ try:
+ await km.ready
+ except Exception:
+ self.log.exception(km.ready.exception())
+ return
+
+ self._kernel_ports[kernel_id] = km.ports
+ self.start_watching_activity(kernel_id)
+ # register callback for failed auto-restart
+ self.add_restart_callback(
+ kernel_id,
+ lambda: self._handle_kernel_died(kernel_id),
+ "dead",
+ )
+
def ports_changed(self, kernel_id):
"""Used by ZMQChannelsHandler to determine how to coordinate nudge and
replays.
@@ -448,6 +467,8 @@
"execution_state": kernel.execution_state,
"connections": self._kernel_connections.get(kernel_id, 0),
}
+ if getattr(kernel, "reason", None):
+ model["reason"] = kernel.reason
return model
def list_kernels(self):
@@ -479,6 +500,7 @@
kernel = self._kernels[kernel_id]
# add busy/activity markers:
kernel.execution_state = "starting"
+ kernel.reason = ""
kernel.last_activity = utcnow()
kernel._activity_stream = kernel.connect_iopub()
session = Session(
@@ -507,7 +529,7 @@
def stop_watching_activity(self, kernel_id):
"""Stop watching IOPub messages on a kernel for activity."""
kernel = self._kernels[kernel_id]
- if kernel._activity_stream:
+ if getattr(kernel, "_activity_stream", None):
kernel._activity_stream.close()
kernel._activity_stream = None
@@ -561,6 +583,17 @@
async def cull_kernel_if_idle(self, kernel_id):
kernel = self._kernels[kernel_id]
+
+ if getattr(kernel, "execution_state") == "dead":
+ self.log.warning(
+ "Culling '%s' dead kernel '%s' (%s).",
+ kernel.execution_state,
+ kernel.kernel_name,
+ kernel_id,
+ )
+ await ensure_async(self.shutdown_kernel(kernel_id))
+ return
+
if hasattr(
kernel, "last_activity"
): # last_activity is monkey-patched, so ensure that has occurred
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/services/sessions/handlers.py
new/jupyter_server-1.13.1/jupyter_server/services/sessions/handlers.py
--- old/jupyter_server-1.11.2/jupyter_server/services/sessions/handlers.py
2021-11-01 21:22:56.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/services/sessions/handlers.py
2021-12-09 20:36:05.000000000 +0100
@@ -4,6 +4,7 @@
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
+import asyncio
import json
try:
@@ -78,6 +79,8 @@
self.set_status(501)
self.finish(json.dumps(dict(message=msg,
short_message=status_msg)))
return
+ except Exception as e:
+ raise web.HTTPError(500, str(e)) from e
location = url_path_join(self.base_url, "api", "sessions", model["id"])
self.set_header("Location", location)
@@ -144,7 +147,10 @@
if model["kernel"]["id"] != before["kernel"]["id"]:
# kernel_id changed because we got a new kernel
# shutdown the old one
- await ensure_async(km.shutdown_kernel(before["kernel"]["id"]))
+ fut =
asyncio.ensure_future(ensure_async(km.shutdown_kernel(before["kernel"]["id"])))
+ # If we are not using pending kernels, wait for the kernel to shut
down
+ if not getattr(km, "use_pending_kernels", None):
+ await fut
self.finish(json.dumps(model, default=json_default))
@web.authenticated
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/services/sessions/sessionmanager.py
new/jupyter_server-1.13.1/jupyter_server/services/sessions/sessionmanager.py
---
old/jupyter_server-1.11.2/jupyter_server/services/sessions/sessionmanager.py
2021-11-01 21:22:56.000000000 +0100
+++
new/jupyter_server-1.13.1/jupyter_server/services/sessions/sessionmanager.py
2021-12-09 20:36:05.000000000 +0100
@@ -1,6 +1,7 @@
"""A base class session manager."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
+import pathlib
import uuid
try:
@@ -13,6 +14,9 @@
from traitlets.config.configurable import LoggingConfigurable
from traitlets import Instance
+from traitlets import Unicode
+from traitlets import validate
+from traitlets import TraitError
from jupyter_server.utils import ensure_async
from jupyter_server.traittypes import InstanceFromClasses
@@ -20,6 +24,36 @@
class SessionManager(LoggingConfigurable):
+ database_filepath = Unicode(
+ default_value=":memory:",
+ help=(
+ "Th filesystem path to SQLite Database file "
+ "(e.g. /path/to/session_database.db). By default, the session "
+ "database is stored in-memory (i.e. `:memory:` setting from
sqlite3) "
+ "and does not persist when the current Jupyter Server shuts down."
+ ),
+ ).tag(config=True)
+
+ @validate("database_filepath")
+ def _validate_database_filepath(self, proposal):
+ value = proposal["value"]
+ if value == ":memory:":
+ return value
+ path = pathlib.Path(value)
+ if path.exists():
+ # Verify that the database path is not a directory.
+ if path.is_dir():
+ raise TraitError(
+ "`database_filepath` expected a file path, but the given
path is a directory."
+ )
+ # Verify that database path is an SQLite 3 Database by checking
its header.
+ with open(value, "rb") as f:
+ header = f.read(100)
+
+ if not header.startswith(b"SQLite format 3") and not header == b"":
+ raise TraitError("The given file is not an SQLite database
file.")
+ return value
+
kernel_manager =
Instance("jupyter_server.services.kernels.kernelmanager.MappingKernelManager")
contents_manager = InstanceFromClasses(
[
@@ -39,7 +73,7 @@
if self._cursor is None:
self._cursor = self.connection.cursor()
self._cursor.execute(
- """CREATE TABLE session
+ """CREATE TABLE IF NOT EXISTS session
(session_id, path, name, type, kernel_id)"""
)
return self._cursor
@@ -48,7 +82,8 @@
def connection(self):
"""Start a database connection"""
if self._connection is None:
- self._connection = sqlite3.connect(":memory:")
+ # Set isolation level to None to autocommit all changes to the
database.
+ self._connection = sqlite3.connect(self.database_filepath,
isolation_level=None)
self._connection.row_factory = sqlite3.Row
return self._connection
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/tests/services/contents/test_api.py
new/jupyter_server-1.13.1/jupyter_server/tests/services/contents/test_api.py
---
old/jupyter_server-1.11.2/jupyter_server/tests/services/contents/test_api.py
2021-11-01 21:22:56.000000000 +0100
+++
new/jupyter_server-1.13.1/jupyter_server/tests/services/contents/test_api.py
2021-12-09 20:36:05.000000000 +0100
@@ -3,6 +3,7 @@
import sys
from base64 import decodebytes
from base64 import encodebytes
+from unicodedata import normalize
import pytest
import tornado
@@ -101,8 +102,8 @@
data = json.loads(response.body.decode())
nbs = notebooks_only(data)
assert len(nbs) > 0
- assert name + ".ipynb" in [n["name"] for n in nbs]
- assert url_path_join(path, name + ".ipynb") in [n["path"] for n in nbs]
+ assert name + ".ipynb" in [normalize("NFC", n["name"]) for n in nbs]
+ assert url_path_join(path, name + ".ipynb") in [normalize("NFC",
n["path"]) for n in nbs]
@pytest.mark.parametrize("path,name", dirs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/tests/services/kernels/test_api.py
new/jupyter_server-1.13.1/jupyter_server/tests/services/kernels/test_api.py
--- old/jupyter_server-1.11.2/jupyter_server/tests/services/kernels/test_api.py
2021-11-01 21:22:56.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server/tests/services/kernels/test_api.py
2021-12-09 20:36:05.000000000 +0100
@@ -4,13 +4,29 @@
import pytest
import tornado
from jupyter_client.kernelspec import NATIVE_KERNEL_NAME
+from tornado.httpclient import HTTPClientError
from ...utils import expected_http_error
+from jupyter_server.services.kernels.kernelmanager import
AsyncMappingKernelManager
from jupyter_server.utils import url_path_join
[email protected](params=["MappingKernelManager", "AsyncMappingKernelManager"])
+class TestMappingKernelManager(AsyncMappingKernelManager):
+ """A no-op subclass to use in a fixture"""
+
+
[email protected](
+ params=["MappingKernelManager", "AsyncMappingKernelManager",
"TestMappingKernelManager"]
+)
def jp_argv(request):
+ if request.param == "TestMappingKernelManager":
+ extra = []
+ if hasattr(AsyncMappingKernelManager, "use_pending_kernels"):
+ extra = ["--AsyncMappingKernelManager.use_pending_kernels=True"]
+ return [
+
"--ServerApp.kernel_manager_class=jupyter_server.tests.services.kernels.test_api."
+ + request.param
+ ] + extra
return [
"--ServerApp.kernel_manager_class=jupyter_server.services.kernels.kernelmanager."
+ request.param
@@ -38,7 +54,7 @@
await jp_cleanup_subprocesses()
-async def test_main_kernel_handler(jp_fetch, jp_base_url,
jp_cleanup_subprocesses):
+async def test_main_kernel_handler(jp_fetch, jp_base_url,
jp_cleanup_subprocesses, jp_serverapp):
# Start the first kernel
r = await jp_fetch(
"api", "kernels", method="POST", body=json.dumps({"name":
NATIVE_KERNEL_NAME})
@@ -83,6 +99,10 @@
assert r.code == 204
# Restart a kernel
+ kernel = jp_serverapp.kernel_manager.get_kernel(kernel2["id"])
+ if hasattr(kernel, "ready"):
+ await kernel.ready
+
r = await jp_fetch(
"api", "kernels", kernel2["id"], "restart", method="POST",
allow_nonstandard_methods=True
)
@@ -143,6 +163,32 @@
await jp_cleanup_subprocesses()
+async def test_kernel_handler_startup_error(
+ jp_fetch, jp_cleanup_subprocesses, jp_serverapp, jp_kernelspecs
+):
+ if getattr(jp_serverapp.kernel_manager, "use_pending_kernels", False):
+ return
+
+ # Create a kernel
+ with pytest.raises(HTTPClientError):
+ await jp_fetch("api", "kernels", method="POST",
body=json.dumps({"name": "bad"}))
+
+
+async def test_kernel_handler_startup_error_pending(
+ jp_fetch, jp_ws_fetch, jp_cleanup_subprocesses, jp_serverapp,
jp_kernelspecs
+):
+ if not getattr(jp_serverapp.kernel_manager, "use_pending_kernels", False):
+ return
+
+ jp_serverapp.kernel_manager.use_pending_kernels = True
+ # Create a kernel
+ r = await jp_fetch("api", "kernels", method="POST",
body=json.dumps({"name": "bad"}))
+ kid = json.loads(r.body.decode())["id"]
+
+ with pytest.raises(HTTPClientError):
+ await jp_ws_fetch("api", "kernels", kid, "channels")
+
+
async def test_connection(
jp_fetch, jp_ws_fetch, jp_http_port, jp_auth_header,
jp_cleanup_subprocesses
):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/tests/services/kernels/test_cull.py
new/jupyter_server-1.13.1/jupyter_server/tests/services/kernels/test_cull.py
---
old/jupyter_server-1.11.2/jupyter_server/tests/services/kernels/test_cull.py
2021-11-01 21:22:56.000000000 +0100
+++
new/jupyter_server-1.13.1/jupyter_server/tests/services/kernels/test_cull.py
2021-12-09 20:36:05.000000000 +0100
@@ -34,7 +34,7 @@
)
-async def test_culling(jp_fetch, jp_ws_fetch, jp_cleanup_subprocesses):
+async def test_cull_idle(jp_fetch, jp_ws_fetch, jp_cleanup_subprocesses):
r = await jp_fetch("api", "kernels", method="POST",
allow_nonstandard_methods=True)
kernel = json.loads(r.body.decode())
kid = kernel["id"]
@@ -52,6 +52,30 @@
assert culled
await jp_cleanup_subprocesses()
+
+async def test_cull_dead(
+ jp_fetch, jp_ws_fetch, jp_serverapp, jp_cleanup_subprocesses,
jp_kernelspecs
+):
+ if not hasattr(jp_serverapp.kernel_manager, "use_pending_kernels"):
+ return
+
+ jp_serverapp.kernel_manager.use_pending_kernels = True
+ jp_serverapp.kernel_manager.default_kernel_name = "bad"
+ r = await jp_fetch("api", "kernels", method="POST",
allow_nonstandard_methods=True)
+ kernel = json.loads(r.body.decode())
+ kid = kernel["id"]
+
+ # Open a websocket connection.
+ with pytest.raises(HTTPClientError):
+ await jp_ws_fetch("api", "kernels", kid, "channels")
+
+ r = await jp_fetch("api", "kernels", kid, method="GET")
+ model = json.loads(r.body.decode())
+ assert model["connections"] == 0
+ culled = await get_cull_status(kid, jp_fetch) # connected, should not be
culled
+ assert culled
+ await jp_cleanup_subprocesses()
+
async def get_cull_status(kid, jp_fetch):
frequency = 0.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/tests/services/kernelspecs/test_api.py
new/jupyter_server-1.13.1/jupyter_server/tests/services/kernelspecs/test_api.py
---
old/jupyter_server-1.11.2/jupyter_server/tests/services/kernelspecs/test_api.py
2021-11-01 21:22:56.000000000 +0100
+++
new/jupyter_server-1.13.1/jupyter_server/tests/services/kernelspecs/test_api.py
2021-12-09 20:36:05.000000000 +0100
@@ -9,7 +9,7 @@
async def test_list_kernelspecs_bad(jp_fetch, jp_kernelspecs, jp_data_dir):
- bad_kernel_dir = jp_data_dir.joinpath(jp_data_dir, "kernels", "bad")
+ bad_kernel_dir = jp_data_dir.joinpath(jp_data_dir, "kernels", "bad2")
bad_kernel_dir.mkdir(parents=True)
bad_kernel_json = bad_kernel_dir.joinpath("kernel.json")
bad_kernel_json.write_text("garbage")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/tests/services/sessions/test_api.py
new/jupyter_server-1.13.1/jupyter_server/tests/services/sessions/test_api.py
---
old/jupyter_server-1.11.2/jupyter_server/tests/services/sessions/test_api.py
2021-11-01 21:22:56.000000000 +0100
+++
new/jupyter_server-1.13.1/jupyter_server/tests/services/sessions/test_api.py
2021-12-09 20:36:05.000000000 +0100
@@ -1,4 +1,5 @@
import json
+import os
import shutil
import time
@@ -7,6 +8,7 @@
from jupyter_client.ioloop import AsyncIOLoopKernelManager
from nbformat import writes
from nbformat.v4 import new_notebook
+from tornado.httpclient import HTTPClientError
from traitlets import default
from ...utils import expected_http_error
@@ -39,10 +41,13 @@
)
def jp_argv(request):
if request.param == "NewPortsMappingKernelManager":
+ extra = []
+ if hasattr(AsyncMappingKernelManager, "use_pending_kernels"):
+ extra = ["--AsyncMappingKernelManager.use_pending_kernels=True"]
return [
"--ServerApp.kernel_manager_class=jupyter_server.tests.services.sessions.test_api."
+ request.param
- ]
+ ] + extra
return [
"--ServerApp.kernel_manager_class=jupyter_server.services.kernels.kernelmanager."
+ request.param
@@ -68,7 +73,7 @@
async def get(self, id):
return await self._req(id, method="GET")
- async def create(self, path, type="notebook", kernel_name="python",
kernel_id=None):
+ async def create(self, path, type="notebook", kernel_name=None,
kernel_id=None):
body = {"path": path, "type": type, "kernel": {"name": kernel_name,
"id": kernel_id}}
return await self._req(method="POST", body=body)
@@ -151,7 +156,7 @@
assert_kernel_equality(actual["kernel"], expected["kernel"])
-async def test_create(session_client, jp_base_url, jp_cleanup_subprocesses):
+async def test_create(session_client, jp_base_url, jp_cleanup_subprocesses,
jp_serverapp):
# Make sure no sessions exist.
resp = await session_client.list()
sessions = j(resp)
@@ -168,6 +173,17 @@
jp_base_url, "/api/sessions/", new_session["id"]
)
+ # Make sure kernel is in expected state
+ kid = new_session["kernel"]["id"]
+ kernel = jp_serverapp.kernel_manager.get_kernel(kid)
+
+ if hasattr(kernel, "ready") and os.name != "nt":
+ km = jp_serverapp.kernel_manager
+ if isinstance(km, AsyncMappingKernelManager):
+ assert kernel.ready.done() == (not km.use_pending_kernels)
+ else:
+ assert kernel.ready.done()
+
# Check that the new session appears in list.
resp = await session_client.list()
sessions = j(resp)
@@ -185,7 +201,61 @@
await jp_cleanup_subprocesses()
-async def test_create_file_session(session_client, jp_cleanup_subprocesses):
+async def test_create_bad(
+ session_client, jp_base_url, jp_cleanup_subprocesses, jp_serverapp,
jp_kernelspecs
+):
+ if getattr(jp_serverapp.kernel_manager, "use_pending_kernels", False):
+ return
+
+ # Make sure no sessions exist.
+ jp_serverapp.kernel_manager.default_kernel_name = "bad"
+ resp = await session_client.list()
+ sessions = j(resp)
+ assert len(sessions) == 0
+
+ # Create a session.
+ with pytest.raises(HTTPClientError):
+ await session_client.create("foo/nb1.ipynb")
+
+ # Need to find a better solution to this.
+ await session_client.cleanup()
+ await jp_cleanup_subprocesses()
+
+
+async def test_create_bad_pending(
+ session_client, jp_base_url, jp_ws_fetch, jp_cleanup_subprocesses,
jp_serverapp, jp_kernelspecs
+):
+ if not getattr(jp_serverapp.kernel_manager, "use_pending_kernels", False):
+ return
+
+ # Make sure no sessions exist.
+ jp_serverapp.kernel_manager.default_kernel_name = "bad"
+ resp = await session_client.list()
+ sessions = j(resp)
+ assert len(sessions) == 0
+
+ # Create a session.
+ resp = await session_client.create("foo/nb1.ipynb")
+ assert resp.code == 201
+
+ # Open a websocket connection.
+ kid = j(resp)["kernel"]["id"]
+ with pytest.raises(HTTPClientError):
+ await jp_ws_fetch("api", "kernels", kid, "channels")
+
+ # Get the updated kernel state
+ resp = await session_client.list()
+ session = j(resp)[0]
+ assert session["kernel"]["execution_state"] == "dead"
+ if os.name != "nt":
+ assert "non_existent_path" in session["kernel"]["reason"]
+
+ # Need to find a better solution to this.
+ await session_client.cleanup()
+ await jp_cleanup_subprocesses()
+
+
+async def test_create_file_session(session_client, jp_cleanup_subprocesses,
jp_serverapp):
resp = await session_client.create("foo/nb1.py", type="file")
assert resp.code == 201
newsession = j(resp)
@@ -195,7 +265,7 @@
await jp_cleanup_subprocesses()
-async def test_create_console_session(session_client, jp_cleanup_subprocesses):
+async def test_create_console_session(session_client, jp_cleanup_subprocesses,
jp_serverapp):
resp = await session_client.create("foo/abc123", type="console")
assert resp.code == 201
newsession = j(resp)
@@ -206,7 +276,7 @@
await jp_cleanup_subprocesses()
-async def test_create_deprecated(session_client, jp_cleanup_subprocesses):
+async def test_create_deprecated(session_client, jp_cleanup_subprocesses,
jp_serverapp):
resp = await session_client.create_deprecated("foo/nb1.ipynb")
assert resp.code == 201
newsession = j(resp)
@@ -219,7 +289,7 @@
async def test_create_with_kernel_id(
- session_client, jp_fetch, jp_base_url, jp_cleanup_subprocesses
+ session_client, jp_fetch, jp_base_url, jp_cleanup_subprocesses,
jp_serverapp
):
# create a new kernel
resp = await jp_fetch("api/kernels", method="POST",
allow_nonstandard_methods=True)
@@ -250,7 +320,18 @@
await jp_cleanup_subprocesses()
-async def test_delete(session_client, jp_cleanup_subprocesses):
+async def test_create_with_bad_kernel_id(session_client,
jp_cleanup_subprocesses, jp_serverapp):
+ resp = await session_client.create("foo/nb1.py", type="file")
+ assert resp.code == 201
+ newsession = j(resp)
+ # TODO
+ assert newsession["path"] == "foo/nb1.py"
+ assert newsession["type"] == "file"
+ await session_client.cleanup()
+ await jp_cleanup_subprocesses()
+
+
+async def test_delete(session_client, jp_cleanup_subprocesses, jp_serverapp):
resp = await session_client.create("foo/nb1.ipynb")
newsession = j(resp)
sid = newsession["id"]
@@ -270,7 +351,7 @@
await jp_cleanup_subprocesses()
-async def test_modify_path(session_client, jp_cleanup_subprocesses):
+async def test_modify_path(session_client, jp_cleanup_subprocesses,
jp_serverapp):
resp = await session_client.create("foo/nb1.ipynb")
newsession = j(resp)
sid = newsession["id"]
@@ -284,7 +365,7 @@
await jp_cleanup_subprocesses()
-async def test_modify_path_deprecated(session_client, jp_cleanup_subprocesses):
+async def test_modify_path_deprecated(session_client, jp_cleanup_subprocesses,
jp_serverapp):
resp = await session_client.create("foo/nb1.ipynb")
newsession = j(resp)
sid = newsession["id"]
@@ -298,7 +379,7 @@
await jp_cleanup_subprocesses()
-async def test_modify_type(session_client, jp_cleanup_subprocesses):
+async def test_modify_type(session_client, jp_cleanup_subprocesses,
jp_serverapp):
resp = await session_client.create("foo/nb1.ipynb")
newsession = j(resp)
sid = newsession["id"]
@@ -312,7 +393,7 @@
await jp_cleanup_subprocesses()
-async def test_modify_kernel_name(session_client, jp_fetch,
jp_cleanup_subprocesses):
+async def test_modify_kernel_name(session_client, jp_fetch,
jp_cleanup_subprocesses, jp_serverapp):
resp = await session_client.create("foo/nb1.ipynb")
before = j(resp)
sid = before["id"]
@@ -329,13 +410,15 @@
kernel_list = j(resp)
after["kernel"].pop("last_activity")
[k.pop("last_activity") for k in kernel_list]
- assert kernel_list == [after["kernel"]]
+ if not getattr(jp_serverapp.kernel_manager, "use_pending_kernels", False):
+ assert kernel_list == [after["kernel"]]
+
# Need to find a better solution to this.
await session_client.cleanup()
await jp_cleanup_subprocesses()
-async def test_modify_kernel_id(session_client, jp_fetch,
jp_cleanup_subprocesses):
+async def test_modify_kernel_id(session_client, jp_fetch,
jp_cleanup_subprocesses, jp_serverapp):
resp = await session_client.create("foo/nb1.ipynb")
before = j(resp)
sid = before["id"]
@@ -359,7 +442,8 @@
kernel.pop("last_activity")
[k.pop("last_activity") for k in kernel_list]
- assert kernel_list == [kernel]
+ if not getattr(jp_serverapp.kernel_manager, "use_pending_kernels", False):
+ assert kernel_list == [kernel]
# Need to find a better solution to this.
await session_client.cleanup()
@@ -369,7 +453,6 @@
async def test_restart_kernel(
session_client, jp_base_url, jp_fetch, jp_ws_fetch, jp_cleanup_subprocesses
):
-
# Create a session.
resp = await session_client.create("foo/nb1.ipynb")
assert resp.code == 201
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server/tests/services/sessions/test_manager.py
new/jupyter_server-1.13.1/jupyter_server/tests/services/sessions/test_manager.py
---
old/jupyter_server-1.11.2/jupyter_server/tests/services/sessions/test_manager.py
2021-11-01 21:22:56.000000000 +0100
+++
new/jupyter_server-1.13.1/jupyter_server/tests/services/sessions/test_manager.py
2021-12-09 20:36:05.000000000 +0100
@@ -1,5 +1,6 @@
import pytest
from tornado import web
+from traitlets import TraitError
from jupyter_server._tz import isoformat
from jupyter_server._tz import utcnow
@@ -264,3 +265,101 @@
await session_manager.delete_session(bad_kwarg="23424") # Bad keyword
with pytest.raises(web.HTTPError):
await session_manager.delete_session(session_id="23424") # nonexistent
+
+
+async def test_bad_database_filepath(jp_runtime_dir):
+ kernel_manager = DummyMKM()
+
+ # Try to write to a path that's a directory, not a file.
+ path_id_directory = str(jp_runtime_dir)
+ # Should raise an error because the path is a directory.
+ with pytest.raises(TraitError) as err:
+ SessionManager(
+ kernel_manager=kernel_manager,
+ contents_manager=ContentsManager(),
+ database_filepath=str(path_id_directory),
+ )
+
+ # Try writing to file that's not a valid SQLite 3 database file.
+ non_db_file = jp_runtime_dir.joinpath("non_db_file.db")
+ non_db_file.write_bytes(b"this is a bad file")
+
+ # Should raise an error because the file doesn't
+ # start with an SQLite database file header.
+ with pytest.raises(TraitError) as err:
+ SessionManager(
+ kernel_manager=kernel_manager,
+ contents_manager=ContentsManager(),
+ database_filepath=str(non_db_file),
+ )
+
+
+async def test_good_database_filepath(jp_runtime_dir):
+ kernel_manager = DummyMKM()
+
+ # Try writing to an empty file.
+ empty_file = jp_runtime_dir.joinpath("empty.db")
+ empty_file.write_bytes(b"")
+
+ session_manager = SessionManager(
+ kernel_manager=kernel_manager,
+ contents_manager=ContentsManager(),
+ database_filepath=str(empty_file),
+ )
+
+ await session_manager.create_session(
+ path="/path/to/test.ipynb", kernel_name="python", type="notebook"
+ )
+ # Assert that the database file exists
+ assert empty_file.exists()
+
+ # Close the current session manager
+ del session_manager
+
+ # Try writing to a file that already exists.
+ session_manager = SessionManager(
+ kernel_manager=kernel_manager,
+ contents_manager=ContentsManager(),
+ database_filepath=str(empty_file),
+ )
+
+ assert session_manager.database_filepath == str(empty_file)
+
+
+async def test_session_persistence(jp_runtime_dir):
+ session_db_path = jp_runtime_dir.joinpath("test-session.db")
+ # Kernel manager needs to persist.
+ kernel_manager = DummyMKM()
+
+ # Initialize a session and start a connection.
+ # This should create the session database the first time.
+ session_manager = SessionManager(
+ kernel_manager=kernel_manager,
+ contents_manager=ContentsManager(),
+ database_filepath=str(session_db_path),
+ )
+
+ session = await session_manager.create_session(
+ path="/path/to/test.ipynb", kernel_name="python", type="notebook"
+ )
+
+ # Assert that the database file exists
+ assert session_db_path.exists()
+
+ with open(session_db_path, "rb") as f:
+ header = f.read(100)
+
+ assert header.startswith(b"SQLite format 3")
+
+ # Close the current session manager
+ del session_manager
+
+ # Get a new session_manager
+ session_manager = SessionManager(
+ kernel_manager=kernel_manager,
+ contents_manager=ContentsManager(),
+ database_filepath=str(session_db_path),
+ )
+
+ # Assert that the session database persists.
+ session = await session_manager.get_session(session_id=session["id"])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.11.2/jupyter_server.egg-info/PKG-INFO
new/jupyter_server-1.13.1/jupyter_server.egg-info/PKG-INFO
--- old/jupyter_server-1.11.2/jupyter_server.egg-info/PKG-INFO 2021-11-01
21:23:33.000000000 +0100
+++ new/jupyter_server-1.13.1/jupyter_server.egg-info/PKG-INFO 2021-12-09
20:36:35.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: jupyter-server
-Version: 1.11.2
+Version: 1.13.1
Summary: The backend???i.e. core services, APIs, and REST endpoints???to
Jupyter web applications.
Home-page: https://jupyter.org
Author: Jupyter Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.11.2/pyproject.toml
new/jupyter_server-1.13.1/pyproject.toml
--- old/jupyter_server-1.11.2/pyproject.toml 2021-11-01 21:23:18.000000000
+0100
+++ new/jupyter_server-1.13.1/pyproject.toml 2021-12-09 20:36:21.000000000
+0100
@@ -18,7 +18,7 @@
skip = ["check-links"]
[tool.tbump.version]
-current = "1.11.2"
+current = "1.13.1"
regex = '''
(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
((?P<channel>a|b|rc|.dev)(?P<release>\d+))?
@@ -30,3 +30,12 @@
[[tool.tbump.file]]
src = "jupyter_server/_version.py"
+version_template = '({major}, {minor}, {patch}, "{channel}", "{release}")'
+
+[[tool.tbump.field]]
+name = "channel"
+default = ""
+
+[[tool.tbump.field]]
+name = "release"
+default = ""