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-07-26 19:44:17
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jupyter-server (Old)
and /work/SRC/openSUSE:Factory/.python-jupyter-server.new.1533 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-jupyter-server"
Tue Jul 26 19:44:17 2022 rev:25 rq:990956 version:1.18.1
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-jupyter-server/python-jupyter-server.changes
2022-06-16 18:21:28.960188371 +0200
+++
/work/SRC/openSUSE:Factory/.python-jupyter-server.new.1533/python-jupyter-server.changes
2022-07-26 19:44:42.753133556 +0200
@@ -1,0 +2,20 @@
+Mon Jul 25 00:12:10 UTC 2022 - Arun Persaud <[email protected]>
+
+- update to version 1.18.1:
+ * Bugs fixed
+ + Notify ChannelQueue that the response router thread is finishing
+ #896 (@CiprianAnton)
+ + Make ChannelQueue.get_msg true async #892 (@CiprianAnton)
+
+- changes from version 1.18.0:
+ * Enhancements made
+ + Show import error when faiing to load an extension #878 (@minrk)
+ * Bugs fixed
+ + Fix gateway kernel shutdown #874 (@kevin-bates)
+ * Maintenance and upkeep improvements
+ + suppress tornado deprecation warnings #882 (@minrk)
+ + Normalize os_path #886 (@martinRenou)
+ + Fix lint #867 (@blink1073)
+ + Fix sphinx 5.0 support #865 (@blink1073)
+
+-------------------------------------------------------------------
Old:
----
jupyter_server-1.17.1.tar.gz
New:
----
jupyter_server-1.18.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-jupyter-server.spec ++++++
--- /var/tmp/diff_new_pack.qirGis/_old 2022-07-26 19:44:43.129075463 +0200
+++ /var/tmp/diff_new_pack.qirGis/_new 2022-07-26 19:44:43.133074845 +0200
@@ -31,13 +31,13 @@
%bcond_with libalternatives
%endif
Name: python-jupyter-server%{psuffix}
-Version: 1.17.1
+Version: 1.18.1
Release: 0
Summary: The backend to Jupyter web applications
License: BSD-3-Clause
Group: Development/Languages/Python
URL: https://jupyter-server.readthedocs.io
-Source:
https://files.pythonhosted.org/packages/source/j/jupyter-server/jupyter_server-%{version}.tar.gz
+Source:
https://files.pythonhosted.org/packages/source/j/jupyter_server/jupyter_server-%{version}.tar.gz
BuildRequires: %{python_module base >= 3.7}
BuildRequires: %{python_module jupyter_packaging}
BuildRequires: %{python_module setuptools}
++++++ jupyter_server-1.17.1.tar.gz -> jupyter_server-1.18.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.17.1/CHANGELOG.md
new/jupyter_server-1.18.1/CHANGELOG.md
--- old/jupyter_server-1.17.1/CHANGELOG.md 2022-06-07 19:07:21.000000000
+0200
+++ new/jupyter_server-1.18.1/CHANGELOG.md 2022-07-05 22:22:43.000000000
+0200
@@ -4,6 +4,58 @@
<!-- <START NEW CHANGELOG ENTRY> -->
+## 1.18.1
+
+([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.18.0...65d779a12b6e4123de0767de6547e6cdf2d5e7e9))
+
+### Bugs fixed
+
+- Notify ChannelQueue that the response router thread is finishing
[#896](https://github.com/jupyter-server/jupyter_server/pull/896)
([@CiprianAnton](https://github.com/CiprianAnton))
+- Make ChannelQueue.get_msg true async
[#892](https://github.com/jupyter-server/jupyter_server/pull/892)
([@CiprianAnton](https://github.com/CiprianAnton))
+
+### Contributors to this release
+
+([GitHub contributors page for this
release](https://github.com/jupyter-server/jupyter_server/graphs/contributors?from=2022-06-23&to=2022-07-05&type=c))
+
+[@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))
+
+### Enhancements made
+
+- Show import error when faiing to load an extension
[#878](https://github.com/jupyter-server/jupyter_server/pull/878)
([@minrk](https://github.com/minrk))
+
+### Bugs fixed
+
+- Fix gateway kernel shutdown
[#874](https://github.com/jupyter-server/jupyter_server/pull/874)
([@kevin-bates](https://github.com/kevin-bates))
+
+### Maintenance and upkeep improvements
+
+- suppress tornado deprecation warnings
[#882](https://github.com/jupyter-server/jupyter_server/pull/882)
([@minrk](https://github.com/minrk))
+- Normalize os_path
[#886](https://github.com/jupyter-server/jupyter_server/pull/886)
([@martinRenou](https://github.com/martinRenou))
+- Fix lint [#867](https://github.com/jupyter-server/jupyter_server/pull/867)
([@blink1073](https://github.com/blink1073))
+- Fix sphinx 5.0 support
[#865](https://github.com/jupyter-server/jupyter_server/pull/865)
([@blink1073](https://github.com/blink1073))
+
+### Documentation improvements
+
+- Add changelog entry for 1.17.1
[#871](https://github.com/jupyter-server/jupyter_server/pull/871)
([@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-06-07&to=2022-06-23&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2022-06-07..2022-06-23&type=Issues)
|
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2022-06-07..2022-06-23&type=Issues)
|
[@davidbrochart](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Adavidbrochart+updated%3A2022-06-07..2022-06-23&type=Issues)
|
[@kevin-bates](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Akevin-bates+updated%3A2022-06-07..2022-06-23&type=Issues)
|
[@meeseeksmachine](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ameeseeksmachine+updated%3A2022-06-07..2022-06-23&type=Issues)
+
+## 1.17.1
+
+([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.17.0...v1.17.1)
+
+- Address security advisory
[GHSA-q874-g24w-4q9g](https://github.com/jupyter-server/jupyter_server/security/advisories/GHSA-q874-g24w-4q9g).
+
## 1.17.0
([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.16.0...2b296099777d50aa86f67faf94d5cbfde906b169))
@@ -28,8 +80,6 @@
[@blink1073](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ablink1073+updated%3A2022-03-29..2022-04-27&type=Issues)
|
[@codecov-commenter](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Acodecov-commenter+updated%3A2022-03-29..2022-04-27&type=Issues)
|
[@davidbrochart](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Adavidbrochart+updated%3A2022-03-29..2022-04-27&type=Issues)
|
[@echarles](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Aecharles+updated%3A2022-03-29..2022-04-27&type=Issues)
|
[@kevin-bates](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Akevin-bates+updated%3A2022-03-29..2022-04-27&type=Issues)
|
[@meeseeksdev](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3Ameeseeksdev+updated%3A2022-03-29..2022-04-27&type=Issues)
| [@meeseeksmachine](https://github.com/search?q=repo%3Ajupyter-server%2Fju
pyter_server+involves%3Ameeseeksmachine+updated%3A2022-03-29..2022-04-27&type=Issues)
|
[@Wh1isper](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AWh1isper+updated%3A2022-03-29..2022-04-27&type=Issues)
|
[@Zsailer](https://github.com/search?q=repo%3Ajupyter-server%2Fjupyter_server+involves%3AZsailer+updated%3A2022-03-29..2022-04-27&type=Issues)
-<!-- <END NEW CHANGELOG ENTRY> -->
-
## 1.16.0
([Full
Changelog](https://github.com/jupyter-server/jupyter_server/compare/v1.15.6...d32b887ae2c3b77fe3ae67ba79c3d3c6713c0d8a))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.17.1/PKG-INFO
new/jupyter_server-1.18.1/PKG-INFO
--- old/jupyter_server-1.17.1/PKG-INFO 2022-06-07 19:16:36.606193500 +0200
+++ new/jupyter_server-1.18.1/PKG-INFO 2022-07-05 22:23:12.652353500 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: jupyter_server
-Version: 1.17.1
+Version: 1.18.1
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.17.1/docs/source/conf.py
new/jupyter_server-1.18.1/docs/source/conf.py
--- old/jupyter_server-1.17.1/docs/source/conf.py 2022-06-07
19:16:17.000000000 +0200
+++ new/jupyter_server-1.18.1/docs/source/conf.py 2022-07-05
22:22:58.000000000 +0200
@@ -107,7 +107,7 @@
# |version| and |release|, also used in various other places throughout the
# built documents.
#
-__version__ = "1.17.1"
+__version__ = "1.18.1"
# The short X.Y version.
version_parsed = parse_version(__version__)
version = f"{version_parsed.major}.{version_parsed.minor}"
@@ -117,7 +117,7 @@
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
-language = None
+language = "en"
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.17.1/jupyter_server/_version.py
new/jupyter_server-1.18.1/jupyter_server/_version.py
--- old/jupyter_server-1.17.1/jupyter_server/_version.py 2022-06-07
19:16:17.000000000 +0200
+++ new/jupyter_server-1.18.1/jupyter_server/_version.py 2022-07-05
22:22:58.000000000 +0200
@@ -2,5 +2,5 @@
store the current version info of the server.
"""
-version_info = (1, 17, 1, "", "")
+version_info = (1, 18, 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.17.1/jupyter_server/extension/manager.py
new/jupyter_server-1.18.1/jupyter_server/extension/manager.py
--- old/jupyter_server-1.17.1/jupyter_server/extension/manager.py
2022-06-07 19:07:21.000000000 +0200
+++ new/jupyter_server-1.18.1/jupyter_server/extension/manager.py
2022-07-05 22:22:43.000000000 +0200
@@ -175,10 +175,10 @@
self._extension_points = {}
try:
self._module, self._metadata = get_metadata(name)
- except ImportError:
+ except ImportError as e:
raise ExtensionModuleNotFound(
- "The module '{name}' could not be found. Are you "
- "sure the extension is installed?".format(name=name)
+ "The module '{name}' could not be found ({e}). Are you "
+ "sure the extension is installed?".format(name=name, e=e)
)
# Create extension point interfaces for each extension path.
for m in self._metadata:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.17.1/jupyter_server/gateway/managers.py
new/jupyter_server-1.18.1/jupyter_server/gateway/managers.py
--- old/jupyter_server-1.17.1/jupyter_server/gateway/managers.py
2022-06-07 19:07:21.000000000 +0200
+++ new/jupyter_server-1.18.1/jupyter_server/gateway/managers.py
2022-07-05 22:22:43.000000000 +0200
@@ -1,11 +1,13 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
+import asyncio
import datetime
import json
import os
from logging import Logger
-from queue import Queue
+from queue import Empty, Queue
from threading import Thread
+from time import monotonic
from typing import Any, Dict, Optional
import websocket
@@ -92,7 +94,7 @@
The uuid of the kernel.
"""
model = None
- km = self.get_kernel(kernel_id)
+ km = self.get_kernel(str(kernel_id))
if km:
model = km.kernel
return model
@@ -166,13 +168,14 @@
async def shutdown_all(self, now=False):
"""Shutdown all kernels."""
- for kernel_id in self._kernels:
+ kids = list(self._kernels)
+ for kernel_id in kids:
km = self.get_kernel(kernel_id)
await km.shutdown_kernel(now=now)
self.remove_kernel(kernel_id)
async def cull_kernels(self):
- """Override cull_kernels so we can be sure their state is current."""
+ """Override cull_kernels, so we can be sure their state is current."""
await self.list_kernels()
await super().cull_kernels()
@@ -295,7 +298,7 @@
kernel_manager =
Instance("jupyter_server.gateway.managers.GatewayMappingKernelManager")
async def kernel_culled(self, kernel_id):
- """Checks if the kernel is still considered alive and returns true if
its not found."""
+ """Checks if the kernel is still considered alive and returns true if
it's not found."""
kernel = None
try:
km = self.kernel_manager.get_kernel(kernel_id)
@@ -387,7 +390,7 @@
if isinstance(self.parent, AsyncMappingKernelManager):
# Update connections only if there's a mapping kernel manager
parent for
# this kernel manager. The current kernel manager instance
may not have
- # an parent instance if, say, a server extension is using
another application
+ # a parent instance if, say, a server extension is using
another application
# (e.g., papermill) that uses a KernelManager instance
directly.
self.parent._kernel_connections[self.kernel_id] =
int(model["connections"])
@@ -448,8 +451,14 @@
if self.has_kernel:
self.log.debug("Request shutdown kernel at: %s", self.kernel_url)
- response = await gateway_request(self.kernel_url, method="DELETE")
- self.log.debug("Shutdown kernel response: %d %s", response.code,
response.reason)
+ try:
+ response = await gateway_request(self.kernel_url,
method="DELETE")
+ self.log.debug("Shutdown kernel response: %d %s",
response.code, response.reason)
+ except web.HTTPError as error:
+ if error.status_code == 404:
+ self.log.debug("Shutdown kernel response: kernel not found
(ignored)")
+ else:
+ raise
async def restart_kernel(self, **kw):
"""Restarts a kernel via HTTP."""
@@ -489,16 +498,35 @@
class ChannelQueue(Queue):
channel_name: Optional[str] = None
+ response_router_finished: bool
def __init__(self, channel_name: str, channel_socket: websocket.WebSocket,
log: Logger):
super().__init__()
self.channel_name = channel_name
self.channel_socket = channel_socket
self.log = log
+ self.response_router_finished = False
+
+ async def _async_get(self, timeout=None):
+ if timeout is None:
+ timeout = float("inf")
+ elif timeout < 0:
+ raise ValueError("'timeout' must be a non-negative number")
+ end_time = monotonic() + timeout
+
+ while True:
+ try:
+ return self.get(block=False)
+ except Empty:
+ if self.response_router_finished:
+ raise RuntimeError("Response router had finished")
+ if monotonic() > end_time:
+ raise
+ await asyncio.sleep(0)
async def get_msg(self, *args: Any, **kwargs: Any) -> dict:
timeout = kwargs.get("timeout", 1)
- msg = self.get(timeout=timeout)
+ msg = await self._async_get(timeout=timeout)
self.log.debug(
"Received message on channel: {}, msg_id: {}, msg_type: {}".format(
self.channel_name, msg["msg_id"], msg["msg_type"] if msg else
"null"
@@ -518,7 +546,7 @@
@staticmethod
def serialize_datetime(dt):
- if isinstance(dt, (datetime.datetime)):
+ if isinstance(dt, datetime.datetime):
return dt.timestamp()
return None
@@ -573,19 +601,21 @@
# flag for whether execute requests should be allowed to call raw_input:
allow_stdin = False
- _channels_stopped = False
- _channel_queues: Optional[dict] = {}
+ _channels_stopped: bool
+ _channel_queues: Optional[Dict[str, ChannelQueue]]
_control_channel: Optional[ChannelQueue]
_hb_channel: Optional[ChannelQueue]
_stdin_channel: Optional[ChannelQueue]
_iopub_channel: Optional[ChannelQueue]
_shell_channel: Optional[ChannelQueue]
- def __init__(self, **kwargs):
+ def __init__(self, kernel_id, **kwargs):
super().__init__(**kwargs)
- self.kernel_id = kwargs["kernel_id"]
+ self.kernel_id = kernel_id
self.channel_socket: Optional[websocket.WebSocket] = None
self.response_router: Optional[Thread] = None
+ self._channels_stopped = False
+ self._channel_queues = {}
#
--------------------------------------------------------------------------
# Channel management methods
@@ -595,7 +625,7 @@
"""Starts the channels for this kernel.
For this class, we establish a websocket connection to the destination
- and setup the channel-based queues on which applicable messages will
+ and set up the channel-based queues on which applicable messages will
be posted.
"""
@@ -606,10 +636,11 @@
"channels",
)
# Gather cert info in case where ssl is desired...
- ssl_options = {}
- ssl_options["ca_certs"] = GatewayClient.instance().ca_certs
- ssl_options["certfile"] = GatewayClient.instance().client_cert
- ssl_options["keyfile"] = GatewayClient.instance().client_key
+ ssl_options = {
+ "ca_certs": GatewayClient.instance().ca_certs,
+ "certfile": GatewayClient.instance().client_cert,
+ "keyfile": GatewayClient.instance().client_key,
+ }
self.channel_socket = websocket.create_connection(
ws_url,
@@ -617,13 +648,14 @@
enable_multithread=True,
sslopt=ssl_options,
)
- self.response_router = Thread(target=self._route_responses)
- self.response_router.start()
await ensure_async(
super().start_channels(shell=shell, iopub=iopub, stdin=stdin,
hb=hb, control=control)
)
+ self.response_router = Thread(target=self._route_responses)
+ self.response_router.start()
+
def stop_channels(self):
"""Stops all the running channels for this kernel.
@@ -720,12 +752,17 @@
self._channel_queues[channel].put_nowait(response_message)
except websocket.WebSocketConnectionClosedException:
- pass # websocket closure most likely due to shutdown
+ pass # websocket closure most likely due to shut down
except BaseException as be:
if not self._channels_stopped:
self.log.warning(f"Unexpected exception encountered ({be})")
+ # Notify channel queues that this thread had finished and no more
messages are being received
+ assert self._channel_queues is not None
+ for channel_queue in self._channel_queues.values():
+ channel_queue.response_router_finished = True
+
self.log.debug("Response router thread exiting...")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.17.1/jupyter_server/utils.py
new/jupyter_server-1.18.1/jupyter_server/utils.py
--- old/jupyter_server-1.17.1/jupyter_server/utils.py 2022-06-07
19:02:17.000000000 +0200
+++ new/jupyter_server-1.18.1/jupyter_server/utils.py 2022-07-05
22:22:43.000000000 +0200
@@ -111,7 +111,7 @@
parts = path.strip("/").split("/")
parts = [p for p in parts if p != ""] # remove duplicate splits
path = os.path.join(root, *parts)
- return path
+ return os.path.normpath(path)
def to_api_path(os_path, root=""):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.17.1/jupyter_server.egg-info/PKG-INFO
new/jupyter_server-1.18.1/jupyter_server.egg-info/PKG-INFO
--- old/jupyter_server-1.17.1/jupyter_server.egg-info/PKG-INFO 2022-06-07
19:16:36.000000000 +0200
+++ new/jupyter_server-1.18.1/jupyter_server.egg-info/PKG-INFO 2022-07-05
22:23:12.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: jupyter-server
-Version: 1.17.1
+Version: 1.18.1
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.17.1/pyproject.toml
new/jupyter_server-1.18.1/pyproject.toml
--- old/jupyter_server-1.17.1/pyproject.toml 2022-06-07 19:16:17.000000000
+0200
+++ new/jupyter_server-1.18.1/pyproject.toml 2022-07-05 22:22:58.000000000
+0200
@@ -18,7 +18,9 @@
# timeout_method = "thread"
filterwarnings = [
"error",
- "ignore:There is no current event loop:DeprecationWarning",
+ "module:make_current is deprecated:DeprecationWarning",
+ "module:clear_current is deprecated:DeprecationWarning",
+ "module:There is no current event loop:DeprecationWarning",
"ignore:Passing a schema to Validator.iter_errors:DeprecationWarning",
"ignore:unclosed <socket.socket:ResourceWarning",
"ignore:unclosed event loop:ResourceWarning",
@@ -32,7 +34,7 @@
post-version-spec = "dev"
[tool.tbump.version]
-current = "1.17.1"
+current = "1.18.1"
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.17.1/tests/services/contents/test_api.py
new/jupyter_server-1.18.1/tests/services/contents/test_api.py
--- old/jupyter_server-1.17.1/tests/services/contents/test_api.py
2022-06-07 19:07:47.000000000 +0200
+++ new/jupyter_server-1.18.1/tests/services/contents/test_api.py
2022-07-05 22:22:43.000000000 +0200
@@ -230,19 +230,20 @@
)
assert expected_http_error(e, 400)
+
@pytest.mark.skipif(sys.platform == "win32", reason="Disabled retrieving
hidden files on Windows")
async def test_get_404_hidden(jp_fetch, contents, contents_dir):
# Create text files
- hidden_dir = contents_dir / '.hidden'
+ hidden_dir = contents_dir / ".hidden"
hidden_dir.mkdir(parents=True, exist_ok=True)
- txt = f"visible text file in hidden dir"
- txtname = hidden_dir.joinpath(f"visible.txt")
+ txt = "visible text file in hidden dir"
+ txtname = hidden_dir.joinpath("visible.txt")
txtname.write_text(txt, encoding="utf-8")
- txt2 = f"hidden text file"
- txtname2 = contents_dir.joinpath(f".hidden.txt")
+ txt2 = "hidden text file"
+ txtname2 = contents_dir.joinpath(".hidden.txt")
txtname2.write_text(txt2, encoding="utf-8")
-
+
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
await jp_fetch(
"api",
@@ -261,6 +262,7 @@
)
assert expected_http_error(e, 404)
+
@pytest.mark.parametrize("path,name", dirs)
async def test_get_binary_file_contents(jp_fetch, contents, path, name):
blobname = name + ".blob"
@@ -441,48 +443,38 @@
@pytest.mark.skipif(sys.platform == "win32", reason="Disabled uploading hidden
files on Windows")
async def test_upload_txt_hidden(jp_fetch, contents, contents_dir):
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
- body = '??nicode t??xt'
+ body = "??nicode t??xt"
model = {
- 'content' : body,
- 'format' : 'text',
- 'type' : 'file',
+ "content": body,
+ "format": "text",
+ "type": "file",
}
- path = '.hidden/Upload t??st.txt'
+ path = ".hidden/Upload t??st.txt"
await jp_fetch("api", "contents", path, method="PUT",
body=json.dumps(model))
assert expected_http_error(e, 400)
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
- body = '??nicode t??xt'
- model = {
- 'content' : body,
- 'format' : 'text',
- 'type' : 'file',
- 'path': '.hidden/test.txt'
- }
- path = 'Upload t??st.txt'
+ body = "??nicode t??xt"
+ model = {"content": body, "format": "text", "type": "file", "path":
".hidden/test.txt"}
+ path = "Upload t??st.txt"
await jp_fetch("api", "contents", path, method="PUT",
body=json.dumps(model))
assert expected_http_error(e, 400)
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
- body = '??nicode t??xt'
+ body = "??nicode t??xt"
model = {
- 'content' : body,
- 'format' : 'text',
- 'type' : 'file',
+ "content": body,
+ "format": "text",
+ "type": "file",
}
- path = '.hidden.txt'
+ path = ".hidden.txt"
await jp_fetch("api", "contents", path, method="PUT",
body=json.dumps(model))
assert expected_http_error(e, 400)
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
- body = '??nicode t??xt'
- model = {
- 'content' : body,
- 'format' : 'text',
- 'type' : 'file',
- 'path': '.hidden.txt'
- }
- path = 'Upload t??st.txt'
+ body = "??nicode t??xt"
+ model = {"content": body, "format": "text", "type": "file", "path":
".hidden.txt"}
+ path = "Upload t??st.txt"
await jp_fetch("api", "contents", path, method="PUT",
body=json.dumps(model))
assert expected_http_error(e, 400)
@@ -581,7 +573,11 @@
@pytest.mark.skipif(sys.platform == "win32", reason="Disabled copying hidden
files on Windows")
-async def test_copy_put_400_hidden(jp_fetch, contents, contents_dir,):
+async def test_copy_put_400_hidden(
+ jp_fetch,
+ contents,
+ contents_dir,
+):
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
await jp_fetch(
"api",
@@ -591,7 +587,7 @@
body=json.dumps({"copy_from": "new.txt"}),
)
assert expected_http_error(e, 400)
-
+
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
await jp_fetch(
"api",
@@ -601,7 +597,7 @@
body=json.dumps({"copy_from": ".hidden/new.txt"}),
)
assert expected_http_error(e, 400)
-
+
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
await jp_fetch(
"api",
@@ -611,7 +607,7 @@
body=json.dumps({"copy_from": "new.txt"}),
)
assert expected_http_error(e, 400)
-
+
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
await jp_fetch(
"api",
@@ -636,16 +632,20 @@
@pytest.mark.skipif(sys.platform == "win32", reason="Disabled copying hidden
files on Windows")
-async def test_copy_400_hidden(jp_fetch, contents, contents_dir,):
+async def test_copy_400_hidden(
+ jp_fetch,
+ contents,
+ contents_dir,
+):
# Create text files
- hidden_dir = contents_dir / '.hidden'
+ hidden_dir = contents_dir / ".hidden"
hidden_dir.mkdir(parents=True, exist_ok=True)
- txt = f"visible text file in hidden dir"
- txtname = hidden_dir.joinpath(f"new.txt")
+ txt = "visible text file in hidden dir"
+ txtname = hidden_dir.joinpath("new.txt")
txtname.write_text(txt, encoding="utf-8")
- paths = ['new.txt', '.hidden.txt']
+ paths = ["new.txt", ".hidden.txt"]
for name in paths:
txt = f"{name} text file"
txtname = contents_dir.joinpath(f"{name}.txt")
@@ -660,7 +660,7 @@
body=json.dumps({"copy_from": "new.txt"}),
)
assert expected_http_error(e, 400)
-
+
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
await jp_fetch(
"api",
@@ -689,7 +689,7 @@
method="POST",
body=json.dumps({"copy_from": ".hidden.txt"}),
)
- assert expected_http_error(e, 400)
+ assert expected_http_error(e, 400)
@pytest.mark.parametrize("path,name", dirs)
@@ -729,20 +729,22 @@
await jp_fetch("api", "contents", "?? b", method="GET")
assert expected_http_error(e, 404)
+
@pytest.mark.skipif(sys.platform == "win32", reason="Disabled deleting hidden
dirs on Windows")
async def test_delete_hidden_dir(jp_fetch, contents):
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
await jp_fetch("api", "contents", ".hidden", method="DELETE")
assert expected_http_error(e, 400)
+
@pytest.mark.skipif(sys.platform == "win32", reason="Disabled deleting hidden
dirs on Windows")
async def test_delete_hidden_file(jp_fetch, contents):
- #Test deleting file in a hidden directory
+ # Test deleting file in a hidden directory
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
await jp_fetch("api", "contents", ".hidden/test.txt", method="DELETE")
assert expected_http_error(e, 400)
- #Test deleting a hidden file
+ # Test deleting a hidden file
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
await jp_fetch("api", "contents", ".hidden.txt", method="DELETE")
assert expected_http_error(e, 400)
@@ -778,11 +780,12 @@
assert "z.ipynb" in nbnames
assert "a.ipynb" not in nbnames
+
@pytest.mark.skipif(sys.platform == "win32", reason="Disabled copying hidden
files on Windows")
async def test_rename_400_hidden(jp_fetch, jp_base_url, contents,
contents_dir):
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
- old_path = '.hidden/old.txt'
- new_path = 'new.txt'
+ old_path = ".hidden/old.txt"
+ new_path = "new.txt"
# Rename the file
r = await jp_fetch(
"api",
@@ -792,10 +795,10 @@
body=json.dumps({"path": new_path}),
)
assert expected_http_error(e, 400)
-
+
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
- old_path = 'old.txt'
- new_path = '.hidden/new.txt'
+ old_path = "old.txt"
+ new_path = ".hidden/new.txt"
# Rename the file
r = await jp_fetch(
"api",
@@ -805,10 +808,10 @@
body=json.dumps({"path": new_path}),
)
assert expected_http_error(e, 400)
-
+
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
- old_path = '.hidden.txt'
- new_path = 'new.txt'
+ old_path = ".hidden.txt"
+ new_path = "new.txt"
# Rename the file
r = await jp_fetch(
"api",
@@ -820,8 +823,8 @@
assert expected_http_error(e, 400)
with pytest.raises(tornado.httpclient.HTTPClientError) as e:
- old_path = 'old.txt'
- new_path = '.hidden.txt'
+ old_path = "old.txt"
+ new_path = ".hidden.txt"
# Rename the file
r = await jp_fetch(
"api",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/jupyter_server-1.17.1/tests/services/contents/test_manager.py
new/jupyter_server-1.18.1/tests/services/contents/test_manager.py
--- old/jupyter_server-1.17.1/tests/services/contents/test_manager.py
2022-06-07 19:07:47.000000000 +0200
+++ new/jupyter_server-1.18.1/tests/services/contents/test_manager.py
2022-07-05 22:22:43.000000000 +0200
@@ -259,80 +259,81 @@
except HTTPError as e:
assert e.status_code == 403
[email protected](sys.platform.startswith('win'), reason="Can't test hidden
files on Windows")
+
[email protected](sys.platform.startswith("win"), reason="Can't test hidden
files on Windows")
async def test_400(jp_file_contents_manager_class, tmp_path):
- #Test Delete behavior
- #Test delete of file in hidden directory
+ # Test Delete behavior
+ # Test delete of file in hidden directory
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = '.hidden'
- file_in_hidden_path = os.path.join(hidden_dir,'visible.txt')
+ hidden_dir = ".hidden"
+ file_in_hidden_path = os.path.join(hidden_dir, "visible.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- os_path = cm._get_os_path(model['path'])
+ os_path = cm._get_os_path(model["path"])
try:
result = await ensure_async(cm.delete_file(os_path))
except HTTPError as e:
assert e.status_code == 400
- #Test delete hidden file in visible directory
+ # Test delete hidden file in visible directory
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = 'visible'
- file_in_hidden_path = os.path.join(hidden_dir,'.hidden.txt')
+ hidden_dir = "visible"
+ file_in_hidden_path = os.path.join(hidden_dir, ".hidden.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- os_path = cm._get_os_path(model['path'])
+ os_path = cm._get_os_path(model["path"])
try:
result = await ensure_async(cm.delete_file(os_path))
except HTTPError as e:
assert e.status_code == 400
- #Test Save behavior
- #Test save of file in hidden directory
+ # Test Save behavior
+ # Test save of file in hidden directory
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = '.hidden'
- file_in_hidden_path = os.path.join(hidden_dir,'visible.txt')
+ hidden_dir = ".hidden"
+ file_in_hidden_path = os.path.join(hidden_dir, "visible.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- os_path = cm._get_os_path(model['path'])
+ os_path = cm._get_os_path(model["path"])
try:
- result = await ensure_async(cm.save(model,path=os_path))
+ result = await ensure_async(cm.save(model, path=os_path))
except HTTPError as e:
assert e.status_code == 400
- #Test save hidden file in visible directory
+ # Test save hidden file in visible directory
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = 'visible'
- file_in_hidden_path = os.path.join(hidden_dir,'.hidden.txt')
+ hidden_dir = "visible"
+ file_in_hidden_path = os.path.join(hidden_dir, ".hidden.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- os_path = cm._get_os_path(model['path'])
+ os_path = cm._get_os_path(model["path"])
try:
- result = await ensure_async(cm.save(model,path=os_path))
+ result = await ensure_async(cm.save(model, path=os_path))
except HTTPError as e:
assert e.status_code == 400
- #Test rename behavior
- #Test rename with source file in hidden directory
+ # Test rename behavior
+ # Test rename with source file in hidden directory
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = '.hidden'
- file_in_hidden_path = os.path.join(hidden_dir,'visible.txt')
+ hidden_dir = ".hidden"
+ file_in_hidden_path = os.path.join(hidden_dir, "visible.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- old_path = cm._get_os_path(model['path'])
+ old_path = cm._get_os_path(model["path"])
new_path = "new.txt"
try:
@@ -340,15 +341,15 @@
except HTTPError as e:
assert e.status_code == 400
- #Test rename of dest file in hidden directory
+ # Test rename of dest file in hidden directory
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = '.hidden'
- file_in_hidden_path = os.path.join(hidden_dir,'visible.txt')
+ hidden_dir = ".hidden"
+ file_in_hidden_path = os.path.join(hidden_dir, "visible.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- new_path = cm._get_os_path(model['path'])
+ new_path = cm._get_os_path(model["path"])
old_path = "old.txt"
try:
@@ -356,15 +357,15 @@
except HTTPError as e:
assert e.status_code == 400
- #Test rename with hidden source file in visible directory
+ # Test rename with hidden source file in visible directory
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = 'visible'
- file_in_hidden_path = os.path.join(hidden_dir,'.hidden.txt')
+ hidden_dir = "visible"
+ file_in_hidden_path = os.path.join(hidden_dir, ".hidden.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- old_path = cm._get_os_path(model['path'])
+ old_path = cm._get_os_path(model["path"])
new_path = "new.txt"
try:
@@ -372,15 +373,15 @@
except HTTPError as e:
assert e.status_code == 400
- #Test rename with hidden dest file in visible directory
+ # Test rename with hidden dest file in visible directory
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = 'visible'
- file_in_hidden_path = os.path.join(hidden_dir,'.hidden.txt')
+ hidden_dir = "visible"
+ file_in_hidden_path = os.path.join(hidden_dir, ".hidden.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- new_path = cm._get_os_path(model['path'])
+ new_path = cm._get_os_path(model["path"])
old_path = "old.txt"
try:
@@ -388,38 +389,40 @@
except HTTPError as e:
assert e.status_code == 400
[email protected](sys.platform.startswith('win'), reason="Can't test hidden
files on Windows")
+
[email protected](sys.platform.startswith("win"), reason="Can't test hidden
files on Windows")
async def test_404(jp_file_contents_manager_class, tmp_path):
- #Test visible file in hidden folder
+ # Test visible file in hidden folder
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = '.hidden'
- file_in_hidden_path = os.path.join(hidden_dir,'visible.txt')
+ hidden_dir = ".hidden"
+ file_in_hidden_path = os.path.join(hidden_dir, "visible.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- os_path = cm._get_os_path(model['path'])
+ os_path = cm._get_os_path(model["path"])
try:
- result = await ensure_async(cm.get(os_path, 'w'))
+ result = await ensure_async(cm.get(os_path, "w"))
except HTTPError as e:
assert e.status_code == 404
- #Test hidden file in visible folder
+ # Test hidden file in visible folder
with pytest.raises(HTTPError) as excinfo:
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
- hidden_dir = 'visible'
- file_in_hidden_path = os.path.join(hidden_dir,'.hidden.txt')
+ hidden_dir = "visible"
+ file_in_hidden_path = os.path.join(hidden_dir, ".hidden.txt")
_make_dir(cm, hidden_dir)
model = await ensure_async(cm.new(path=file_in_hidden_path))
- os_path = cm._get_os_path(model['path'])
+ os_path = cm._get_os_path(model["path"])
try:
- result = await ensure_async(cm.get(os_path, 'w'))
+ result = await ensure_async(cm.get(os_path, "w"))
except HTTPError as e:
assert e.status_code == 404
+
async def test_escape_root(jp_file_contents_manager_class, tmp_path):
td = str(tmp_path)
cm = jp_file_contents_manager_class(root_dir=td)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/jupyter_server-1.17.1/tests/test_gateway.py
new/jupyter_server-1.18.1/tests/test_gateway.py
--- old/jupyter_server-1.17.1/tests/test_gateway.py 2022-06-07
19:02:17.000000000 +0200
+++ new/jupyter_server-1.18.1/tests/test_gateway.py 2022-07-05
22:22:43.000000000 +0200
@@ -1,17 +1,24 @@
"""Test GatewayClient"""
+import asyncio
import json
+import logging
import os
import uuid
from datetime import datetime
from io import BytesIO
-from unittest.mock import patch
+from queue import Empty
+from unittest.mock import MagicMock, patch
import pytest
import tornado
from tornado.httpclient import HTTPRequest, HTTPResponse
from tornado.web import HTTPError
-from jupyter_server.gateway.managers import GatewayClient
+from jupyter_server.gateway.managers import (
+ ChannelQueue,
+ GatewayClient,
+ GatewayKernelManager,
+)
from jupyter_server.utils import ensure_async
from .utils import expected_http_error
@@ -135,6 +142,9 @@
# Shutdown existing kernel
if endpoint.rfind("/api/kernels/") >= 0 and method == "DELETE":
requested_kernel_id = endpoint.rpartition("/")[2]
+ if requested_kernel_id not in running_kernels:
+ raise HTTPError(404, message="Kernel does not exist: %s" %
requested_kernel_id)
+
running_kernels.pop(
requested_kernel_id
) # Simulate shutdown by removing kernel from running set
@@ -158,6 +168,15 @@
mock_http_user = "alice"
+def mock_websocket_create_connection(recv_side_effect=None):
+ def helper(*args, **kwargs):
+ mock = MagicMock()
+ mock.recv = MagicMock(side_effect=recv_side_effect)
+ return mock
+
+ return helper
+
+
@pytest.fixture
def init_gateway(monkeypatch):
"""Initializes the server for use as a gateway client."""
@@ -292,6 +311,101 @@
assert await is_kernel_running(jp_fetch, kernel_id) is False
[email protected]("missing_kernel", [True, False])
+async def test_gateway_shutdown(init_gateway, jp_serverapp, jp_fetch,
missing_kernel):
+ # Validate server shutdown when multiple gateway kernels are present or
+ # we've lost track of at least one (missing) kernel
+
+ # create two kernels
+ k1 = await create_kernel(jp_fetch, "kspec_bar")
+ k2 = await create_kernel(jp_fetch, "kspec_bar")
+
+ # ensure they're considered running
+ assert await is_kernel_running(jp_fetch, k1) is True
+ assert await is_kernel_running(jp_fetch, k2) is True
+
+ if missing_kernel:
+ running_kernels.pop(k1) # "terminate" kernel w/o our knowledge
+
+ with mocked_gateway:
+ await jp_serverapp.kernel_manager.shutdown_all()
+
+ assert await is_kernel_running(jp_fetch, k1) is False
+ assert await is_kernel_running(jp_fetch, k2) is False
+
+
+@patch("websocket.create_connection",
mock_websocket_create_connection(recv_side_effect=Exception))
+async def
test_kernel_client_response_router_notifies_channel_queue_when_finished(
+ init_gateway, jp_serverapp, jp_fetch
+):
+ # create
+ kernel_id = await create_kernel(jp_fetch, "kspec_bar")
+
+ # get kernel manager
+ km: GatewayKernelManager =
jp_serverapp.kernel_manager.get_kernel(kernel_id)
+
+ # create kernel client
+ kc = km.client()
+
+ await ensure_async(kc.start_channels())
+
+ with pytest.raises(RuntimeError):
+ await kc.iopub_channel.get_msg(timeout=10)
+
+ all_channels = [
+ kc.shell_channel,
+ kc.iopub_channel,
+ kc.stdin_channel,
+ kc.hb_channel,
+ kc.control_channel,
+ ]
+ assert all(channel.response_router_finished if True else False for channel
in all_channels)
+
+ await ensure_async(kc.stop_channels())
+
+ # delete
+ await delete_kernel(jp_fetch, kernel_id)
+
+
+async def test_channel_queue_get_msg_with_invalid_timeout():
+ queue = ChannelQueue("iopub", MagicMock(), logging.getLogger())
+
+ with pytest.raises(ValueError):
+ await queue.get_msg(timeout=-1)
+
+
+async def test_channel_queue_get_msg_raises_empty_after_timeout():
+ queue = ChannelQueue("iopub", MagicMock(), logging.getLogger())
+
+ with pytest.raises(Empty):
+ await asyncio.wait_for(queue.get_msg(timeout=0.1), 2)
+
+
+async def test_channel_queue_get_msg_without_timeout():
+ queue = ChannelQueue("iopub", MagicMock(), logging.getLogger())
+
+ with pytest.raises(asyncio.TimeoutError):
+ await asyncio.wait_for(queue.get_msg(timeout=None), 1)
+
+
+async def test_channel_queue_get_msg_with_existing_item():
+ sent_message = {"msg_id": 1, "msg_type": 2}
+ queue = ChannelQueue("iopub", MagicMock(), logging.getLogger())
+ queue.put_nowait(sent_message)
+
+ received_message = await asyncio.wait_for(queue.get_msg(timeout=None), 1)
+
+ assert received_message == sent_message
+
+
+async def test_channel_queue_get_msg_when_response_router_had_finished():
+ queue = ChannelQueue("iopub", MagicMock(), logging.getLogger())
+ queue.response_router_finished = True
+
+ with pytest.raises(RuntimeError):
+ await queue.get_msg()
+
+
#
# Test methods below...
#