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-10-26 14:21:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jupyter-server (Old)
 and      /work/SRC/openSUSE:Factory/.python-jupyter-server.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-jupyter-server"

Wed Oct 26 14:21:57 2022 rev:27 rq:1031311 version:1.21.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-jupyter-server/python-jupyter-server.changes  
    2022-08-06 22:08:15.602674331 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-jupyter-server.new.2275/python-jupyter-server.changes
    2022-10-26 14:21:59.079890085 +0200
@@ -1,0 +2,31 @@
+Wed Oct 26 10:32:16 UTC 2022 - Ben Greiner <[email protected]>
+
+- Update to version 1.21.0
+  * Handle client 8 pending kernels #1014 (@blink1073)
+- Release 1.19.1
+  * Wrap the concurrent futures in an asyncio future #1000
+    (@blink1073)
+- Release 1.19.0
+  * New features added
+    + Backport Inject session identifier into environment variable
+      #920 (@vidartf)
+  * Enhancements made
+    + Backport #981: Make it easier to pass custom env variables to
+      kernel #994 (@divyansshhh)
+    + Retry certain errors between server and gateway #944
+      (@kevin-bates)
+  * Bugs fixed
+    + Backport PR #965: Correct content-type headers #966
+      (@epignot)
+    + avoid creating asyncio.Lock at import time #935 (@minrk)
+    + Fix c.GatewayClient.url snippet syntax #917 (@rickwierenga)
+    + Add back support for kernel launch timeout pad #910
+      (@CiprianAnton)
+  * Maintenance and upkeep improvements
+    + Test with client 8 updates #992 (@blink1073)
+    + Backport PR #922: Improve logging of bare exceptions etc.
+      #926 (@thetorpedodog)
+    + Fix handling of dev version #913 (@blink1073)
+    + Fix owasp link #908 (@blink1073)
+
+-------------------------------------------------------------------

Old:
----
  jupyter_server-1.18.1.tar.gz

New:
----
  jupyter_server-1.21.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-jupyter-server.spec ++++++
--- /var/tmp/diff_new_pack.NFTbTT/_old  2022-10-26 14:21:59.783891287 +0200
+++ /var/tmp/diff_new_pack.NFTbTT/_new  2022-10-26 14:21:59.787891294 +0200
@@ -31,7 +31,7 @@
 %bcond_with libalternatives
 %endif
 Name:           python-jupyter-server%{psuffix}
-Version:        1.18.1
+Version:        1.21.0
 Release:        0
 Summary:        The backend to Jupyter web applications
 License:        BSD-3-Clause
@@ -94,7 +94,7 @@
 Group:          Development/Languages/Python
 Requires:       python-ipykernel
 Requires:       python-jupyter-server = %{version}
-Requires:       python-pytest >= 6
+Requires:       python-pytest >= 7
 Requires:       python-pytest-console-scripts
 Requires:       python-pytest-mock
 Requires:       python-pytest-timeout

++++++ jupyter_server-1.18.1.tar.gz -> jupyter_server-1.21.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/CHANGELOG.md 
new/jupyter_server-1.21.0/CHANGELOG.md
--- old/jupyter_server-1.18.1/CHANGELOG.md      2022-07-05 22:22:43.000000000 
+0200
+++ new/jupyter_server-1.21.0/CHANGELOG.md      2022-10-11 13:35:05.000000000 
+0200
@@ -4,6 +4,73 @@
 
 <!-- <START NEW CHANGELOG ENTRY> -->
 
+## 1.21.0
+
+([Full 
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.19.1...2f529bf5d1f8c68a6b7193f474c9ba025111d743))
+
+### Maintenance and upkeep improvements
+
+- Handle client 8 pending kernels 
[#1014](https://github.com/jupyter-server/jupyter_server/pull/1014) 
([@blink1073](https://github.com/blink1073))
+
+### Documentation improvements
+
+- Pin docutils to fix docs build 
[#1004](https://github.com/jupyter-server/jupyter_server/pull/1004) 
([@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=2022-09-27&to=2022-10-11&type=c))
+
+[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2022-09-27..2022-10-11&type=Issues)
 | 
[@meeseeksmachine](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ameeseeksmachine+updated%3A2022-09-27..2022-10-11&type=Issues)
 | 
[@welcome](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Awelcome+updated%3A2022-09-27..2022-10-11&type=Issues)
+
+<!-- <END NEW CHANGELOG ENTRY> -->
+
+## 1.19.1
+
+([Full 
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.19.0...690d2f4131b2d65476d05b302a4b3e9a2f052650))
+
+### Bugs fixed
+
+- Wrap the concurrent futures in an asyncio future 
[#1000](https://github.com/jupyter-server/jupyter_server/pull/1000) 
([@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=2022-09-26&to=2022-09-27&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2022-09-26..2022-09-27&type=Issues)
 | 
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2022-09-26..2022-09-27&type=Issues)
+
+## 1.19.0
+
+([Full 
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.18.1...27bc44c23fba38a4d02b3d9d9eeef45e53eb76ba))
+
+### New features added
+
+- Backport Inject session identifier into environment variable 
[#920](https://github.com/jupyter-server/jupyter_server/pull/920) 
([@vidartf](https://github.com/vidartf))
+
+### Enhancements made
+
+- Backport #981: Make it easier to pass custom env variables to kernel 
[#994](https://github.com/jupyter-server/jupyter_server/pull/994) 
([@divyansshhh](https://github.com/divyansshhh))
+- Retry certain errors between server and gateway 
[#944](https://github.com/jupyter-server/jupyter_server/pull/944) 
([@kevin-bates](https://github.com/kevin-bates))
+
+### Bugs fixed
+
+- Backport PR #965: Correct content-type headers 
[#966](https://github.com/jupyter-server/jupyter_server/pull/966) 
([@epignot](https://github.com/epignot))
+- avoid creating asyncio.Lock at import time 
[#935](https://github.com/jupyter-server/jupyter_server/pull/935) 
([@minrk](https://github.com/minrk))
+- Fix c.GatewayClient.url snippet syntax 
[#917](https://github.com/jupyter-server/jupyter_server/pull/917) 
([@rickwierenga](https://github.com/rickwierenga))
+- Add back support for kernel launch timeout pad 
[#910](https://github.com/jupyter-server/jupyter_server/pull/910) 
([@CiprianAnton](https://github.com/CiprianAnton))
+
+### Maintenance and upkeep improvements
+
+- Test with client 8 updates 
[#992](https://github.com/jupyter-server/jupyter_server/pull/992) 
([@blink1073](https://github.com/blink1073))
+- Backport PR #922: Improve logging of bare exceptions etc. 
[#926](https://github.com/jupyter-server/jupyter_server/pull/926) 
([@thetorpedodog](https://github.com/thetorpedodog))
+- Fix handling of dev version 
[#913](https://github.com/jupyter-server/jupyter_server/pull/913) 
([@blink1073](https://github.com/blink1073))
+- Fix owasp link 
[#908](https://github.com/jupyter-server/jupyter_server/pull/908) 
([@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=2022-07-05&to=2022-09-26&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@davidbrochart](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Adavidbrochart+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@divyansshhh](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Adivyansshhh+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@dlqqq](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Adlqqq+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@echarles](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Aecharles+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@epignot](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involve
 s%3Aepignot+updated%3A2022-07-05..2022-09-26&type=Issues) | 
[@kevin-bates](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Akevin-bates+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@meeseeksdev](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ameeseeksdev+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@meeseeksmachine](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ameeseeksmachine+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@minrk](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Aminrk+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@thetorpedodog](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Athetorpedodog+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@vidartf](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Avidartf+updated%3A2022-07-05..2022-09-26&type=Issues)
 | [@welcome](https://github.c
 
om/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Awelcome+updated%3A2022-07-05..2022-09-26&type=Issues)
 | 
[@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AZsailer+updated%3A2022-07-05..2022-09-26&type=Issues)
+
 ## 1.18.1
 
 ([Full 
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.18.0...65d779a12b6e4123de0767de6547e6cdf2d5e7e9))
@@ -19,8 +86,6 @@
 
 
[@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2022-06-23..2022-07-05&type=Issues)
 | 
[@Carreau](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3ACarreau+updated%3A2022-06-23..2022-07-05&type=Issues)
 | 
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2022-06-23..2022-07-05&type=Issues)
 | 
[@davidbrochart](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Adavidbrochart+updated%3A2022-06-23..2022-07-05&type=Issues)
 | 
[@echarles](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Aecharles+updated%3A2022-06-23..2022-07-05&type=Issues)
 | 
[@kevin-bates](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Akevin-bates+updated%3A2022-06-23..2022-07-05&type=Issues)
 | 
[@meeseeksmachine](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_se
 rver+involves%3Ameeseeksmachine+updated%3A2022-06-23..2022-07-05&type=Issues)
 
-<!-- <END NEW CHANGELOG ENTRY> -->
-
 ## 1.18.0
 
 ([Full 
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.17.1...a625db91efc4927c4b6fed4bf67ab995e479c36d))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/PKG-INFO 
new/jupyter_server-1.21.0/PKG-INFO
--- old/jupyter_server-1.18.1/PKG-INFO  2022-07-05 22:23:12.652353500 +0200
+++ new/jupyter_server-1.21.0/PKG-INFO  2022-10-11 13:35:20.650097600 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: jupyter_server
-Version: 1.18.1
+Version: 1.21.0
 Summary: The backend???i.e. core services, APIs, and REST endpoints???to 
Jupyter web applications.
 Home-page: https://jupyter-server.readthedocs.io
 Author: Jupyter Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/RELEASE.md 
new/jupyter_server-1.21.0/RELEASE.md
--- old/jupyter_server-1.18.1/RELEASE.md        2022-07-05 22:22:43.000000000 
+0200
+++ new/jupyter_server-1.21.0/RELEASE.md        2022-10-11 13:34:37.000000000 
+0200
@@ -4,6 +4,9 @@
 
 The recommended way to make a release is to use 
[`jupyter_releaser`](https://github.com/jupyter-server/jupyter_releaser#checklist-for-adoption).
 
+Note that we must use manual versions since Jupyter Releaser does not
+yet support "next" or "patch" when dev versions are used.
+
 ## Manual Release
 
 To create a manual release, perform the following steps:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/docs/doc-requirements.txt 
new/jupyter_server-1.21.0/docs/doc-requirements.txt
--- old/jupyter_server-1.18.1/docs/doc-requirements.txt 2022-07-05 
22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/docs/doc-requirements.txt 2022-10-11 
13:34:37.000000000 +0200
@@ -1,6 +1,11 @@
+# needed because m2r uses deprecated APIs
+# m2r is depended on by sphinxcontrib-openapi
+docutils<0.19
 ipykernel
 jinja2
 jupyter_client
+jupyter_server
+mistune<1.0.0
 myst-parser
 nbformat
 prometheus_client
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/docs/source/conf.py 
new/jupyter_server-1.21.0/docs/source/conf.py
--- old/jupyter_server-1.18.1/docs/source/conf.py       2022-07-05 
22:22:58.000000000 +0200
+++ new/jupyter_server-1.21.0/docs/source/conf.py       2022-10-11 
13:35:02.000000000 +0200
@@ -107,7 +107,7 @@
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
-__version__ = "1.18.1"
+__version__ = "1.21.0"
 # The short X.Y version.
 version_parsed = parse_version(__version__)
 version = f"{version_parsed.major}.{version_parsed.minor}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/docs/source/operators/public-server.rst 
new/jupyter_server-1.21.0/docs/source/operators/public-server.rst
--- old/jupyter_server-1.18.1/docs/source/operators/public-server.rst   
2022-07-05 22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/docs/source/operators/public-server.rst   
2022-10-11 13:34:37.000000000 +0200
@@ -180,7 +180,7 @@
 certificate and follow the steps in :ref:`using-lets-encrypt` to set up a
 public server.
 
-.. _OWASP: https://www.owasp.org/index.php/Main_Page
+.. _OWASP: https://owasp.org/sitemap/
 .. _tutorial: 
https://arstechnica.com/information-technology/2009/12/how-to-get-set-with-a-secure-sertificate-for-free/
 
 .. _jupyter_public_server:
@@ -364,7 +364,7 @@
 
    .. code-block:: python
 
-      c.GatewayClient.url = http://my-gateway-server:8888
+      c.GatewayClient.url = 'http://my-gateway-server:8888'
 
 When provided, all kernel specifications will be retrieved from the specified 
Gateway server and all
 kernels will be managed by that server.  This option enables the ability to 
target kernel processes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/jupyter_server/_version.py 
new/jupyter_server-1.21.0/jupyter_server/_version.py
--- old/jupyter_server-1.18.1/jupyter_server/_version.py        2022-07-05 
22:22:58.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server/_version.py        2022-10-11 
13:35:02.000000000 +0200
@@ -2,5 +2,5 @@
 store the current version info of the server.
 
 """
-version_info = (1, 18, 1, "", "")
+version_info = (1, 21, 0, "", "")
 __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.18.1/jupyter_server/base/handlers.py 
new/jupyter_server-1.21.0/jupyter_server/base/handlers.py
--- old/jupyter_server-1.18.1/jupyter_server/base/handlers.py   2022-07-05 
22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server/base/handlers.py   2022-10-11 
13:34:37.000000000 +0200
@@ -95,7 +95,7 @@
                 # tornado raise Exception (not a subclass)
                 # if method is unsupported (websocket and 
Access-Control-Allow-Origin
                 # for example, so just ignore)
-                self.log.debug(e)
+                self.log.exception("Could not set default headers: %s", e)
 
     def force_clear_cookie(self, name, path="/", domain=None):
         """Deletes the cookie with the given name.
@@ -646,7 +646,7 @@
                 reply["message"] = "Unhandled error"
                 reply["reason"] = None
                 reply["traceback"] = 
"".join(traceback.format_exception(*exc_info))
-        self.log.warning(reply["message"])
+        self.log.warning("wrote error: %r", reply["message"])
         self.finish(json.dumps(reply))
 
     def get_current_user(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/jupyter_server/extension/manager.py 
new/jupyter_server-1.21.0/jupyter_server/extension/manager.py
--- old/jupyter_server-1.18.1/jupyter_server/extension/manager.py       
2022-07-05 22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server/extension/manager.py       
2022-10-11 13:34:37.000000000 +0200
@@ -1,6 +1,4 @@
 import importlib
-import sys
-import traceback
 
 from tornado.gen import multi
 from traitlets import Any, Bool, Dict, HasTraits, Instance, Unicode, default, 
observe
@@ -326,7 +324,13 @@
         except Exception as e:
             if self.serverapp.reraise_server_extension_failures:
                 raise
-            self.log.warning(e)
+            self.log.warning(
+                "%s | error adding extension (enabled: %s): %s",
+                extension_name,
+                enabled,
+                e,
+                exc_info=True,
+            )
         return False
 
     def link_extension(self, name):
@@ -337,11 +341,11 @@
                 # Link extension and store links
                 extension.link_all_points(self.serverapp)
                 self.linked_extensions[name] = True
-                self.log.info(f"{name} | extension was successfully linked.")
+                self.log.info("%s | extension was successfully linked.", name)
             except Exception as e:
                 if self.serverapp.reraise_server_extension_failures:
                     raise
-                self.log.warning(e)
+                self.log.warning("%s | error linking extension: %s", name, e, 
exc_info=True)
 
     def load_extension(self, name):
         extension = self.extensions.get(name)
@@ -352,21 +356,17 @@
             except Exception as e:
                 if self.serverapp.reraise_server_extension_failures:
                     raise
-                
self.log.debug("".join(traceback.format_exception(*sys.exc_info())))
-                self.log.warning(
-                    "{name} | extension failed loading with message: 
{error}".format(
-                        name=name, error=str(e)
-                    )
-                )
+                self.log.warning("%s | extension failed loading with message: 
%s", name, e)
+                self.log.exception("%s | stack trace", name)
             else:
-                self.log.info(f"{name} | extension was successfully loaded.")
+                self.log.info("%s | extension was successfully loaded.", name)
 
     async def stop_extension(self, name, apps):
         """Call the shutdown hooks in the specified apps."""
         for app in apps:
-            self.log.debug(f'{name} | extension app "{app.name}" stopping')
+            self.log.debug("%s | extension app %r stopping", name, app.name)
             await app.stop_extension()
-            self.log.debug(f'{name} | extension app "{app.name}" stopped')
+            self.log.debug("%s | extension app %r stopped", name, app.name)
 
     def link_all_extensions(self):
         """Link all enabled extensions
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/jupyter_server/gateway/gateway_client.py 
new/jupyter_server-1.21.0/jupyter_server/gateway/gateway_client.py
--- old/jupyter_server-1.18.1/jupyter_server/gateway/gateway_client.py  
2022-07-05 22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server/gateway/gateway_client.py  
2022-10-11 13:34:37.000000000 +0200
@@ -1,11 +1,14 @@
 # Copyright (c) Jupyter Development Team.
 # Distributed under the terms of the Modified BSD License.
+import asyncio
 import json
+import logging
 import os
+import typing as ty
 from socket import gaierror
 
 from tornado import web
-from tornado.httpclient import AsyncHTTPClient, HTTPError
+from tornado.httpclient import AsyncHTTPClient, HTTPClientError, HTTPResponse
 from traitlets import Bool, Float, Int, TraitError, Unicode, default, validate
 from traitlets.config import SingletonConfigurable
 
@@ -128,7 +131,7 @@
             os.environ.get("JUPYTER_GATEWAY_CONNECT_TIMEOUT", 
self.connect_timeout_default_value)
         )
 
-    request_timeout_default_value = 40.0
+    request_timeout_default_value = 42.0
     request_timeout_env = "JUPYTER_GATEWAY_REQUEST_TIMEOUT"
     request_timeout = Float(
         default_value=request_timeout_default_value,
@@ -341,6 +344,25 @@
             os.environ.get("JUPYTER_GATEWAY_RETRY_MAX", 
self.gateway_retry_max_default_value)
         )
 
+    launch_timeout_pad_default_value = 2.0
+    launch_timeout_pad_env = "JUPYTER_GATEWAY_LAUNCH_TIMEOUT_PAD"
+    launch_timeout_pad = Float(
+        default_value=launch_timeout_pad_default_value,
+        config=True,
+        help="""Timeout pad to be ensured between KERNEL_LAUNCH_TIMEOUT and 
request_timeout
+        such that request_timeout >= KERNEL_LAUNCH_TIMEOUT + 
launch_timeout_pad.
+        (JUPYTER_GATEWAY_LAUNCH_TIMEOUT_PAD env var)""",
+    )
+
+    @default("launch_timeout_pad")
+    def launch_timeout_pad_default(self):
+        return float(
+            os.environ.get(
+                "JUPYTER_GATEWAY_LAUNCH_TIMEOUT_PAD",
+                self.launch_timeout_pad_default_value,
+            )
+        )
+
     @property
     def gateway_enabled(self):
         return bool(self.url is not None and len(self.url) > 0)
@@ -353,12 +375,18 @@
         perform this operation once.
 
         """
-        # Ensure that request timeout and KERNEL_LAUNCH_TIMEOUT are the same, 
taking the
-        #  greater value of the two.
-        if self.request_timeout < float(GatewayClient.KERNEL_LAUNCH_TIMEOUT):
-            self.request_timeout = float(GatewayClient.KERNEL_LAUNCH_TIMEOUT)
-        elif self.request_timeout > float(GatewayClient.KERNEL_LAUNCH_TIMEOUT):
-            GatewayClient.KERNEL_LAUNCH_TIMEOUT = int(self.request_timeout)
+        # Ensure that request timeout and KERNEL_LAUNCH_TIMEOUT are in sync, 
taking the
+        #  greater value of the two and taking into account the following 
relation:
+        #  request_timeout = KERNEL_LAUNCH_TIME + padding
+        minimum_request_timeout = (
+            float(GatewayClient.KERNEL_LAUNCH_TIMEOUT) + 
self.launch_timeout_pad
+        )
+        if self.request_timeout < minimum_request_timeout:
+            self.request_timeout = minimum_request_timeout
+        elif self.request_timeout > minimum_request_timeout:
+            GatewayClient.KERNEL_LAUNCH_TIMEOUT = int(
+                self.request_timeout - self.launch_timeout_pad
+            )
         # Ensure any adjustments are reflected in env.
         os.environ["KERNEL_LAUNCH_TIMEOUT"] = 
str(GatewayClient.KERNEL_LAUNCH_TIMEOUT)
 
@@ -388,44 +416,112 @@
         if len(self._static_args) == 0:
             self.init_static_args()
 
-        kwargs.update(self._static_args)
+        for arg, static_value in self._static_args.items():
+            if arg == "headers":
+                given_value = kwargs.setdefault(arg, {})
+                if isinstance(given_value, dict):
+                    given_value.update(static_value)
+            else:
+                kwargs[arg] = static_value
+
         return kwargs
 
 
-async def gateway_request(endpoint, **kwargs):
+class RetryableHTTPClient:
+    """
+    Inspired by urllib.util.Retry 
(https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html),
+    this class is initialized with desired retry characteristics, uses a 
recursive method `fetch()` against an instance
+    of `AsyncHTTPClient` which tracks the current retry count across 
applicable request retries.
+    """
+
+    MAX_RETRIES_DEFAULT = 2
+    MAX_RETRIES_CAP = 10  # The upper limit to max_retries value.
+    max_retries: int = int(os.getenv("JUPYTER_GATEWAY_MAX_REQUEST_RETRIES", 
MAX_RETRIES_DEFAULT))
+    max_retries = max(0, min(max_retries, MAX_RETRIES_CAP))  # Enforce 
boundaries
+    retried_methods: ty.Set[str] = {"GET", "DELETE"}
+    retried_errors: ty.Set[int] = {502, 503, 504, 599}
+    retried_exceptions: ty.Set[type] = {ConnectionError}
+    backoff_factor: float = 0.1
+
+    def __init__(self):
+        self.retry_count: int = 0
+        self.client: AsyncHTTPClient = AsyncHTTPClient()
+
+    async def fetch(self, endpoint: str, **kwargs: ty.Any) -> HTTPResponse:
+        """
+        Retryable AsyncHTTPClient.fetch() method.  When the request fails, 
this method will
+        recurse up to max_retries times if the condition deserves a retry.
+        """
+        self.retry_count = 0
+        return await self._fetch(endpoint, **kwargs)
+
+    async def _fetch(self, endpoint: str, **kwargs: ty.Any) -> HTTPResponse:
+        """
+        Performs the fetch against the contained AsyncHTTPClient instance and 
determines
+        if retry is necessary on any exceptions.  If so, retry is performed 
recursively.
+        """
+        try:
+            response: HTTPResponse = await self.client.fetch(endpoint, 
**kwargs)
+        except Exception as e:
+            is_retryable: bool = await self._is_retryable(kwargs["method"], e)
+            if not is_retryable:
+                raise e
+            logging.getLogger("ServerApp").info(
+                f"Attempting retry ({self.retry_count}) against "
+                f"endpoint '{endpoint}'.  Retried error: '{repr(e)}'"
+            )
+            response = await self._fetch(endpoint, **kwargs)
+        return response
+
+    async def _is_retryable(self, method: str, exception: Exception) -> bool:
+        """Determines if the given exception is retryable based on object's 
configuration."""
+
+        if method not in self.retried_methods:
+            return False
+        if self.retry_count == self.max_retries:
+            return False
+
+        # Determine if error is retryable...
+        if isinstance(exception, HTTPClientError):
+            hce: HTTPClientError = exception
+            if hce.code not in self.retried_errors:
+                return False
+        elif not any(isinstance(exception, error) for error in 
self.retried_exceptions):
+            return False
+
+        # Is retryable, wait for backoff, then increment count
+        await asyncio.sleep(self.backoff_factor * (2**self.retry_count))
+        self.retry_count += 1
+        return True
+
+
+async def gateway_request(endpoint: str, **kwargs: ty.Any) -> HTTPResponse:
     """Make an async request to kernel gateway endpoint, returns a response"""
-    client = AsyncHTTPClient()
     kwargs = GatewayClient.instance().load_connection_args(**kwargs)
+    rhc = RetryableHTTPClient()
     try:
-        response = await client.fetch(endpoint, **kwargs)
+        response = await rhc.fetch(endpoint, **kwargs)
     # Trap a set of common exceptions so that we can inform the user that 
their Gateway url is incorrect
     # or the server is not running.
-    # NOTE: We do this here since this handler is called during the Notebook's 
startup and subsequent refreshes
+    # NOTE: We do this here since this handler is called during the server's 
startup and subsequent refreshes
     # of the tree view.
-    except ConnectionRefusedError as e:
+    except HTTPClientError as e:
         raise web.HTTPError(
-            503,
-            "Connection refused from Gateway server url '{}'.  "
-            "Check to be sure the Gateway instance is running.".format(
-                GatewayClient.instance().url
-            ),
+            e.code,
+            f"Error attempting to connect to Gateway server url 
'{GatewayClient.instance().url}'.  "
+            "Ensure gateway url is valid and the Gateway instance is running.",
         ) from e
-    except HTTPError as e:
-        # This can occur if the host is valid (e.g., foo.com) but there's 
nothing there.
+    except ConnectionError as e:
         raise web.HTTPError(
-            e.code,
-            "Error attempting to connect to Gateway server url '{}'.  "
-            "Ensure gateway url is valid and the Gateway instance is 
running.".format(
-                GatewayClient.instance().url
-            ),
+            503,
+            f"ConnectionError was received from Gateway server url 
'{GatewayClient.instance().url}'.  "
+            "Check to be sure the Gateway instance is running.",
         ) from e
     except gaierror as e:
         raise web.HTTPError(
             404,
-            "The Gateway server specified in the gateway_url '{}' doesn't 
appear to be valid.  "
-            "Ensure gateway url is valid and the Gateway instance is 
running.".format(
-                GatewayClient.instance().url
-            ),
+            f"The Gateway server specified in the gateway_url 
'{GatewayClient.instance().url}' doesn't "
+            f"appear to be valid.  Ensure gateway url is valid and the Gateway 
instance is running.",
         ) from e
 
     return response
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/jupyter_server/gateway/managers.py 
new/jupyter_server-1.21.0/jupyter_server/gateway/managers.py
--- old/jupyter_server-1.18.1/jupyter_server/gateway/managers.py        
2022-07-05 22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server/gateway/managers.py        
2022-10-11 13:34:37.000000000 +0200
@@ -326,7 +326,8 @@
         self.kernels_url = url_path_join(
             GatewayClient.instance().url, 
GatewayClient.instance().kernels_endpoint
         )
-        self.kernel_url = self.kernel = self.kernel_id = None
+        self.kernel_url: str
+        self.kernel = self.kernel_id = None
         # simulate busy/activity markers:
         self.execution_state = self.last_activity = None
 
@@ -435,7 +436,12 @@
 
             json_body = json_encode({"name": kernel_name, "env": kernel_env})
 
-            response = await gateway_request(self.kernels_url, method="POST", 
body=json_body)
+            response = await gateway_request(
+                self.kernels_url,
+                method="POST",
+                headers={"Content-Type": "application/json"},
+                body=json_body,
+            )
             self.kernel = json_decode(response.body)
             self.kernel_id = self.kernel["id"]
             self.kernel_url = url_path_join(self.kernels_url, 
url_escape(str(self.kernel_id)))
@@ -466,7 +472,12 @@
             assert self.kernel_url is not None
             kernel_url = self.kernel_url + "/restart"
             self.log.debug("Request restart kernel at: %s", kernel_url)
-            response = await gateway_request(kernel_url, method="POST", 
body=json_encode({}))
+            response = await gateway_request(
+                kernel_url,
+                method="POST",
+                headers={"Content-Type": "application/json"},
+                body=json_encode({}),
+            )
             self.log.debug("Restart kernel response: %d %s", response.code, 
response.reason)
 
     async def interrupt_kernel(self):
@@ -475,7 +486,12 @@
             assert self.kernel_url is not None
             kernel_url = self.kernel_url + "/interrupt"
             self.log.debug("Request interrupt kernel at: %s", kernel_url)
-            response = await gateway_request(kernel_url, method="POST", 
body=json_encode({}))
+            response = await gateway_request(
+                kernel_url,
+                method="POST",
+                headers={"Content-Type": "application/json"},
+                body=json_encode({}),
+            )
             self.log.debug("Interrupt kernel response: %d %s", response.code, 
response.reason)
 
     async def is_alive(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/jupyter_server/services/kernels/handlers.py 
new/jupyter_server-1.21.0/jupyter_server/services/kernels/handlers.py
--- old/jupyter_server-1.18.1/jupyter_server/services/kernels/handlers.py       
2022-07-05 22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server/services/kernels/handlers.py       
2022-10-11 13:34:37.000000000 +0200
@@ -4,6 +4,7 @@
 """
 # Copyright (c) Jupyter Development Team.
 # Distributed under the terms of the Modified BSD License.
+import asyncio
 import json
 from textwrap import dedent
 from traceback import format_tb
@@ -15,8 +16,9 @@
 except ImportError:
     from jupyter_client.jsonutil import date_default as json_default
 
+from concurrent.futures import Future
+
 from tornado import gen, web
-from tornado.concurrent import Future
 from tornado.ioloop import IOLoop
 
 from jupyter_server.auth import authorized
@@ -55,7 +57,9 @@
         else:
             model.setdefault("name", km.default_kernel_name)
 
-        kernel_id = await km.start_kernel(kernel_name=model["name"], 
path=model.get("path"))
+        kernel_id = await ensure_async(
+            km.start_kernel(kernel_name=model["name"], path=model.get("path"))
+        )
         model = await ensure_async(km.kernel_model(kernel_id))
         location = url_path_join(self.base_url, "api", "kernels", 
url_escape(kernel_id))
         self.set_header("Location", location)
@@ -104,6 +108,15 @@
         self.finish()
 
 
+def _ensure_future(f):
+    """Wrap a concurrent future as an asyncio future if there is a running 
loop."""
+    try:
+        asyncio.get_running_loop()
+        return asyncio.wrap_future(f)
+    except RuntimeError:
+        return f
+
+
 class ZMQChannelsHandler(AuthenticatedZMQStreamHandler):
     """There is one ZMQChannelsHandler per running kernel and it oversees all
     the sessions.
@@ -182,7 +195,7 @@
             self.log.debug("Nudge: not nudging busy kernel %s", self.kernel_id)
             f: Future = Future()
             f.set_result(None)
-            return f
+            return _ensure_future(f)
         # Use a transient shell channel to prevent leaking
         # shell responses to the front-end.
         shell_channel = kernel.connect_shell()
@@ -283,7 +296,7 @@
         future = gen.with_timeout(loop.time() + self.kernel_info_timeout, 
both_done)
         # ensure we have no dangling resources or unresolved Futures in case 
of timeout
         future.add_done_callback(finish)
-        return future
+        return _ensure_future(future)
 
     def request_kernel_info(self):
         """send a request for kernel_info"""
@@ -307,7 +320,7 @@
             if not future.done():
                 self.log.debug("Waiting for pending kernel_info request")
             future.add_done_callback(lambda f: 
self._finish_kernel_info(f.result()))
-        return self._kernel_info_future
+        return _ensure_future(self._kernel_info_future)
 
     def _handle_kernel_info_reply(self, msg):
         """process the kernel_info_reply
@@ -384,8 +397,11 @@
         kernel = self.kernel_manager.get_kernel(self.kernel_id)
 
         if hasattr(kernel, "ready"):
+            ready = kernel.ready
+            if not isinstance(ready, asyncio.Future):
+                ready = asyncio.wrap_future(ready)
             try:
-                await kernel.ready
+                await ready
             except Exception as e:
                 kernel.execution_state = "dead"
                 kernel.reason = str(e)
@@ -404,7 +420,7 @@
         loop = IOLoop.current()
         loop.add_timeout(loop.time() + self.kernel_info_timeout, give_up)
         # actually wait for it
-        await future
+        await asyncio.wrap_future(future)
 
     async def get(self, kernel_id):
         self.kernel_id = kernel_id
@@ -700,7 +716,7 @@
 
     def close(self):
         super().close()
-        return self._close_future
+        return _ensure_future(self._close_future)
 
     def on_close(self):
         self.log.debug("Websocket closed %s", self.session_key)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/jupyter_server/services/kernels/kernelmanager.py 
new/jupyter_server-1.21.0/jupyter_server/services/kernels/kernelmanager.py
--- old/jupyter_server-1.18.1/jupyter_server/services/kernels/kernelmanager.py  
2022-07-05 22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server/services/kernels/kernelmanager.py  
2022-10-11 13:34:37.000000000 +0200
@@ -227,15 +227,15 @@
             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)
+            self.log.info("Kernel started: %s", kernel_id)
+            self.log.debug("Kernel args: %r", kwargs)
 
             # Increase the metric of number of kernels running
             # for the relevant kernel type by 1
             
KERNEL_CURRENTLY_RUNNING_TOTAL.labels(type=self._kernels[kernel_id].kernel_name).inc()
 
         else:
-            self.log.info("Using existing kernel: %s" % kernel_id)
+            self.log.info("Using existing kernel: %s", kernel_id)
 
         # Initialize culling if not already
         if not self._initialized_culler:
@@ -246,10 +246,13 @@
     async def _finish_kernel_start(self, kernel_id):
         km = self.get_kernel(kernel_id)
         if hasattr(km, "ready"):
+            ready = km.ready
+            if not isinstance(ready, asyncio.Future):
+                ready = asyncio.wrap_future(ready)
             try:
-                await km.ready
-            except Exception as e:
-                self.log.exception(e)
+                await ready
+            except Exception:
+                self.log.exception("Error waiting for kernel manager ready")
                 return
 
         self._kernel_ports[kernel_id] = km.ports
@@ -276,7 +279,7 @@
         changed_ports = self._get_changed_ports(kernel_id)
         if changed_ports:
             # If changed, update captured ports and return True, else return 
False.
-            self.log.debug(f"Port change detected for kernel: {kernel_id}")
+            self.log.debug("Port change detected for kernel: %s", kernel_id)
             self._kernel_ports[kernel_id] = changed_ports
             return True
         return False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/jupyter_server/services/nbconvert/handlers.py 
new/jupyter_server-1.21.0/jupyter_server/services/nbconvert/handlers.py
--- old/jupyter_server-1.18.1/jupyter_server/services/nbconvert/handlers.py     
2022-07-05 22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server/services/nbconvert/handlers.py     
2022-10-11 13:34:37.000000000 +0200
@@ -11,11 +11,16 @@
 AUTH_RESOURCE = "nbconvert"
 
 
-LOCK = asyncio.Lock()
-
-
 class NbconvertRootHandler(APIHandler):
     auth_resource = AUTH_RESOURCE
+    _exporter_lock: asyncio.Lock
+
+    def initialize(self, **kwargs):
+        super().initialize(**kwargs)
+        # share lock across instances of this handler class
+        if not hasattr(self.__class__, "_exporter_lock"):
+            self.__class__._exporter_lock = asyncio.Lock()
+        self._exporter_lock = self.__class__._exporter_lock
 
     @web.authenticated
     @authorized
@@ -28,22 +33,22 @@
         # Some exporters use the filesystem when instantiating, delegate that
         # to a thread so we don't block the event loop for it.
         exporters = await run_sync(base.get_export_names)
-        for exporter_name in exporters:
-            try:
-                async with LOCK:
+        async with self._exporter_lock:
+            for exporter_name in exporters:
+                try:
                     exporter_class = await run_sync(base.get_exporter, 
exporter_name)
-            except ValueError:
-                # I think the only way this will happen is if the entrypoint
-                # is uninstalled while this method is running
-                continue
-            # XXX: According to the docs, it looks like this should be set to 
None
-            # if the exporter shouldn't be exposed to the front-end and a 
friendly
-            # name if it should. However, none of the built-in exports have it 
defined.
-            # if not exporter_class.export_from_notebook:
-            #    continue
-            res[exporter_name] = {
-                "output_mimetype": exporter_class.output_mimetype,
-            }
+                except ValueError:
+                    # I think the only way this will happen is if the 
entrypoint
+                    # is uninstalled while this method is running
+                    continue
+                # XXX: According to the docs, it looks like this should be set 
to None
+                # if the exporter shouldn't be exposed to the front-end and a 
friendly
+                # name if it should. However, none of the built-in exports 
have it defined.
+                # if not exporter_class.export_from_notebook:
+                #    continue
+                res[exporter_name] = {
+                    "output_mimetype": exporter_class.output_mimetype,
+                }
 
         self.finish(json.dumps(res))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/jupyter_server/services/sessions/sessionmanager.py 
new/jupyter_server-1.21.0/jupyter_server/services/sessions/sessionmanager.py
--- 
old/jupyter_server-1.18.1/jupyter_server/services/sessions/sessionmanager.py    
    2022-07-05 22:22:43.000000000 +0200
+++ 
new/jupyter_server-1.21.0/jupyter_server/services/sessions/sessionmanager.py    
    2022-10-11 13:34:37.000000000 +0200
@@ -1,6 +1,7 @@
 """A base class session manager."""
 # Copyright (c) Jupyter Development Team.
 # Distributed under the terms of the Modified BSD License.
+import os
 import pathlib
 import uuid
 
@@ -267,12 +268,19 @@
         self._pending_sessions.remove(record)
         return result
 
+    def get_kernel_env(self, path):
+        """Return the environment variables that need to be set in the 
kernel"""
+        return {**os.environ, "JPY_SESSION_NAME": path}
+
     async def start_kernel_for_session(self, session_id, path, name, type, 
kernel_name):
         """Start a new kernel for a given session."""
         # allow contents manager to specify kernels cwd
-        kernel_path = self.contents_manager.get_kernel_path(path=path)
+        kernel_path = await 
ensure_async(self.contents_manager.get_kernel_path(path=path))
+        kernel_env = self.get_kernel_env(path)
         kernel_id = await self.kernel_manager.start_kernel(
-            path=kernel_path, kernel_name=kernel_name
+            path=kernel_path,
+            kernel_name=kernel_name,
+            env=kernel_env,
         )
         return kernel_id
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/jupyter_server.egg-info/PKG-INFO 
new/jupyter_server-1.21.0/jupyter_server.egg-info/PKG-INFO
--- old/jupyter_server-1.18.1/jupyter_server.egg-info/PKG-INFO  2022-07-05 
22:23:12.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server.egg-info/PKG-INFO  2022-10-11 
13:35:20.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: jupyter-server
-Version: 1.18.1
+Version: 1.21.0
 Summary: The backend???i.e. core services, APIs, and REST endpoints???to 
Jupyter web applications.
 Home-page: https://jupyter-server.readthedocs.io
 Author: Jupyter Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/jupyter_server.egg-info/requires.txt 
new/jupyter_server-1.21.0/jupyter_server.egg-info/requires.txt
--- old/jupyter_server-1.18.1/jupyter_server.egg-info/requires.txt      
2022-07-05 22:23:12.000000000 +0200
+++ new/jupyter_server-1.21.0/jupyter_server.egg-info/requires.txt      
2022-10-11 13:35:20.000000000 +0200
@@ -26,5 +26,5 @@
 pytest-mock
 pytest-timeout
 pytest-tornasync
-pytest>=6.0
+pytest>=7.0
 requests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/pyproject.toml 
new/jupyter_server-1.21.0/pyproject.toml
--- old/jupyter_server-1.18.1/pyproject.toml    2022-07-05 22:22:58.000000000 
+0200
+++ new/jupyter_server-1.21.0/pyproject.toml    2022-10-11 13:35:02.000000000 
+0200
@@ -34,7 +34,7 @@
 post-version-spec = "dev"
 
 [tool.tbump.version]
-current = "1.18.1"
+current = "1.21.0"
 regex = '''
   (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
   ((?P<channel>a|b|rc|.dev)(?P<release>\d+))?
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/setup.cfg 
new/jupyter_server-1.21.0/setup.cfg
--- old/jupyter_server-1.18.1/setup.cfg 2022-07-05 22:23:12.652353500 +0200
+++ new/jupyter_server-1.21.0/setup.cfg 2022-10-11 13:35:20.650097600 +0200
@@ -65,7 +65,7 @@
        pytest-mock
        pytest-timeout
        pytest-tornasync
-       pytest>=6.0
+       pytest>=7.0
        requests
        requests
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/tests/extension/test_app.py 
new/jupyter_server-1.21.0/tests/extension/test_app.py
--- old/jupyter_server-1.18.1/tests/extension/test_app.py       2022-07-05 
22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/tests/extension/test_app.py       2022-10-11 
13:34:37.000000000 +0200
@@ -161,8 +161,8 @@
     run_sync(jp_serverapp.cleanup_extensions())
     assert [msg for *_, msg in caplog.record_tuples] == [
         "Shutting down 1 extension",
-        f'{extension_name} | extension app "mockextension" stopping',
-        f'{extension_name} | extension app "mockextension" stopped',
+        f"{extension_name} | extension app 'mockextension' stopping",
+        f"{extension_name} | extension app 'mockextension' stopped",
     ]
 
     # check the shutdown method was called once
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/tests/services/kernels/test_api.py 
new/jupyter_server-1.21.0/tests/services/kernels/test_api.py
--- old/jupyter_server-1.18.1/tests/services/kernels/test_api.py        
2022-07-05 22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/tests/services/kernels/test_api.py        
2022-10-11 13:34:37.000000000 +0200
@@ -1,3 +1,4 @@
+import asyncio
 import json
 import os
 import time
@@ -22,7 +23,10 @@
         if getattr(km, "use_pending_kernels", False):
             kernel = km.get_kernel(kernel_id)
             if getattr(kernel, "ready", None):
-                await kernel.ready
+                ready = kernel.ready
+                if not isinstance(ready, asyncio.Future):
+                    ready = asyncio.wrap_future(ready)
+                await ready
 
     return _
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_server-1.18.1/tests/services/sessions/test_api.py 
new/jupyter_server-1.21.0/tests/services/sessions/test_api.py
--- old/jupyter_server-1.18.1/tests/services/sessions/test_api.py       
2022-07-05 22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/tests/services/sessions/test_api.py       
2022-10-11 13:34:37.000000000 +0200
@@ -1,3 +1,4 @@
+import asyncio
 import json
 import os
 import shutil
@@ -163,7 +164,10 @@
             kernel_id = session["kernel"]["id"]
             kernel = mkm.get_kernel(kernel_id)
             if getattr(kernel, "ready", None):
-                await kernel.ready
+                ready = kernel.ready
+                if not isinstance(ready, asyncio.Future):
+                    ready = asyncio.wrap_future(ready)
+                await ready
 
     return _
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_server-1.18.1/tests/test_gateway.py 
new/jupyter_server-1.21.0/tests/test_gateway.py
--- old/jupyter_server-1.18.1/tests/test_gateway.py     2022-07-05 
22:22:43.000000000 +0200
+++ new/jupyter_server-1.21.0/tests/test_gateway.py     2022-10-11 
13:34:37.000000000 +0200
@@ -186,6 +186,7 @@
     monkeypatch.setenv("JUPYTER_GATEWAY_HTTP_USER", mock_http_user)
     monkeypatch.setenv("JUPYTER_GATEWAY_REQUEST_TIMEOUT", "44.4")
     monkeypatch.setenv("JUPYTER_GATEWAY_CONNECT_TIMEOUT", "44.4")
+    monkeypatch.setenv("JUPYTER_GATEWAY_LAUNCH_TIMEOUT_PAD", "1.1")
     yield
     GatewayClient.clear_instance()
 
@@ -198,11 +199,10 @@
         jp_serverapp.gateway_config.connect_timeout == 
jp_serverapp.gateway_config.request_timeout
     )
     assert jp_serverapp.gateway_config.connect_timeout == 44.4
+    assert jp_serverapp.gateway_config.launch_timeout_pad == 1.1
 
     GatewayClient.instance().init_static_args()
-    assert GatewayClient.instance().KERNEL_LAUNCH_TIMEOUT == int(
-        jp_serverapp.gateway_config.request_timeout
-    )
+    assert GatewayClient.instance().KERNEL_LAUNCH_TIMEOUT == 43
 
 
 async def test_gateway_cli_options(jp_configurable_serverapp):
@@ -211,6 +211,7 @@
         "--GatewayClient.http_user=" + mock_http_user,
         "--GatewayClient.connect_timeout=44.4",
         "--GatewayClient.request_timeout=96.0",
+        "--GatewayClient.launch_timeout_pad=5.1",
     ]
 
     GatewayClient.clear_instance()
@@ -221,10 +222,40 @@
     assert app.gateway_config.http_user == mock_http_user
     assert app.gateway_config.connect_timeout == 44.4
     assert app.gateway_config.request_timeout == 96.0
+    assert app.gateway_config.launch_timeout_pad == 5.1
     GatewayClient.instance().init_static_args()
     assert (
-        GatewayClient.instance().KERNEL_LAUNCH_TIMEOUT == 96
-    )  # Ensure KLT gets set from request-timeout
+        GatewayClient.instance().KERNEL_LAUNCH_TIMEOUT == 90
+    )  # Ensure KLT gets set from request-timeout - launch_timeout_pad
+    GatewayClient.clear_instance()
+
+
[email protected](
+    
"request_timeout,kernel_launch_timeout,expected_request_timeout,expected_kernel_launch_timeout",
+    [(50, 10, 50, 45), (10, 50, 55, 50)],
+)
+async def test_gateway_request_timeout_pad_option(
+    jp_configurable_serverapp,
+    monkeypatch,
+    request_timeout,
+    kernel_launch_timeout,
+    expected_request_timeout,
+    expected_kernel_launch_timeout,
+):
+    argv = [
+        f"--GatewayClient.request_timeout={request_timeout}",
+        "--GatewayClient.launch_timeout_pad=5",
+    ]
+
+    GatewayClient.clear_instance()
+    app = jp_configurable_serverapp(argv=argv)
+
+    monkeypatch.setattr(GatewayClient, "KERNEL_LAUNCH_TIMEOUT", 
kernel_launch_timeout)
+    GatewayClient.instance().init_static_args()
+
+    assert app.gateway_config.request_timeout == expected_request_timeout
+    assert GatewayClient.instance().KERNEL_LAUNCH_TIMEOUT == 
expected_kernel_launch_timeout
+
     GatewayClient.clear_instance()
 
 

Reply via email to