Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-jupyter-client for 
openSUSE:Factory checked in at 2026-03-11 20:50:25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jupyter-client (Old)
 and      /work/SRC/openSUSE:Factory/.python-jupyter-client.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-jupyter-client"

Wed Mar 11 20:50:25 2026 rev:30 rq:1338104 version:8.8.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-jupyter-client/python-jupyter-client.changes  
    2025-12-19 16:42:50.906350867 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-jupyter-client.new.8177/python-jupyter-client.changes
    2026-03-11 20:51:17.804030773 +0100
@@ -1,0 +2,17 @@
+Tue Mar 10 21:59:08 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 8.8.0:
+  * Added kernel_protocol_version to kernelspec #1097
+  * Faster message serialization
+  * Path resolution by kernel manager and providers #1005
+  * chore: update some parts of the pre-commit #1093 (@henryiii,
+  * Fix flaky client tests by waiting for first request's reply
+  * Re-enable qtconsole in downstream checks #1090 (@rgbkrk)
+  * Fix channel cleanup by closing streams before stopping ioloop
+    thread #1089 (@rgbkrk)
+  * Fix downstream workflow #1087 (@rgbkrk)
+  * Skip mypy on PyPy to avoid librt compilation issues #1086
+  * Upgrade to version 5.5 #1096
+  * Update messaging spec #1095
+
+-------------------------------------------------------------------

Old:
----
  jupyter_client-8.7.0.tar.gz

New:
----
  jupyter_client-8.8.0.tar.gz

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

Other differences:
------------------
++++++ python-jupyter-client.spec ++++++
--- /var/tmp/diff_new_pack.jmhAaP/_old  2026-03-11 20:51:18.440057100 +0100
+++ /var/tmp/diff_new_pack.jmhAaP/_new  2026-03-11 20:51:18.444057266 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-jupyter-client
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -26,7 +26,7 @@
 %endif
 %{?sle15_python_module_pythons}
 Name:           python-jupyter-client%{psuffix}
-Version:        8.7.0
+Version:        8.8.0
 Release:        0
 Summary:        Jupyter protocol implementation and client libraries
 License:        BSD-3-Clause

++++++ jupyter_client-8.7.0.tar.gz -> jupyter_client-8.8.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_client-8.7.0/.github/workflows/downstream.yml 
new/jupyter_client-8.8.0/.github/workflows/downstream.yml
--- old/jupyter_client-8.7.0/.github/workflows/downstream.yml   2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/.github/workflows/downstream.yml   2020-02-02 
01:00:00.000000000 +0100
@@ -30,6 +30,8 @@
         with:
           package_name: nbclient
           env_values: IPYKERNEL_CELL_NAME=\<IPY-INPUT\>
+          # Skip test with execution_count mismatch - nbclient test issue, not 
jupyter_client
+          test_command: "pytest -vv -raXxs -W default --durations 10 
--color=yes --deselect 
tests/test_client.py::test_run_all_notebooks[update-display-id.ipynb-opts14]"
 
   nbconvert:
     runs-on: ubuntu-latest
@@ -62,13 +64,13 @@
       - name: Base Setup
         uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
 
-      - name: Setup conda ${{ matrix.python-version }}
+      - name: Setup conda
         uses: conda-incubator/setup-miniconda@v3
         with:
           auto-update-conda: true
           activate-environment: jupyter_kernel_test
-          miniforge-variant: Mambaforge
-          python-version: ${{ matrix.python-version }}
+          miniforge-version: latest
+          python-version: "3.10"
 
       - name: Test jupyter_kernel_test
         shell: bash -l {0}
@@ -89,7 +91,7 @@
       - name: Setup Python
         uses: actions/setup-python@v5
         with:
-          python-version: "3.9"
+          python-version: "3.10"
           architecture: "x64"
 
       - name: Install System Packages
@@ -121,7 +123,6 @@
     needs:
       - ipykernel
       - nbclient
-      - papermill
       - nbconvert
       - jupyter_server
       - jupyter_kernel_test
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/.github/workflows/main.yml 
new/jupyter_client-8.8.0/.github/workflows/main.yml
--- old/jupyter_client-8.7.0/.github/workflows/main.yml 2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/.github/workflows/main.yml 2020-02-02 
01:00:00.000000000 +0100
@@ -19,6 +19,8 @@
 jobs:
   check_release:
     runs-on: ubuntu-latest
+    env:
+      HATCH_ENV: ""
     steps:
       - uses: actions/checkout@v4
       - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
@@ -124,7 +126,7 @@
   test_minimum_verisons:
     name: Test Minimum Versions
     runs-on: ubuntu-latest
-    timeout-minutes: 10
+    timeout-minutes: 20
     steps:
       - uses: actions/checkout@v4
       - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
@@ -141,6 +143,11 @@
         run: |
           hatch -vv run test:nowarn
 
+      - name: Run the unit tests with orjson installed
+        run: |
+          hatch -e test run pip install orjson
+          hatch -vv run test:nowarn
+
   test_prereleases:
     name: Test Prereleases
     timeout-minutes: 10
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/.jupyter-releaser.toml 
new/jupyter_client-8.8.0/.jupyter-releaser.toml
--- old/jupyter_client-8.7.0/.jupyter-releaser.toml     1970-01-01 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/.jupyter-releaser.toml     2020-02-02 
01:00:00.000000000 +0100
@@ -0,0 +1,5 @@
+[options]
+python-packages = ["."]
+
+[hooks]
+before-bump-version = ["pip install hatch"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/.pre-commit-config.yaml 
new/jupyter_client-8.8.0/.pre-commit-config.yaml
--- old/jupyter_client-8.7.0/.pre-commit-config.yaml    2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/.pre-commit-config.yaml    2020-02-02 
01:00:00.000000000 +0100
@@ -30,21 +30,30 @@
     hooks:
       - id: mdformat
 
-  - repo: https://github.com/pre-commit/mirrors-prettier
-    rev: "v4.0.0-alpha.8"
+  - repo: https://github.com/rbubley/mirrors-prettier
+    rev: "v3.7.3"
     hooks:
       - id: prettier
         types_or: [yaml, html, json]
 
   - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: "v1.18.2"
+    rev: "v1.19.0"
     hooks:
       - id: mypy
         files: jupyter_client
         stages: [manual]
-        args: ["--install-types", "--non-interactive"]
+        args: []
         additional_dependencies:
-          ["traitlets>=5.13", "ipykernel>=6.26", "jupyter_core>=5.3.2"]
+          - traitlets>=5.13
+          - ipykernel>=6.26
+          - jupyter_core>=5.3.2
+          - orjson>=3.11.4
+          - msgpack-types
+          - types-pexpect
+          - types-paramiko
+          - types-netifaces
+          - types-psutil
+          - types-python-dateutil
 
   - repo: https://github.com/adamchainz/blacken-docs
     rev: "1.20.0"
@@ -69,13 +78,10 @@
     rev: v0.14.0
     hooks:
       - id: ruff-check
-        types_or: [python, jupyter]
         args: ["--fix", "--show-fixes"]
       - id: ruff-format
-        types_or: [python, jupyter]
 
   - repo: https://github.com/scientific-python/cookie
     rev: "2025.10.01"
     hooks:
       - id: sp-repo-review
-        additional_dependencies: ["repo-review[cli]"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/CHANGELOG.md 
new/jupyter_client-8.8.0/CHANGELOG.md
--- old/jupyter_client-8.7.0/CHANGELOG.md       2020-02-02 01:00:00.000000000 
+0100
+++ new/jupyter_client-8.8.0/CHANGELOG.md       2020-02-02 01:00:00.000000000 
+0100
@@ -2,6 +2,41 @@
 
 <!-- <START NEW CHANGELOG ENTRY> -->
 
+## 8.8.0
+
+([Full 
Changelog](https://github.com/jupyter/jupyter_client/compare/v8.7.0...523294fc6cb9be685fa25129067a06348edaaab6))
+
+### Enhancements made
+
+- Added kernel_protocol_version to kernelspec 
[#1097](https://github.com/jupyter/jupyter_client/pull/1097) 
([@JohanMabille](https://github.com/JohanMabille), 
[@SylvainCorlay](https://github.com/SylvainCorlay))
+- Faster message serialization 
[#1064](https://github.com/jupyter/jupyter_client/pull/1064) 
([@fleming79](https://github.com/fleming79), 
[@Carreau](https://github.com/Carreau), 
[@henryiii](https://github.com/henryiii), 
[@krassowski](https://github.com/krassowski), 
[@minrk](https://github.com/minrk), [@rgbkrk](https://github.com/rgbkrk))
+- Path resolution by kernel manager and providers 
[#1005](https://github.com/jupyter/jupyter_client/pull/1005) 
([@krassowski](https://github.com/krassowski), 
[@rgbkrk](https://github.com/rgbkrk))
+
+### Maintenance and upkeep improvements
+
+- chore: update some parts of the pre-commit 
[#1093](https://github.com/jupyter/jupyter_client/pull/1093) 
([@henryiii](https://github.com/henryiii), [@minrk](https://github.com/minrk), 
[@rgbkrk](https://github.com/rgbkrk))
+- Fix flaky client tests by waiting for first request's reply 
[#1091](https://github.com/jupyter/jupyter_client/pull/1091) 
([@rgbkrk](https://github.com/rgbkrk))
+- Re-enable qtconsole in downstream checks 
[#1090](https://github.com/jupyter/jupyter_client/pull/1090) 
([@rgbkrk](https://github.com/rgbkrk))
+- Fix channel cleanup by closing streams before stopping ioloop thread 
[#1089](https://github.com/jupyter/jupyter_client/pull/1089) 
([@rgbkrk](https://github.com/rgbkrk))
+- Fix downstream workflow 
[#1087](https://github.com/jupyter/jupyter_client/pull/1087) 
([@rgbkrk](https://github.com/rgbkrk))
+- Skip mypy on PyPy to avoid librt compilation issues 
[#1086](https://github.com/jupyter/jupyter_client/pull/1086) 
([@rgbkrk](https://github.com/rgbkrk))
+
+### Documentation improvements
+
+- Upgrade to version 5.5 
[#1096](https://github.com/jupyter/jupyter_client/pull/1096) 
([@JohanMabille](https://github.com/JohanMabille), 
[@SylvainCorlay](https://github.com/SylvainCorlay))
+- Update messaging spec 
[#1095](https://github.com/jupyter/jupyter_client/pull/1095) 
([@JohanMabille](https://github.com/JohanMabille), 
[@minrk](https://github.com/minrk), [@rgbkrk](https://github.com/rgbkrk))
+
+### Contributors to this release
+
+The following people contributed discussions, new ideas, code and 
documentation contributions, and review.
+See [our definition of 
contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports).
+
+([GitHub contributors page for this 
release](https://github.com/jupyter/jupyter_client/graphs/contributors?from=2025-12-09&to=2026-01-08&type=c))
+
+@Carreau 
([activity](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3ACarreau+updated%3A2025-12-09..2026-01-08&type=Issues))
 | @ccordoba12 
([activity](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Accordoba12+updated%3A2025-12-09..2026-01-08&type=Issues))
 | @fleming79 
([activity](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Afleming79+updated%3A2025-12-09..2026-01-08&type=Issues))
 | @henryiii 
([activity](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Ahenryiii+updated%3A2025-12-09..2026-01-08&type=Issues))
 | @JohanMabille 
([activity](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3AJohanMabille+updated%3A2025-12-09..2026-01-08&type=Issues))
 | @krassowski 
([activity](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Akrassowski+updated%3A2025-12-09..2026-01-08&type=Issues))
 | @minrk ([activity](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_cli
 ent+involves%3Aminrk+updated%3A2025-12-09..2026-01-08&type=Issues)) | @rgbkrk 
([activity](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Argbkrk+updated%3A2025-12-09..2026-01-08&type=Issues))
 | @SylvainCorlay 
([activity](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3ASylvainCorlay+updated%3A2025-12-09..2026-01-08&type=Issues))
+
+<!-- <END NEW CHANGELOG ENTRY> -->
+
 ## 8.7.0
 
 ([Full 
Changelog](https://github.com/jupyter/jupyter_client/compare/v8.6.3...7b4340d29da062cc2386fa218f645c7e80eb800f))
@@ -40,8 +75,6 @@
 
 
[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Ablink1073+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@Carreau](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3ACarreau+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@cben](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Acben+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Adavidbrochart+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@ianthomas23](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Aianthomas23+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@JamesWrigley](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3AJamesWrigley+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@kevin-bates](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Akevin-bates+updated%3A2024-09-17..2025-12-09&type=Issue
 s) | 
[@krassowski](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Akrassowski+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@minrk](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Aminrk+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@rgbkrk](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Argbkrk+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@sebwills](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Asebwills+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@takluyver](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Atakluyver+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@tmaxwell-anthropic](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Atmaxwell-anthropic+updated%3A2024-09-17..2025-12-09&type=Issues)
 | 
[@wpk-nist-gov](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_client+involves%3Awpk-nist-gov+updated%3A2024-09-17..2025-12-09&typ
 e=Issues)
 
-<!-- <END NEW CHANGELOG ENTRY> -->
-
 ## 8.6.3
 
 ([Full 
Changelog](https://github.com/jupyter/jupyter_client/compare/v8.6.2...7d7251f24ed42c34b9b6ebdf09a5d89c46450131))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/PKG-INFO 
new/jupyter_client-8.8.0/PKG-INFO
--- old/jupyter_client-8.7.0/PKG-INFO   2020-02-02 01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/PKG-INFO   2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: jupyter_client
-Version: 8.7.0
+Version: 8.8.0
 Summary: Jupyter protocol implementation and client libraries
 Project-URL: Homepage, https://jupyter.org
 Project-URL: Documentation, https://jupyter-client.readthedocs.io/
@@ -62,11 +62,14 @@
 Requires-Dist: sphinx>=4; extra == 'docs'
 Requires-Dist: sphinxcontrib-github-alt; extra == 'docs'
 Requires-Dist: sphinxcontrib-spelling; extra == 'docs'
+Provides-Extra: orjson
+Requires-Dist: orjson; extra == 'orjson'
 Provides-Extra: test
 Requires-Dist: anyio; extra == 'test'
 Requires-Dist: coverage; extra == 'test'
 Requires-Dist: ipykernel>=6.14; extra == 'test'
-Requires-Dist: mypy; extra == 'test'
+Requires-Dist: msgpack; extra == 'test'
+Requires-Dist: mypy; (platform_python_implementation != 'PyPy') and extra == 
'test'
 Requires-Dist: paramiko; (sys_platform == 'win32') and extra == 'test'
 Requires-Dist: pre-commit; extra == 'test'
 Requires-Dist: pytest; extra == 'test'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/docs/messaging.rst 
new/jupyter_client-8.8.0/docs/messaging.rst
--- old/jupyter_client-8.7.0/docs/messaging.rst 2020-02-02 01:00:00.000000000 
+0100
+++ new/jupyter_client-8.8.0/docs/messaging.rst 2020-02-02 01:00:00.000000000 
+0100
@@ -21,7 +21,7 @@
 
 The Jupyter message specification is versioned independently of the packages
 that use it.
-The current version of the specification is 5.4.
+The current version of the specification is 5.5.
 
 .. note::
    *New in* and *Changed in* messages in this document refer to versions of the
@@ -84,6 +84,11 @@
 5. **Heartbeat**: This socket allows for simple bytestring messages to be sent
    between the frontend and the kernel to ensure that they are still connected.
 
+.. versionchanged:: 5.5
+   The **IOPub** PUB socket is replaced with an XPUB socket,
+   to enable the ``iopub_welcome`` message.
+   There is no other difference between kernel PUB and XPUB sockets from the 
client perspective.
+
 The actual format of the messages allowed on each of these channels is
 specified below.  Messages are dicts of dicts with string keys and values that
 are reasonably representable in JSON.
@@ -1136,12 +1141,21 @@
 
 .. versionadded:: 5.3
 
+Kernel info
+-----------
+
+This is the same :ref:`kernel info <msging_kernel_info>` message as that 
received on the Shell channel.
+
+.. versionadded:: 5.5
+
 Debug request
 -------------
 
 This message type is used with debugging kernels to request specific actions
 to be performed by the debugger such as adding a breakpoint or stepping into
 a code.
+Kernels supporting subshells must include ``'debugger'`` in 
``'supported_features'``
+in :ref:`kernel info <msging_kernel_info>` reply messages.
 
 Message type: ``debug_request``::
 
@@ -1158,6 +1172,8 @@
 Debug requests and replies are sent over the ``control`` channel to prevent
 queuing behind execution requests.
 
+.. versionadded:: 5.5
+
 Additions to the DAP
 ~~~~~~~~~~~~~~~~~~~~
 
@@ -1196,6 +1212,8 @@
           }
      }
 
+.. versionadded:: 5.5
+
 debugInfo
 #########
 
@@ -1231,11 +1249,14 @@
               'stoppedThreads' : list(int),  # threads in which the debugger 
is currently in a stopped state
               'richRendering' : bool,  # whether the debugger supports rich 
rendering of variables
               'exceptionPaths' : list(str),  # exception names used to match 
leaves or nodes in a tree of exception
+              'copyToGlobals' : bool, # whether the debugger supports supports 
the copyToGlobals request
           }
       }
 
   The ``source_breakpoint`` schema is specified by the Debug Adapter Protocol.
 
+.. versionadded:: 5.5
+
 inspectVariables
 ################
 
@@ -1267,6 +1288,8 @@
           }
       }
 
+.. versionadded:: 5.5
+
 richInspectVariables
 ####################
 
@@ -1297,11 +1320,15 @@
           }
       }
 
+.. versionadded:: 5.5
+
 copyToGlobals
 #############
 
 The ``copyToGlobals`` request allows to copy a variable from the local 
variable panel
 of the debugger to the ``global`` scope to inspect it after debug session.
+The support for this request is optional and should be indicated to the client 
via
+the ``copyToGlobals`` boolean field in the debugInfo reply.
 
   Content of the ``copyToGlobals`` request::
 
@@ -1414,8 +1441,39 @@
 
 .. versionadded:: 5.5
 
-Messages on the IOPub (PUB/SUB) channel
-=======================================
+Messages on the IOPub (XPUB/SUB) channel
+========================================
+
+Welcome message
+---------------
+
+This message is sent to a client SUB socket the first time it connects to the
+XPUB kernel socket, to notify the client that the connection is established.
+
+message type: ``iopub_welcome``::
+
+    content = {
+        # The topic the SUB has subscribed to. Can be empty string if
+        # the client has subscribed to all topics.
+        'subscription' : str,
+    }
+
+.. note::
+
+   This message has no parent header.
+
+.. note::
+
+   Welcome messages do not and cannot identify the client whose subscription 
is being received.
+   Receiving an iopub_welcome message with your subscription does not mean it 
is in response to
+   your own subscription. However, receiving a message does mean that a 
matching subscription has
+   been registered for your client, otherwise no message will be received. So 
if only one
+   subscription is registered, as is normally the case, receiving any welcome 
message is sufficient
+   to indicate that your client's subscription is fully established. The gist 
is that receiving a
+   welcome message is a sufficient condition to establish the 
subscription-propagation event, and
+   additional welcome messages should be expected and ignored.
+
+.. versionadded:: 5.5
 
 Streams (stdout,  stderr, etc)
 ------------------------------
@@ -1851,15 +1909,17 @@
 Changelog
 =========
 
-5.5 (draft)
------------
+5.5
+---
 
-- Added ``debug_request/reply`` messages
-- Added ``debug_event`` message
+- Added ``debug_request/reply`` and ``debug_event`` messages.
+- Replaced **IOPUB** PUB socket with an XPUB socket.
+- Added support for :ref:`kernel info <msging_kernel_info>` request on the 
Control channel.
 - Added ``supported_features`` in :ref:`kernel info <msging_kernel_info>` 
reply messages.
 - Deprecated ``debugger`` in :ref:`kernel info <msging_kernel_info>` reply 
messages as
   replaced with ``supported_features``.
 - Added ``create_subshell``, ``delete_subshell`` and ``list_subshell`` 
messages.
+- Added ``copyToGlobals`` debug request
 
 5.4
 ---
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/jupyter_client/_version.py 
new/jupyter_client-8.8.0/jupyter_client/_version.py
--- old/jupyter_client-8.7.0/jupyter_client/_version.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/jupyter_client/_version.py 2020-02-02 
01:00:00.000000000 +0100
@@ -3,7 +3,7 @@
 import re
 from typing import Union
 
-__version__ = "8.7.0"
+__version__ = "8.8.0"
 
 # Build up version_info tuple for backwards compatibility
 pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/jupyter_client/kernelspec.py 
new/jupyter_client-8.8.0/jupyter_client/kernelspec.py
--- old/jupyter_client-8.7.0/jupyter_client/kernelspec.py       2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/jupyter_client/kernelspec.py       2020-02-02 
01:00:00.000000000 +0100
@@ -30,6 +30,7 @@
     mimetype = Unicode()
     display_name = Unicode()
     language = Unicode()
+    kernel_protocol_version = Unicode()
     env = Dict()
     resource_dir = Unicode()
     interrupt_mode = CaselessStrEnum(["message", "signal"], 
default_value="signal")
@@ -55,6 +56,7 @@
             "language": self.language,
             "interrupt_mode": self.interrupt_mode,
             "metadata": self.metadata,
+            "kernel_protocol_version": self.kernel_protocol_version,
         }
 
         return d
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/jupyter_client/manager.py 
new/jupyter_client-8.8.0/jupyter_client/manager.py
--- old/jupyter_client-8.7.0/jupyter_client/manager.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/jupyter_client/manager.py  2020-02-02 
01:00:00.000000000 +0100
@@ -280,6 +280,11 @@
     # Kernel management
     # 
--------------------------------------------------------------------------
 
+    def resolve_path(self, path: str) -> str | None:
+        """Resolve path to given file."""
+        assert self.provisioner is not None
+        return self.provisioner.resolve_path(path)
+
     def update_env(self, *, env: t.Dict[str, str]) -> None:
         """
         Allow to update the environment of a kernel manager.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_client-8.7.0/jupyter_client/provisioning/local_provisioner.py 
new/jupyter_client-8.8.0/jupyter_client/provisioning/local_provisioner.py
--- old/jupyter_client-8.7.0/jupyter_client/provisioning/local_provisioner.py   
2020-02-02 01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/jupyter_client/provisioning/local_provisioner.py   
2020-02-02 01:00:00.000000000 +0100
@@ -4,6 +4,7 @@
 # Distributed under the terms of the Modified BSD License.
 import asyncio
 import os
+import pathlib
 import signal
 import sys
 from typing import TYPE_CHECKING, Any
@@ -32,6 +33,7 @@
     pgid = None
     ip = None
     ports_cached = False
+    cwd = None
 
     @property
     def has_process(self) -> bool:
@@ -212,6 +214,7 @@
 
     async def launch_kernel(self, cmd: list[str], **kwargs: Any) -> 
KernelConnectionInfo:
         """Launch a kernel with a command."""
+
         scrubbed_kwargs = LocalProvisioner._scrub_kwargs(kwargs)
         self.process = launch_kernel(cmd, **scrubbed_kwargs)
         pgid = None
@@ -223,8 +226,18 @@
 
         self.pid = self.process.pid
         self.pgid = pgid
+        self.cwd = kwargs.get("cwd", pathlib.Path.cwd())
         return self.connection_info
 
+    def resolve_path(self, path_str: str) -> str | None:
+        """Resolve path to given file."""
+        path = pathlib.Path(path_str).expanduser()
+        if not path.is_absolute() and self.cwd:
+            path = (pathlib.Path(self.cwd) / path).resolve()
+        if path.exists():
+            return path.as_posix()
+        return None
+
     @staticmethod
     def _scrub_kwargs(kwargs: dict[str, Any]) -> dict[str, Any]:
         """Remove any keyword arguments that Popen does not tolerate."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/jupyter_client-8.7.0/jupyter_client/provisioning/provisioner_base.py 
new/jupyter_client-8.8.0/jupyter_client/provisioning/provisioner_base.py
--- old/jupyter_client-8.7.0/jupyter_client/provisioning/provisioner_base.py    
2020-02-02 01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/jupyter_client/provisioning/provisioner_base.py    
2020-02-02 01:00:00.000000000 +0100
@@ -218,6 +218,21 @@
         """
         return recommended
 
+    def resolve_path(self, path: str) -> str | None:
+        """
+        Returns the path resolved relative to kernel working directory.
+
+        For example, path `my_code.py` for a kernel started in `/tmp/`
+        should result in `/tmp/my_code.py`, while path `~/test.py` for
+        a kernel started in `/home/my_user/` should resolve to the
+        (fully specified) `/home/my_user/test.py` path.
+
+        The provisioner may choose not to resolve any paths, or restrict
+        the resolution to paths local to the kernel working directory
+        to prevent path traversal and exposure of file system layout.
+        """
+        return None
+
     def _finalize_env(self, env: dict[str, str]) -> None:
         """
         Ensures env is appropriate prior to launch.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/jupyter_client/session.py 
new/jupyter_client-8.8.0/jupyter_client/session.py
--- old/jupyter_client-8.7.0/jupyter_client/session.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/jupyter_client/session.py  2020-02-02 
01:00:00.000000000 +0100
@@ -13,6 +13,7 @@
 # Distributed under the terms of the Modified BSD License.
 from __future__ import annotations
 
+import functools
 import hashlib
 import hmac
 import json
@@ -33,6 +34,7 @@
 from traitlets import (
     Any,
     Bool,
+    Callable,
     CBytes,
     CUnicode,
     Dict,
@@ -125,6 +127,41 @@
     return json.loads(s)
 
 
+try:
+    import orjson
+except ModuleNotFoundError:
+    has_orjson = False
+    orjson_packer, orjson_unpacker = json_packer, json_unpacker
+else:
+    has_orjson = True
+
+    def orjson_packer(
+        obj: t.Any, *, option: int | None = orjson.OPT_NAIVE_UTC | 
orjson.OPT_UTC_Z
+    ) -> bytes:
+        """Convert a json object to a bytes using orjson with fallback to 
json_packer."""
+        try:
+            return orjson.dumps(obj, default=json_default, option=option)
+        except Exception:
+            return json_packer(obj)
+
+    def orjson_unpacker(s: str | bytes) -> t.Any:
+        """Convert a json bytes or string to an object using orjson with 
fallback to json_unpacker."""
+        try:
+            return orjson.loads(s)
+        except Exception:
+            return json_unpacker(s)
+
+
+try:
+    import msgpack
+except ModuleNotFoundError:
+    has_msgpack = False
+else:
+    has_msgpack = True
+    msgpack_packer = functools.partial(msgpack.packb, default=json_default)
+    msgpack_unpacker = msgpack.unpackb
+
+
 def pickle_packer(o: t.Any) -> bytes:
     """Pack an object using the pickle module."""
     return pickle.dumps(squash_dates(o), PICKLE_PROTOCOL)
@@ -132,8 +169,6 @@
 
 pickle_unpacker = pickle.loads
 
-default_packer = json_packer
-default_unpacker = json_unpacker
 
 DELIM = b"<IDS|MSG>"
 # singleton dummy tracker, which will always report as done
@@ -316,7 +351,7 @@
 
     debug : bool
         whether to trigger extra debugging statements
-    packer/unpacker : str : 'json', 'pickle' or import_string
+    packer/unpacker : str : 'orjson', 'json', 'pickle', 'msgpack' or 
import_string
         importstrings for methods to serialize message parts.  If just
         'json' or 'pickle', predefined JSON and pickle packers will be used.
         Otherwise, the entire importstring must be used.
@@ -351,48 +386,42 @@
         """,
     )
 
+    # serialization traits:
     packer = DottedObjectName(
-        "json",
+        "orjson" if has_orjson else "json",
         config=True,
         help="""The name of the packer for serializing messages.
             Should be one of 'json', 'pickle', or an import name
             for a custom callable serializer.""",
     )
-
-    @observe("packer")
-    def _packer_changed(self, change: t.Any) -> None:
-        new = change["new"]
-        if new.lower() == "json":
-            self.pack = json_packer
-            self.unpack = json_unpacker
-            self.unpacker = new
-        elif new.lower() == "pickle":
-            self.pack = pickle_packer
-            self.unpack = pickle_unpacker
-            self.unpacker = new
-        else:
-            self.pack = import_item(str(new))
-
     unpacker = DottedObjectName(
-        "json",
+        "orjson" if has_orjson else "json",
         config=True,
         help="""The name of the unpacker for unserializing messages.
         Only used with custom functions for `packer`.""",
     )
-
-    @observe("unpacker")
-    def _unpacker_changed(self, change: t.Any) -> None:
-        new = change["new"]
-        if new.lower() == "json":
-            self.pack = json_packer
-            self.unpack = json_unpacker
-            self.packer = new
-        elif new.lower() == "pickle":
-            self.pack = pickle_packer
-            self.unpack = pickle_unpacker
-            self.packer = new
+    pack = Callable(orjson_packer if has_orjson else json_packer)  # the 
actual packer function
+    unpack = Callable(
+        orjson_unpacker if has_orjson else json_unpacker
+    )  # the actual unpacker function
+
+    @observe("packer", "unpacker")
+    def _packer_unpacker_changed(self, change: t.Any) -> None:
+        new = change["new"].lower()
+        if new == "orjson" and has_orjson:
+            self.pack, self.unpack = orjson_packer, orjson_unpacker
+        elif new == "json" or new == "orjson":
+            self.pack, self.unpack = json_packer, json_unpacker
+        elif new == "pickle":
+            self.pack, self.unpack = pickle_packer, pickle_unpacker
+        elif new == "msgpack" and has_msgpack:
+            self.pack, self.unpack = msgpack_packer, msgpack_unpacker
         else:
-            self.unpack = import_item(str(new))
+            obj = import_item(str(change["new"]))
+            name = "pack" if change["name"] == "packer" else "unpack"
+            self.set_trait(name, obj)
+            return
+        self.packer = self.unpacker = change["new"]
 
     session = CUnicode("", config=True, help="""The UUID identifying this 
session.""")
 
@@ -417,8 +446,7 @@
     metadata = Dict(
         {},
         config=True,
-        help="Metadata dictionary, which serves as the default top-level 
metadata dict for each "
-        "message.",
+        help="Metadata dictionary, which serves as the default top-level 
metadata dict for each message.",
     )
 
     # if 0, no adapting to do.
@@ -487,25 +515,6 @@
     # for protecting against sends from forks
     pid = Integer()
 
-    # serialization traits:
-
-    pack = Any(default_packer)  # the actual packer function
-
-    @observe("pack")
-    def _pack_changed(self, change: t.Any) -> None:
-        new = change["new"]
-        if not callable(new):
-            raise TypeError("packer must be callable, not %s" % type(new))
-
-    unpack = Any(default_unpacker)  # the actual packer function
-
-    @observe("unpack")
-    def _unpack_changed(self, change: t.Any) -> None:
-        # unpacker is not checked - it is assumed to be
-        new = change["new"]
-        if not callable(new):
-            raise TypeError("unpacker must be callable, not %s" % type(new))
-
     # thresholds:
     copy_threshold = Integer(
         2**16,
@@ -515,8 +524,7 @@
     buffer_threshold = Integer(
         MAX_BYTES,
         config=True,
-        help="Threshold (in bytes) beyond which an object's buffer should be 
extracted to avoid "
-        "pickling.",
+        help="Threshold (in bytes) beyond which an object's buffer should be 
extracted to avoid pickling.",
     )
     item_threshold = Integer(
         MAX_ITEMS,
@@ -534,7 +542,7 @@
 
         debug : bool
             whether to trigger extra debugging statements
-        packer/unpacker : str : 'json', 'pickle' or import_string
+        packer/unpacker : str : 'orjson', 'json', 'pickle', 'msgpack' or 
import_string
             importstrings for methods to serialize message parts.  If just
             'json' or 'pickle', predefined JSON and pickle packers will be 
used.
             Otherwise, the entire importstring must be used.
@@ -626,10 +634,7 @@
             unpacked = unpack(packed)
             assert unpacked == msg_list
         except Exception as e:
-            msg = (
-                f"unpacker '{self.unpacker}' could not handle output from 
packer"
-                f" '{self.packer}': {e}"
-            )
+            msg = f"unpacker {self.unpacker!r} could not handle output from 
packer {self.packer!r}: {e}"
             raise ValueError(msg) from e
 
         # check datetime support
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/jupyter_client/threaded.py 
new/jupyter_client-8.8.0/jupyter_client/threaded.py
--- old/jupyter_client-8.7.0/jupyter_client/threaded.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/jupyter_client/threaded.py 2020-02-02 
01:00:00.000000000 +0100
@@ -214,7 +214,13 @@
 
     def _flush(self) -> None:
         """Callback for :method:`self.flush`."""
-        assert self.stream is not None
+        # Race condition: flush() checks stream validity then schedules this
+        # callback on the ioloop thread. Between scheduling and execution,
+        # stop_channels() may close the stream (e.g., during teardown).
+        # Handle gracefully rather than asserting, since this is an expected
+        # edge case during shutdown, not a programming error.
+        if self.stream is None or self.stream.closed():
+            return
         self.stream.flush()
         self._flushed = True
 
@@ -230,6 +236,13 @@
         super().__init__()
         self.daemon = True
 
+        # Instance variable to track exit state for this specific thread.
+        # The class variable _exiting is used by _notice_exit for interpreter 
shutdown.
+        # Without this instance variable, stopping one IOLoopThread sets the 
class-level
+        # _exiting = True, causing all subsequent IOLoopThread instances to 
exit immediately
+        # in _async_run(). This breaks sequential kernel usage (e.g., 
qtconsole tests).
+        self._exiting = False
+
     @staticmethod
     @atexit.register
     def _notice_exit() -> None:
@@ -333,6 +346,19 @@
 
     def stop_channels(self) -> None:
         """Stop the channels on the client."""
+        # Close channel streams while ioloop is still running
+        # This must happen before stopping the ioloop thread, otherwise
+        # the ZMQ streams can't be properly unregistered from the event loop
+        if self.ioloop_thread and self.ioloop_thread.is_alive():
+            if self._shell_channel is not None:
+                self._shell_channel.close()
+            if self._iopub_channel is not None:
+                self._iopub_channel.close()
+            if self._stdin_channel is not None:
+                self._stdin_channel.close()
+            if self._control_channel is not None:
+                self._control_channel.close()
+
         super().stop_channels()
         if self.ioloop_thread and self.ioloop_thread.is_alive():
             self.ioloop_thread.stop()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/pyproject.toml 
new/jupyter_client-8.8.0/pyproject.toml
--- old/jupyter_client-8.7.0/pyproject.toml     2020-02-02 01:00:00.000000000 
+0100
+++ new/jupyter_client-8.8.0/pyproject.toml     2020-02-02 01:00:00.000000000 
+0100
@@ -48,13 +48,14 @@
     "anyio",
     "coverage",
     "ipykernel>=6.14",
-    "mypy",
+    "mypy; platform_python_implementation != 'PyPy'",
     "paramiko; sys_platform == 'win32'",
     "pre-commit",
     "pytest",
     "pytest-jupyter[client]>=0.6.2",
     "pytest-cov",
     "pytest-timeout",
+    "msgpack"
 ]
 docs = [
     "ipykernel",
@@ -65,6 +66,7 @@
     "sphinxcontrib-spelling",
     "sphinx-autodoc-typehints",
 ]
+orjson = ["orjson"] # When orjson is installed it will be used for faster pack 
and unpack
 
 [project.scripts]
 jupyter-kernelspec = 
"jupyter_client.kernelspecapp:KernelSpecApp.launch_instance"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/tests/test_client.py 
new/jupyter_client-8.8.0/tests/test_client.py
--- old/jupyter_client-8.7.0/tests/test_client.py       2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/tests/test_client.py       2020-02-02 
01:00:00.000000000 +0100
@@ -50,6 +50,9 @@
         kc = self.kc
         msg_id = kc.history(session=0)
         self.assertIsInstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        kc._recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = kc.history(session=0, reply=True, timeout=TIMEOUT)
         self._check_reply("history", reply)
 
@@ -57,6 +60,9 @@
         kc = self.kc
         msg_id = kc.inspect("who cares")
         self.assertIsInstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        kc._recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = kc.inspect("code", reply=True, timeout=TIMEOUT)
         self._check_reply("inspect", reply)
 
@@ -64,6 +70,9 @@
         kc = self.kc
         msg_id = kc.complete("who cares")
         self.assertIsInstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        kc._recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = kc.complete("code", reply=True, timeout=TIMEOUT)
         self._check_reply("complete", reply)
 
@@ -71,6 +80,9 @@
         kc = self.kc
         msg_id = kc.kernel_info()
         self.assertIsInstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        kc._recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = kc.kernel_info(reply=True, timeout=TIMEOUT)
         self._check_reply("kernel_info", reply)
 
@@ -78,6 +90,9 @@
         kc = self.kc
         msg_id = kc.comm_info()
         self.assertIsInstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        kc._recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = kc.comm_info(reply=True, timeout=TIMEOUT)
         self._check_reply("comm_info", reply)
 
@@ -150,36 +165,54 @@
     async def test_history(self, kc):
         msg_id = kc.history(session=0)
         assert isinstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        await kc._async_recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = await kc.history(session=0, reply=True, timeout=TIMEOUT)
         self._check_reply("history", reply)
 
     async def test_inspect(self, kc):
         msg_id = kc.inspect("who cares")
         assert isinstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        await kc._async_recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = await kc.inspect("code", reply=True, timeout=TIMEOUT)
         self._check_reply("inspect", reply)
 
     async def test_complete(self, kc):
         msg_id = kc.complete("who cares")
         assert isinstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        await kc._async_recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = await kc.complete("code", reply=True, timeout=TIMEOUT)
         self._check_reply("complete", reply)
 
     async def test_is_complete(self, kc):
         msg_id = kc.is_complete("who cares")
         assert isinstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        await kc._async_recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = await kc.is_complete("code", reply=True, timeout=TIMEOUT)
         self._check_reply("is_complete", reply)
 
     async def test_kernel_info(self, kc):
         msg_id = kc.kernel_info()
         assert isinstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        await kc._async_recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = await kc.kernel_info(reply=True, timeout=TIMEOUT)
         self._check_reply("kernel_info", reply)
 
     async def test_comm_info(self, kc):
         msg_id = kc.comm_info()
         assert isinstance(msg_id, str)
+        # Drain the first reply to avoid race condition
+        await kc._async_recv_reply(msg_id, timeout=TIMEOUT)
+        # Now test the reply=True convenience path
         reply = await kc.comm_info(reply=True, timeout=TIMEOUT)
         self._check_reply("comm_info", reply)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jupyter_client-8.7.0/tests/test_session.py 
new/jupyter_client-8.8.0/tests/test_session.py
--- old/jupyter_client-8.7.0/tests/test_session.py      2020-02-02 
01:00:00.000000000 +0100
+++ new/jupyter_client-8.8.0/tests/test_session.py      2020-02-02 
01:00:00.000000000 +0100
@@ -9,12 +9,14 @@
 import uuid
 import warnings
 from datetime import datetime
+from pickle import PicklingError
 from unittest import mock
 
 import pytest
 import zmq
 from dateutil.tz import tzlocal
 from tornado import ioloop
+from traitlets import TraitError
 from zmq.eventloop.zmqstream import ZMQStream
 
 from jupyter_client import jsonutil
@@ -41,6 +43,16 @@
     return ss.Session()
 
 
+serializers = [
+    ("json", ss.json_packer, ss.json_unpacker),
+    ("pickle", ss.pickle_packer, ss.pickle_unpacker),
+]
+if ss.has_orjson:
+    serializers.append(("orjson", ss.orjson_packer, ss.orjson_unpacker))
+if ss.has_msgpack:
+    serializers.append(("msgpack", ss.msgpack_packer, ss.msgpack_unpacker))
+
+
 @pytest.mark.usefixtures("no_copy_threshold")
 class TestSession:
     def assertEqual(self, a, b):
@@ -64,7 +76,11 @@
         self.assertEqual(msg["header"]["msg_type"], "execute")
         self.assertEqual(msg["msg_type"], "execute")
 
-    def test_serialize(self, session):
+    @pytest.mark.parametrize(["packer", "pack", "unpack"], serializers)
+    def test_serialize(self, session, packer, pack, unpack):
+        session.packer = packer
+        assert session.pack is pack
+        assert session.unpack is unpack
         msg = session.msg("execute", content=dict(a=10, b=1.1))
         msg_list = session.serialize(msg, ident=b"foo")
         ident, msg_list = session.feed_identities(msg_list)
@@ -234,16 +250,14 @@
     def test_args(self, session):
         """initialization arguments for Session"""
         s = session
-        self.assertTrue(s.pack is ss.default_packer)
-        self.assertTrue(s.unpack is ss.default_unpacker)
         self.assertEqual(s.username, os.environ.get("USER", "username"))
 
         s = ss.Session()
         self.assertEqual(s.username, os.environ.get("USER", "username"))
 
-        with pytest.raises(TypeError):
+        with pytest.raises(TraitError):
             ss.Session(pack="hi")
-        with pytest.raises(TypeError):
+        with pytest.raises(TraitError):
             ss.Session(unpack="hi")
         u = str(uuid.uuid4())
         s = ss.Session(username="carrot", session=u)
@@ -491,11 +505,6 @@
         B.close()
         ctx.term()
 
-    def test_set_packer(self, session):
-        s = session
-        s.packer = "json"
-        s.unpacker = "json"
-
     def test_clone(self, session):
         s = session
         s._add_digest("initial")
@@ -515,14 +524,45 @@
     assert ss.squash_unicode("hi") == b"hi"
 
 
-def test_json_packer():
-    ss.json_packer(dict(a=1))
-    with pytest.raises(ValueError):
-        ss.json_packer(dict(a=ss.Session()))
-    ss.json_packer(dict(a=datetime(2021, 4, 1, 12, tzinfo=tzlocal())))
[email protected](
+    ["description", "data"],
+    [
+        ("dict", [{"a": 1}, [{"a": 1}]]),
+        ("infinite", [math.inf, ["inf", None]]),
+        ("datetime", [datetime(2021, 4, 1, 12, tzinfo=tzlocal()), []]),
+    ],
+)
[email protected](["packer", "pack", "unpack"], serializers)
+def test_serialize_objects(packer, pack, unpack, description, data):
+    data_in, data_out_options = data
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
-        ss.json_packer(dict(a=math.inf))
+        value = pack(data_in)
+    unpacked = unpack(value)
+    if (description == "infinite") and (packer in ["pickle", "msgpack"]):
+        assert math.isinf(unpacked)
+    elif description == "datetime":
+        assert data_in == jsonutil.parse_date(unpacked)
+    else:
+        assert unpacked in data_out_options
+
+
[email protected](["packer", "pack", "unpack"], serializers)
+def test_cannot_serialize(session, packer, pack, unpack):
+    data = {"a": session}
+    with pytest.raises((TypeError, ValueError, PicklingError)):
+        pack(data)
+
+
[email protected]("mode", ["packer", "unpacker"])
[email protected](["packer", "pack", "unpack"], serializers)
+def test_pack_unpack(session, packer, pack, unpack, mode):
+    s: ss.Session = session
+    s.set_trait(mode, packer)
+    assert s.pack is pack
+    assert s.unpack is unpack
+    mode_reverse = "unpacker" if mode == "packer" else "packer"
+    assert getattr(s, mode_reverse) == packer
 
 
 def test_message_cls():

Reply via email to