New Upstream Release - jupyter-console

Ready changes

Summary

Merged new upstream version: 6.6.3 (was: 6.4.4).

Diff

diff --git a/.github/workflows/check-release.yml b/.github/workflows/check-release.yml
deleted file mode 100644
index 643ffda..0000000
--- a/.github/workflows/check-release.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-name: Check Release
-on:
-  push:
-    branches: ["main"]
-  pull_request:
-    branches: ["*"]
-
-permissions:
-  contents: write
-
-jobs:
-  check_release:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        group: [check_release, link_check]
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v2
-      - name: Install Python
-        uses: actions/setup-python@v2
-        with:
-          python-version: 3.9
-          architecture: "x64"
-      - name: Install node
-        uses: actions/setup-node@v2
-        with:
-          node-version: "14.x"
-      - name: Get pip cache dir
-        id: pip-cache
-        run: |
-          echo "::set-output name=dir::$(pip cache dir)"
-      - name: Cache pip
-        uses: actions/cache@v1
-        with:
-          path: ${{ steps.pip-cache.outputs.dir }}
-          key: ${{ runner.os }}-pip-${{ hashFiles('setup.cfg') }}
-          restore-keys: |
-            ${{ runner.os }}-pip-
-            ${{ runner.os }}-pip-
-      - name: Cache checked links
-        if: ${{ matrix.group == 'link_check' }}
-        uses: actions/cache@v2
-        with:
-          path: ~/.cache/pytest-link-check
-          key: ${{ runner.os }}-linkcheck-${{ hashFiles('**/*.md', '**/*.rst') }}-md-links
-          restore-keys: |
-            ${{ runner.os }}-linkcheck-
-      - name: Upgrade packaging dependencies
-        run: |
-          pip install --upgrade pip setuptools wheel --user
-      - name: Install Dependencies
-        run: |
-          pip install -e .
-      - name: Check Release
-        if: ${{ matrix.group == 'check_release' }}
-        uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v1
-        with:
-          token: ${{ secrets.GITHUB_TOKEN }}
-      - name: Check Links
-        if: ${{ matrix.group == 'link_check' }}
-        uses: jupyter-server/jupyter_releaser/.github/actions/check-links@v1
diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index 73af585..f6151fc 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -9,13 +9,18 @@ on:
   pull_request:
     branches: [ main ]
 
+defaults:
+  run:
+    shell: bash -eux {0}
+
 jobs:
   build:
 
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: ['3.7', '3.8', '3.9', '3.10']
+        python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
+      fail-fast: false
 
     steps:
     - uses: actions/checkout@v2
@@ -26,12 +31,65 @@ jobs:
     - name: Install dependencies
       run: |
         python -m pip install --upgrade pip check-manifest
-        pip install pytest pytest-cov
-        pip install .
+        pip install pytest-cov
+        pip install ".[test]"
         python -m ipykernel.kernelspec --user
     - name: Test with pytest
       run: |
-        pytest --cov jupyter_console
-    - name: Check Manifest
-      run: |
-        check-manifest
+        pytest --cov jupyter_console || pytest jupyter_console --lf
+
+  lint:
+    runs-on: ubuntu-latest
+    steps:
+     - uses: actions/checkout@v2
+     - uses: actions/setup-python@v2
+     - name: Linting
+       run: |
+         pip install Flake8-pyproject mypy
+         mypy jupyter_console
+         flake8 jupyter_console
+
+  check_release:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
+      - uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v2
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+  check_links:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
+      - uses: jupyterlab/maintainer-tools/.github/actions/check-links@v1
+
+  test_minimum_versions:
+    name: Test Minimum Versions
+    runs-on: ubuntu-latest
+    timeout-minutes: 10
+    steps:
+      - uses: actions/checkout@v3
+      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
+        with:
+          dependency_type: minimum
+      - name: Run the unit tests
+        run: |
+          pip install ".[test]"
+          pytest || pytest --lf
+
+  tests_check: # This job does nothing and is only used for the branch protection
+    if: always()
+    needs:
+      - build
+      - lint
+      - check_release
+      - check_links
+      - test_minimum_versions
+    runs-on: ubuntu-latest
+    steps:
+      - name: Decide whether the needed jobs succeeded or failed
+        uses: re-actors/alls-green@release/v1
+        with:
+          jobs: ${{ toJSON(needs) }}
diff --git a/readthedocs.yml b/.readthedocs.yaml
similarity index 100%
rename from readthedocs.yml
rename to .readthedocs.yaml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ea433a4..49614ca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,94 @@
 
 <!-- <START NEW CHANGELOG ENTRY> -->
 
+## 6.6.3
+
+([Full Changelog](https://github.com/jupyter/jupyter_console/compare/v6.6.2...2c444cbd51c4a4ae8b2bd81b654687bb1fefa802))
+
+### Bugs fixed
+
+- Fix handle_external_iopub again [#286](https://github.com/jupyter/jupyter_console/pull/286) ([@blink1073](https://github.com/blink1073))
+
+### Contributors to this release
+
+([GitHub contributors page for this release](https://github.com/jupyter/jupyter_console/graphs/contributors?from=2023-02-27&to=2023-03-06&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_console+involves%3Ablink1073+updated%3A2023-02-27..2023-03-06&type=Issues)
+
+<!-- <END NEW CHANGELOG ENTRY> -->
+
+## 6.6.2
+
+([Full Changelog](https://github.com/jupyter/jupyter_console/compare/v6.6.1...27b0ffeb4e71f317bcd1ad2d3af41e0458bd05d4))
+
+### Bugs fixed
+
+- Fix handle_external_iopub [#285](https://github.com/jupyter/jupyter_console/pull/285) ([@blink1073](https://github.com/blink1073))
+
+### Contributors to this release
+
+([GitHub contributors page for this release](https://github.com/jupyter/jupyter_console/graphs/contributors?from=2023-02-21&to=2023-02-27&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_console+involves%3Ablink1073+updated%3A2023-02-21..2023-02-27&type=Issues)
+
+## 6.6.1
+
+([Full Changelog](https://github.com/jupyter/jupyter_console/compare/v6.6.0...0108eacfd6b7ff8bf6c6ef4d8d95a53df86d430b))
+
+### Maintenance and upkeep improvements
+
+- More build system cleanup [#282](https://github.com/jupyter/jupyter_console/pull/282) ([@blink1073](https://github.com/blink1073))
+
+### Contributors to this release
+
+([GitHub contributors page for this release](https://github.com/jupyter/jupyter_console/graphs/contributors?from=2023-02-20&to=2023-02-21&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_console+involves%3Ablink1073+updated%3A2023-02-20..2023-02-21&type=Issues)
+
+## 6.6.0
+
+([Full Changelog](https://github.com/jupyter/jupyter_console/compare/v6.5.1...7a6bbfecac9c34e9a6b26eae6c018cee7622b403))
+
+### Maintenance and upkeep improvements
+
+- Switch to hatch backend [#281](https://github.com/jupyter/jupyter_console/pull/281) ([@blink1073](https://github.com/blink1073))
+- Add flaky [#280](https://github.com/jupyter/jupyter_console/pull/280) ([@blink1073](https://github.com/blink1073))
+- Clean up license [#279](https://github.com/jupyter/jupyter_console/pull/279) ([@dcsaba89](https://github.com/dcsaba89))
+
+### Contributors to this release
+
+([GitHub contributors page for this release](https://github.com/jupyter/jupyter_console/graphs/contributors?from=2023-02-13&to=2023-02-20&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_console+involves%3Ablink1073+updated%3A2023-02-13..2023-02-20&type=Issues) | [@dcsaba89](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_console+involves%3Adcsaba89+updated%3A2023-02-13..2023-02-20&type=Issues)
+
+## 6.5.1
+
+([Full Changelog](https://github.com/jupyter/jupyter_console/compare/v6.5.0...25fe1d530cefe22596fc2aa9694cdcded14c0af3))
+
+### Bugs fixed
+
+- Fix completion handling [#278](https://github.com/jupyter/jupyter_console/pull/278) ([@blink1073](https://github.com/blink1073))
+
+### Contributors to this release
+
+([GitHub contributors page for this release](https://github.com/jupyter/jupyter_console/graphs/contributors?from=2023-02-09&to=2023-02-13&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_console+involves%3Ablink1073+updated%3A2023-02-09..2023-02-13&type=Issues)
+
+## 6.5.0
+
+([Full Changelog](https://github.com/jupyter/jupyter_console/compare/v6.4.4...7bcb1c61c709d033d5b24ecaea3cc6161ff69f5a))
+
+### Bugs fixed
+
+- Fix client 7 and 8 compat [#276](https://github.com/jupyter/jupyter_console/pull/276) ([@blink1073](https://github.com/blink1073))
+
+### Contributors to this release
+
+([GitHub contributors page for this release](https://github.com/jupyter/jupyter_console/graphs/contributors?from=2022-06-22&to=2023-02-09&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_console+involves%3Ablink1073+updated%3A2022-06-22..2023-02-09&type=Issues)
+
 ## 6.4.4
 
 ([Full Changelog](https://github.com/jupyter/jupyter_console/compare/v6.4.3...18cb350dc05c903d541f30de18fcf53943ec0e3f))
@@ -16,8 +104,6 @@
 
 [@davidbrochart](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_console+involves%3Adavidbrochart+updated%3A2022-03-07..2022-06-22&type=Issues) | [@encukou](https://github.com/search?q=repo%3Ajupyter%2Fjupyter_console+involves%3Aencukou+updated%3A2022-03-07..2022-06-22&type=Issues)
 
-<!-- <END NEW CHANGELOG ENTRY> -->
-
 ## 6.4.3
 
 ([Full Changelog](https://github.com/jupyter/jupyter_console/compare/v6.4.2...6e8f29e0a90804badda75c60c5eb50046544eb49))
diff --git a/LICENSE b/LICENSE
index bd6397d..076177a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,4 @@
-# Licensing terms
-
-This project is licensed under the terms of the Modified BSD License
-(also known as New or Revised or 3-Clause BSD), as follows:
+BSD 3-Clause License
 
 - Copyright (c) 2001-2015, IPython Development Team
 - Copyright (c) 2015-, Jupyter Development Team
@@ -11,50 +8,24 @@ All rights reserved.
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
 
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
 
-Neither the name of the Jupyter Development Team nor the names of its
-contributors may be used to endorse or promote products derived from this
-software without specific prior written permission.
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
 
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-## About the Jupyter Development Team
-
-The Jupyter Development Team is the set of all contributors to the Jupyter project.
-This includes all of the Jupyter subprojects.
-
-The core team that coordinates development on GitHub can be found here:
-https://github.com/jupyter/.
-
-## Our Copyright Policy
-
-Jupyter uses a shared copyright model. Each contributor maintains copyright
-over their contributions to Jupyter. But, it is important to note that these
-contributions are typically only changes to the repositories. Thus, the Jupyter
-source code, in its entirety is not the copyright of any single person or
-institution.  Instead, it is the collective copyright of the entire Jupyter
-Development Team.  If individual contributors want to maintain a record of what
-changes/contributions they have specific copyright on, they should indicate
-their copyright in the commit message of the change, when they commit the
-change to one of the Jupyter repositories.
-
-With this in mind, the following banner should be used in any source code file
-to indicate the copyright and license terms:
-
-    # Copyright (c) Jupyter Development Team.
-    # Distributed under the terms of the Modified BSD License.
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 835d5fc..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,29 +0,0 @@
-include COPYING.md
-include CONTRIBUTING.md
-include README.md
-
-# Documentation
-graft docs
-graft scripts
-exclude docs/\#*
-
-# Examples
-graft examples
-
-# docs subdirs we want to skip
-prune docs/build
-prune docs/gh-pages
-prune docs/dist
-
-# Patterns to exclude from any directory
-global-exclude *~
-global-exclude *.pyc
-global-exclude *.pyo
-global-exclude .git
-global-exclude .ipynb_checkpoints
-
-include *.md
-include *.yml
-include mypy.ini
-include pytest.ini
-include .mailmap
diff --git a/README.md b/README.md
index 60f06d9..aa7880b 100644
--- a/README.md
+++ b/README.md
@@ -47,3 +47,31 @@ $ python -m pep517.build .
 - [Documentation for Project Jupyter](https://jupyter.readthedocs.io/en/latest/index.html) [[PDF](https://media.readthedocs.org/pdf/jupyter/latest/jupyter.pdf)]
 - [Issues](https://github.com/jupyter/jupyter_console/issues)
 - [Technical support - Jupyter Google Group](https://groups.google.com/forum/#!forum/jupyter)
+
+## About the Jupyter Development Team
+
+The Jupyter Development Team is the set of all contributors to the Jupyter project.
+This includes all of the Jupyter subprojects.
+
+The core team that coordinates development on GitHub can be found here:
+https://github.com/jupyter/.
+
+## Our Copyright Policy
+
+Jupyter uses a shared copyright model. Each contributor maintains copyright
+over their contributions to Jupyter. But, it is important to note that these
+contributions are typically only changes to the repositories. Thus, the Jupyter
+source code, in its entirety is not the copyright of any single person or
+institution.  Instead, it is the collective copyright of the entire Jupyter
+Development Team.  If individual contributors want to maintain a record of what
+changes/contributions they have specific copyright on, they should indicate
+their copyright in the commit message of the change, when they commit the
+change to one of the Jupyter repositories.
+
+With this in mind, the following banner should be used in any source code file
+to indicate the copyright and license terms:
+
+```
+# Copyright (c) Jupyter Development Team.
+# Distributed under the terms of the Modified BSD License.
+```
diff --git a/RELEASING.md b/RELEASING.md
index 9221254..9c15e13 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -9,12 +9,12 @@ The recommended way to make a release is to use [`jupyter_releaser`](https://git
 ### Prerequisites
 
 - First check that the CHANGELOG.md is up to date for the next release version
-- Install packaging requirements: `pip install tbump build tomlkit==0.7.0`
+- Install packaging requirements: `pip install hatch twine`
 
 ### Bump version
 
 - `export version=<NEW_VERSION>`
-- `tbump ${version} --no-push`
+- `hatch version ${version}`
 
 ### Push to GitHub
 
@@ -27,6 +27,6 @@ git push upstream && git push upstream --tags
 ```bash
 rm -rf dist/*
 rm -rf build/*
-python -m build .
+hatch build .
 twine upload dist/*
 ```
diff --git a/debian/changelog b/debian/changelog
index d2fd7de..d9272eb 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+jupyter-console (6.6.3-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 31 May 2023 21:37:38 -0000
+
 jupyter-console (6.4.4-2) unstable; urgency=medium
 
   [ Debian Janitor ]
diff --git a/debian/patches/0002-Disable-sphinxcontrib_github_alt-in-documentation.patch b/debian/patches/0002-Disable-sphinxcontrib_github_alt-in-documentation.patch
index bf24708..21aaf37 100644
--- a/debian/patches/0002-Disable-sphinxcontrib_github_alt-in-documentation.patch
+++ b/debian/patches/0002-Disable-sphinxcontrib_github_alt-in-documentation.patch
@@ -8,11 +8,11 @@ fail.
  docs/conf.py | 1 -
  1 file changed, 1 deletion(-)
 
-diff --git a/docs/conf.py b/docs/conf.py
-index a61206f..d9ad185 100644
---- a/docs/conf.py
-+++ b/docs/conf.py
-@@ -45,7 +45,6 @@ if os.environ.get('READTHEDOCS', ''):
+Index: jupyter-console.git/docs/conf.py
+===================================================================
+--- jupyter-console.git.orig/docs/conf.py
++++ jupyter-console.git/docs/conf.py
+@@ -46,7 +46,6 @@ if os.environ.get('READTHEDOCS', ''):
  # ones.
  extensions = [
      'sphinx.ext.intersphinx',
diff --git a/docs/conf.py b/docs/conf.py
index a61206f..fd908b2 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -17,6 +17,7 @@ import sys
 import os
 import shlex
 import shutil
+from typing import Dict
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
@@ -74,7 +75,7 @@ author = 'The Jupyter Development Team'
 # built documents.
 #
 
-version_ns = {}
+version_ns: Dict = {}
 version_py = os.path.join('..', 'jupyter_console', '_version.py')
 with open(version_py) as f:
     exec(compile(f.read(), version_py, 'exec'), version_ns)
@@ -229,7 +230,7 @@ htmlhelp_basename = 'Jupyterconsoledoc'
 
 # -- Options for LaTeX output ---------------------------------------------
 
-latex_elements = {
+#latex_elements = {
 # The paper size ('letterpaper' or 'a4paper').
 #'papersize': 'letterpaper',
 
@@ -241,7 +242,7 @@ latex_elements = {
 
 # Latex figure (float) alignment
 #'figure_align': 'htbp',
-}
+#}
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title,
diff --git a/jupyter_console/__init__.py b/jupyter_console/__init__.py
index 99a8058..02bbfd1 100644
--- a/jupyter_console/__init__.py
+++ b/jupyter_console/__init__.py
@@ -1,3 +1,3 @@
 """Jupyter terminal console"""
 
-from ._version import version_info, __version__
+from ._version import version_info, __version__  # noqa
diff --git a/jupyter_console/_version.py b/jupyter_console/_version.py
index bafb3ca..f206fd0 100644
--- a/jupyter_console/_version.py
+++ b/jupyter_console/_version.py
@@ -1,7 +1,7 @@
 import re
 from typing import List, Union
 
-__version__ = "6.4.4"
+__version__ = "6.6.3"
 
 # Build up version_info tuple for backwards compatibility
 pattern = r'(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)'
diff --git a/jupyter_console/app.py b/jupyter_console/app.py
index fb2518e..b68b4d7 100644
--- a/jupyter_console/app.py
+++ b/jupyter_console/app.py
@@ -63,7 +63,7 @@ frontend_flags = set(app_flags.keys())
 #-----------------------------------------------------------------------------
 
 
-class ZMQTerminalIPythonApp(JupyterApp, JupyterConsoleApp):
+class ZMQTerminalIPythonApp(JupyterApp, JupyterConsoleApp):  # type:ignore[misc]
     name = "jupyter-console"
     version = __version__
     """Start a terminal frontend to the IPython zmq kernel."""
@@ -85,9 +85,9 @@ class ZMQTerminalIPythonApp(JupyterApp, JupyterConsoleApp):
     """
     examples = _examples
 
-    classes = [ZMQTerminalInteractiveShell] + JupyterConsoleApp.classes
-    flags = Dict(flags)
-    aliases = Dict(aliases)
+    classes = [ZMQTerminalInteractiveShell] + JupyterConsoleApp.classes  # type:ignore[operator]
+    flags = Dict(flags)  # type:ignore[assignment]
+    aliases = Dict(aliases)  # type:ignore[assignment]
     frontend_aliases = Any(frontend_aliases)
     frontend_flags = Any(frontend_flags)
 
diff --git a/jupyter_console/completer.py b/jupyter_console/completer.py
index 9fd5dc5..8130a78 100644
--- a/jupyter_console/completer.py
+++ b/jupyter_console/completer.py
@@ -7,7 +7,7 @@
 from traitlets.config import Configurable
 from traitlets import Float
 
-from jupyter_client.utils import run_sync
+from jupyter_console.utils import run_sync
 
 
 class ZMQCompleter(Configurable):
diff --git a/jupyter_console/ptshell.py b/jupyter_console/ptshell.py
index 1dae25b..12fb38b 100644
--- a/jupyter_console/ptshell.py
+++ b/jupyter_console/ptshell.py
@@ -76,7 +76,7 @@ from pygments.lexers import get_lexer_by_name
 from pygments.util import ClassNotFound
 from pygments.token import Token
 
-from jupyter_client.utils import run_sync
+from jupyter_console.utils import run_sync, ensure_async
 
 
 def ask_yes_no(prompt, default=None, interrupt=None):
@@ -316,7 +316,7 @@ class ZMQTerminalInteractiveShell(SingletonConfigurable):
         ),
         default_value="multicolumn",
     ).tag(config=True)
-    
+
     prompt_includes_vi_mode = Bool(True,
         help="Display the current vi mode (when using vi editing mode)."
     ).tag(config=True)
@@ -374,7 +374,7 @@ class ZMQTerminalInteractiveShell(SingletonConfigurable):
                 and self.prompt_includes_vi_mode):
             return '['+str(self.pt_cli.app.vi_state.input_mode)[3:6]+'] '
         return ''
-    
+
     def get_prompt_tokens(self, ec=None):
         if ec is None:
             ec = self.execution_count
@@ -839,9 +839,10 @@ class ZMQTerminalInteractiveShell(SingletonConfigurable):
     async def handle_external_iopub(self, loop=None):
         while self.keep_running:
             # we need to check for keep_running from time to time
-            poll_result = await self.client.iopub_channel.socket.poll(500)
+            poll_result = await ensure_async(self.client.iopub_channel.socket.poll(0))
             if poll_result:
                 self.handle_iopub()
+            await asyncio.sleep(0.5)
 
     def handle_iopub(self, msg_id=''):
         """Process messages on the IOPub channel
diff --git a/jupyter_console/tests/test_console.py b/jupyter_console/tests/test_console.py
index fb8cc0d..83b5ee6 100644
--- a/jupyter_console/tests/test_console.py
+++ b/jupyter_console/tests/test_console.py
@@ -9,13 +9,17 @@ import sys
 import tempfile
 from subprocess import check_output
 
+from flaky import flaky
 import pytest
 
 from traitlets.tests.utils import check_help_all_output
 
 
-@pytest.mark.xfail
-@pytest.mark.skipif(sys.platform == "win32", reason="skip on windows")
+should_skip = sys.platform == "win32" or sys.version_info < (3,8) or sys.version_info[:2] == (3, 10)  # noqa
+
+
+@flaky
+@pytest.mark.skipif(should_skip, reason="not supported")
 def test_console_starts():
     """test that `jupyter console` starts a terminal"""
     p, pexpect, t = start_console()
@@ -28,7 +32,9 @@ def test_help_output():
     """jupyter console --help-all works"""
     check_help_all_output('jupyter_console')
 
-@pytest.mark.xfail
+
+@flaky
+@pytest.mark.skipif(should_skip, reason="not supported")
 def test_display_text():
     "Ensure display protocol plain/text key is supported"
     # equivalent of:
@@ -68,12 +74,17 @@ def start_console():
     except IOError:
         pytest.skip("Couldn't find command %s" % cmd)
     
-    # timeout after one minute
-    t = 60
+    # timeout after two minutes
+    t = 120
     p.expect(r"In \[\d+\]", timeout=t)
     return p, pexpect, t
 
 
+def test_multiprocessing():
+    p, pexpect, t = start_console()
+    p.sendline('')
+
+
 def test_generate_config():
     """jupyter console --generate-config works"""
     td = tempfile.mkdtemp()
diff --git a/jupyter_console/tests/writetofile.py b/jupyter_console/tests/writetofile.py
index 920ac0e..bd1b6b2 100644
--- a/jupyter_console/tests/writetofile.py
+++ b/jupyter_console/tests/writetofile.py
@@ -2,7 +2,7 @@
 # Copyright (C) 2012 The IPython Development Team
 #
 # Distributed under the terms of the BSD License. The full license is in
-# the file COPYING, distributed as part of this software.
+# the file LICENSE, distributed as part of this software.
 #-----------------------------------------------------------------------------
 
 """
diff --git a/jupyter_console/utils.py b/jupyter_console/utils.py
new file mode 100644
index 0000000..2f9035b
--- /dev/null
+++ b/jupyter_console/utils.py
@@ -0,0 +1,25 @@
+import inspect
+import typing as t
+from jupyter_core.utils import run_sync as _run_sync, ensure_async  # noqa
+
+
+T = t.TypeVar("T")
+
+
+def run_sync(coro: t.Callable[..., t.Union[T, t.Awaitable[T]]]) -> t.Callable[..., T]:
+    """Wraps coroutine in a function that blocks until it has executed.
+
+    Parameters
+    ----------
+    coro : coroutine-function
+        The coroutine-function to be executed.
+
+    Returns
+    -------
+    result :
+        Whatever the coroutine-function returns.
+    """
+    if not inspect.iscoroutinefunction(coro):
+        return t.cast(t.Callable[..., T], coro)
+    return _run_sync(coro)
+
diff --git a/jupyter_console/zmqhistory.py b/jupyter_console/zmqhistory.py
index 1309d92..877d94f 100644
--- a/jupyter_console/zmqhistory.py
+++ b/jupyter_console/zmqhistory.py
@@ -1,21 +1,22 @@
 """ ZMQ Kernel History accessor and manager. """
-#-----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
 #  Copyright (C) 2010-2011 The IPython Development Team.
 #
 #  Distributed under the terms of the BSD License.
 #
-#  The full license is in the file COPYING.txt, distributed with this software.
-#-----------------------------------------------------------------------------
+#  The full license is in the file LICENSE, distributed with this software.
+# -----------------------------------------------------------------------------
 
-#-----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
 # Imports
-#-----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
 
 from IPython.core.history import HistoryAccessorBase
 from traitlets import Dict, List
 
 from queue import Empty  # Py 3
 
+
 class ZMQHistoryManager(HistoryAccessorBase):
     """History accessor and manager for ZMQ-based kernels"""
     input_hist_parsed = List([""])
@@ -44,7 +45,7 @@ class ZMQHistoryManager(HistoryAccessorBase):
         """
         history = []
         if hasattr(self.client, "history"):
-            ## In tests, KernelClient may not have a history method
+            # In tests, KernelClient may not have a history method
             msg_id = self.client.history(raw=raw, output=output,
                                          hist_access_type=hist_access_type,
                                          **kwargs)
@@ -69,7 +70,7 @@ class ZMQHistoryManager(HistoryAccessorBase):
                                   raw=raw, search_raw=search_raw, 
                                   output=output, n=n, unique=unique)
 
-    def get_range(self, session, start=1, stop=None, raw=True,output=False):
+    def get_range(self, session, start=1, stop=None, raw=True, output=False):
         return self._load_history(hist_access_type='range', raw=raw, 
                                   output=output, start=start, stop=stop,
                                   session=session)
@@ -89,4 +90,3 @@ class ZMQHistoryManager(HistoryAccessorBase):
         Nothing to do for ZMQ-based histories.
         """
         pass
-
diff --git a/mypy.ini b/mypy.ini
deleted file mode 100644
index cb3c188..0000000
--- a/mypy.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[mypy]
-python_version = 3.7
-ignore_missing_imports = True
-follow_imports = silent
diff --git a/pyproject.toml b/pyproject.toml
index 37c3250..1eda15d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,23 +1,71 @@
 [build-system]
-requires = ["setuptools>=40.8.0", "wheel"]
-build-backend = "setuptools.build_meta"
+requires = ["hatchling>=1.5"]
+build-backend = "hatchling.build"
 
-[tool.jupyter-releaser]
-skip = ["check-links"]
+[project]
+name = "jupyter-console"
+dynamic = ["version"]
+description = "Jupyter terminal console"
+readme = "README.md"
+license = { file= "LICENSE" }
+requires-python = ">=3.7"
+authors = [
+    { name = "Jupyter Development Team", email = "jupyter@googlegroups.com" },
+]
+keywords = [
+    "Interactive",
+    "Interpreter",
+    "Shell",
+    "Web",
+]
+classifiers = [
+    "Intended Audience :: Developers",
+    "Intended Audience :: Science/Research",
+    "Intended Audience :: System Administrators",
+    "License :: OSI Approved :: BSD License",
+    "Programming Language :: Python",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.7",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+]
+dependencies = [
+    "ipykernel>=6.14",
+    "ipython",
+    "jupyter_client>=7.0.0",
+    "jupyter_core>=4.12,!=5.0.*",
+    "prompt_toolkit>=3.0.30",
+    "pygments",
+    "pyzmq>=17",
+    "traitlets>=5.4",
+]
 
-[tool.check-manifest]
-ignore = [".mailmap", "*.yml", "*.yaml"]
+[project.optional-dependencies]
+test = [
+    "pexpect",
+    "pytest",
+    "flaky",
+]
 
-[tool.tbump.version]
-current = "6.4.4"
-regex = '''
-  (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
-  ((?P<channel>a|b|rc|.dev)(?P<release>\d+))?
-'''
+[project.scripts]
+jupyter-console = "jupyter_console.app:main"
 
-[tool.tbump.git]
-message_template = "Bump to {new_version}"
-tag_template = "v{new_version}"
+[project.urls]
+Homepage = "https://jupyter.org"
 
-[[tool.tbump.file]]
-src = "jupyter_console/_version.py"
+[tool.hatch.version]
+path = "jupyter_console/_version.py"
+
+[tool.pytest.ini_options]
+addopts = "--durations=10"
+
+[tool.flake8]
+max-line-length = "99"
+ignore = "W291, E266, E265, E128, E251, E402, E124, E302, W293, E231, E222, W503, E126, E121, W391, E226, E127, W504"
+
+[tool.mypy]
+python_version = "3.8"
+ignore_missing_imports = true
+follow_imports = "silent"
diff --git a/pytest.ini b/pytest.ini
deleted file mode 100644
index 9da3653..0000000
--- a/pytest.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[pytest]
-addopts = --durations=10
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 7eaac12..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-[flake8]
-max-line-length = 99
-ignore = W291, E266, E265, E128, E251, E402, E124, E302, W293, E231, E222, W503, E126, E121, W391, E226, E127, W504
-
-[metadata]
-name = jupyter_console
-version = attr: jupyter_console._version.__version__
-license_file = LICENSE
diff --git a/setup.py b/setup.py
deleted file mode 100644
index c449c8a..0000000
--- a/setup.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-# Copyright (c) Jupyter Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-from __future__ import print_function
-
-# the name of the project
-name = 'jupyter_console'
-
-import os
-
-from setuptools import setup
-
-pjoin = os.path.join
-here = os.path.abspath(os.path.dirname(__file__))
-pkg_root = pjoin(here, name)
-
-packages = []
-for d, _, _ in os.walk(pjoin(here, name)):
-    if os.path.exists(pjoin(d, '__init__.py')):
-        packages.append(d[len(here)+1:].replace(os.path.sep, '.'))
-
-
-setup_args = dict(
-    name            = name,
-    packages        = packages,
-    description     = "Jupyter terminal console",
-    long_description= "An IPython-like terminal frontend for Jupyter kernels in any language.",
-    long_description_content_type='text/markdown',
-    author          = 'Jupyter Development Team',
-    author_email    = 'jupyter@googlegroups.com',
-    url             = 'https://jupyter.org',
-    license         = 'BSD',
-    platforms       = "Linux, Mac OS X, Windows",
-    keywords        = ['Interactive', 'Interpreter', 'Shell', 'Web'],
-    classifiers     = [
-        'Intended Audience :: Developers',
-        'Intended Audience :: System Administrators',
-        'Intended Audience :: Science/Research',
-        'License :: OSI Approved :: BSD License',
-        'Programming Language :: Python',
-        'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.7',
-        'Programming Language :: Python :: 3.8',
-        'Programming Language :: Python :: 3.9',
-    ],
-    install_requires=[
-        'jupyter_client>=7.0.0',
-        'ipython',
-        'ipykernel',  # bless IPython kernel for now
-        'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
-        'pygments',
-    ],
-    extras_require={
-        'test:sys_platform != "win32"': ['pexpect'],
-    },
-    python_requires='>=3.7',
-    entry_points={
-        'console_scripts': [
-            'jupyter-console = jupyter_console.app:main',
-        ]
-    }
-)
-
-
-if __name__ == '__main__':
-    setup(**setup_args)

More details

Full run details

Historical runs