Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-ipykernel for
openSUSE:Factory checked in at 2024-01-21 23:07:29
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-ipykernel (Old)
and /work/SRC/openSUSE:Factory/.python-ipykernel.new.16006 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ipykernel"
Sun Jan 21 23:07:29 2024 rev:45 rq:1140287 version:6.29.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-ipykernel/python-ipykernel.changes
2023-12-21 23:38:47.074912191 +0100
+++
/work/SRC/openSUSE:Factory/.python-ipykernel.new.16006/python-ipykernel.changes
2024-01-21 23:07:40.108001142 +0100
@@ -1,0 +2,17 @@
+Sun Jan 21 10:45:42 UTC 2024 - Ben Greiner <[email protected]>
+
+- Update to 6.29.0
+ * Always set debugger to true in kernelspec #1191 (@ianthomas23)
+ * Revert "Enable ProactorEventLoop on windows for ipykernel"
+ #1194 (@blink1073)
+ * Make outputs go to correct cell when generated in
+ threads/asyncio #1186 (@krassowski)
+ * Pin pytest-asyncio to 0.23.2 #1189 (@ianthomas23)
+- Update to 6.28.0
+ * Enable ProactorEventLoop on windows for ipykernel #1184
+ (@NewUserHa)
+ * Adds a flag in debug_info for the copyToGlobals support #1099
+ (@brichet)
+ * Support python 3.12 #1185 (@blink1073)
+
+-------------------------------------------------------------------
Old:
----
ipykernel-6.27.1.tar.gz
New:
----
ipykernel-6.29.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-ipykernel.spec ++++++
--- /var/tmp/diff_new_pack.TDT0gs/_old 2024-01-21 23:07:40.676021846 +0100
+++ /var/tmp/diff_new_pack.TDT0gs/_new 2024-01-21 23:07:40.676021846 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-ipykernel
#
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-ipykernel
-Version: 6.27.1
+Version: 6.29.0
Release: 0
Summary: IPython Kernel for Jupyter
License: BSD-3-Clause
@@ -51,7 +51,7 @@
BuildRequires: %{python_module nest-asyncio}
BuildRequires: %{python_module packaging}
BuildRequires: %{python_module psutil}
-BuildRequires: %{python_module pyzmq >= 20}
+BuildRequires: %{python_module pyzmq >= 24}
BuildRequires: %{python_module tornado >= 6.1}
BuildRequires: %{python_module traitlets >= 5.1.0}
BuildRequires: %{python_module jupyter-core >= 5.1 or (%python-jupyter-core
>= 4.12 with %python-jupyter-core < 5.0)}
@@ -64,7 +64,7 @@
Requires: python-nest-asyncio
Requires: python-packaging
Requires: python-psutil
-Requires: python-pyzmq >= 20
+Requires: python-pyzmq >= 24
Requires: python-tornado >= 6.1
Requires: python-traitlets >= 5.4.0
Requires: (python-jupyter-core >= 5.1 or (python-jupyter-core >= 4.12
with python-jupyter-core < 5.0))
@@ -90,6 +90,7 @@
%prep
%autosetup -p1 -n ipykernel-%{version}
sed -i -e 's/, "--color=yes"//' pyproject.toml
+sed -i -e '/ignore:.* current event loop:DeprecationWarning/ a \
"ignore:pytest-asyncio detected an unclosed event loop:DeprecationWarning",'
pyproject.toml
%build
%pyproject_wheel
++++++ ipykernel-6.27.1.tar.gz -> ipykernel-6.29.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/.github/dependabot.yml
new/ipykernel-6.29.0/.github/dependabot.yml
--- old/ipykernel-6.27.1/.github/dependabot.yml 2020-02-02 01:00:00.000000000
+0100
+++ new/ipykernel-6.29.0/.github/dependabot.yml 2020-02-02 01:00:00.000000000
+0100
@@ -4,7 +4,15 @@
directory: "/"
schedule:
interval: "weekly"
+ groups:
+ actions:
+ patterns:
+ - "*"
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
+ groups:
+ actions:
+ patterns:
+ - "*"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/.github/workflows/ci.yml
new/ipykernel-6.29.0/.github/workflows/ci.yml
--- old/ipykernel-6.27.1/.github/workflows/ci.yml 2020-02-02
01:00:00.000000000 +0100
+++ new/ipykernel-6.29.0/.github/workflows/ci.yml 2020-02-02
01:00:00.000000000 +0100
@@ -22,16 +22,16 @@
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
- python-version: ["3.8", "3.11"]
+ python-version: ["3.8", "3.12"]
include:
- os: windows-latest
python-version: "3.9"
- os: ubuntu-latest
- python-version: "pypy-3.8"
+ python-version: "pypy-3.9"
- os: macos-latest
python-version: "3.10"
- os: ubuntu-latest
- python-version: "3.8"
+ python-version: "3.11"
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -153,11 +153,11 @@
- name: List installed packages
run: |
- hatch run test:list
+ hatch -v run test:list
- name: Run the unit tests
run: |
- hatch run test:nowarn || hatch run test:nowarn --lf
+ hatch -v run test:nowarn || hatch run test:nowarn --lf
test_prereleases:
name: Test Prereleases
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/.github/workflows/downstream.yml
new/ipykernel-6.29.0/.github/workflows/downstream.yml
--- old/ipykernel-6.27.1/.github/workflows/downstream.yml 2020-02-02
01:00:00.000000000 +0100
+++ new/ipykernel-6.29.0/.github/workflows/downstream.yml 2020-02-02
01:00:00.000000000 +0100
@@ -92,7 +92,7 @@
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: "3.9"
architecture: "x64"
@@ -124,7 +124,7 @@
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: "3.9"
architecture: "x64"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/.pre-commit-config.yaml
new/ipykernel-6.29.0/.pre-commit-config.yaml
--- old/ipykernel-6.27.1/.pre-commit-config.yaml 2020-02-02
01:00:00.000000000 +0100
+++ new/ipykernel-6.29.0/.pre-commit-config.yaml 2020-02-02
01:00:00.000000000 +0100
@@ -22,7 +22,7 @@
- id: trailing-whitespace
- repo: https://github.com/python-jsonschema/check-jsonschema
- rev: 0.27.1
+ rev: 0.27.3
hooks:
- id: check-github-workflows
@@ -34,13 +34,13 @@
[mdformat-gfm, mdformat-frontmatter, mdformat-footnote]
- repo: https://github.com/pre-commit/mirrors-prettier
- rev: "v3.1.0"
+ rev: "v4.0.0-alpha.8"
hooks:
- id: prettier
types_or: [yaml, html, json]
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: "v1.7.0"
+ rev: "v1.8.0"
hooks:
- id: mypy
files: ipykernel
@@ -74,7 +74,7 @@
- id: rst-inline-touching-normal
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.1.6
+ rev: v0.1.9
hooks:
- id: ruff
types_or: [python, jupyter]
@@ -83,7 +83,7 @@
types_or: [python, jupyter]
- repo: https://github.com/scientific-python/cookie
- rev: "2023.11.17"
+ rev: "2023.12.21"
hooks:
- id: sp-repo-review
additional_dependencies: ["repo-review[cli]"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/CHANGELOG.md
new/ipykernel-6.29.0/CHANGELOG.md
--- old/ipykernel-6.27.1/CHANGELOG.md 2020-02-02 01:00:00.000000000 +0100
+++ new/ipykernel-6.29.0/CHANGELOG.md 2020-02-02 01:00:00.000000000 +0100
@@ -2,6 +2,58 @@
<!-- <START NEW CHANGELOG ENTRY> -->
+## 6.29.0
+
+([Full
Changelog](https://github.com/ipython/ipykernel/compare/v6.28.0...84955484ec1636ee4c7611471d20df2016b5cb57))
+
+### Enhancements made
+
+- Always set debugger to true in kernelspec
[#1191](https://github.com/ipython/ipykernel/pull/1191)
([@ianthomas23](https://github.com/ianthomas23))
+
+### Bugs fixed
+
+- Revert "Enable `ProactorEventLoop` on windows for `ipykernel`"
[#1194](https://github.com/ipython/ipykernel/pull/1194)
([@blink1073](https://github.com/blink1073))
+- Make outputs go to correct cell when generated in threads/asyncio
[#1186](https://github.com/ipython/ipykernel/pull/1186)
([@krassowski](https://github.com/krassowski))
+
+### Maintenance and upkeep improvements
+
+- Pin pytest-asyncio to 0.23.2
[#1189](https://github.com/ipython/ipykernel/pull/1189)
([@ianthomas23](https://github.com/ianthomas23))
+- chore: update pre-commit hooks
[#1187](https://github.com/ipython/ipykernel/pull/1187)
([@pre-commit-ci](https://github.com/pre-commit-ci))
+
+### Contributors to this release
+
+([GitHub contributors page for this
release](https://github.com/ipython/ipykernel/graphs/contributors?from=2023-12-26&to=2024-01-16&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Ablink1073+updated%3A2023-12-26..2024-01-16&type=Issues)
|
[@ianthomas23](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Aianthomas23+updated%3A2023-12-26..2024-01-16&type=Issues)
|
[@krassowski](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Akrassowski+updated%3A2023-12-26..2024-01-16&type=Issues)
|
[@pre-commit-ci](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Apre-commit-ci+updated%3A2023-12-26..2024-01-16&type=Issues)
+
+<!-- <END NEW CHANGELOG ENTRY> -->
+
+## 6.28.0
+
+([Full
Changelog](https://github.com/ipython/ipykernel/compare/v6.27.1...de45c7a49e197f0889f867f33f24cce322768a0e))
+
+### Enhancements made
+
+- Enable `ProactorEventLoop` on windows for `ipykernel`
[#1184](https://github.com/ipython/ipykernel/pull/1184)
([@NewUserHa](https://github.com/NewUserHa))
+- Adds a flag in debug_info for the copyToGlobals support
[#1099](https://github.com/ipython/ipykernel/pull/1099)
([@brichet](https://github.com/brichet))
+
+### Maintenance and upkeep improvements
+
+- Support python 3.12 [#1185](https://github.com/ipython/ipykernel/pull/1185)
([@blink1073](https://github.com/blink1073))
+- Bump actions/setup-python from 4 to 5
[#1181](https://github.com/ipython/ipykernel/pull/1181)
([@dependabot](https://github.com/dependabot))
+- chore: update pre-commit hooks
[#1179](https://github.com/ipython/ipykernel/pull/1179)
([@pre-commit-ci](https://github.com/pre-commit-ci))
+- Refactor execute_request to reduce redundancy and improve consistency
[#1177](https://github.com/ipython/ipykernel/pull/1177)
([@jjvraw](https://github.com/jjvraw))
+
+### Documentation improvements
+
+- Update pytest commands in README
[#1178](https://github.com/ipython/ipykernel/pull/1178)
([@ianthomas23](https://github.com/ianthomas23))
+
+### Contributors to this release
+
+([GitHub contributors page for this
release](https://github.com/ipython/ipykernel/graphs/contributors?from=2023-11-27&to=2023-12-26&type=c))
+
+[@blink1073](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Ablink1073+updated%3A2023-11-27..2023-12-26&type=Issues)
|
[@brichet](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Abrichet+updated%3A2023-11-27..2023-12-26&type=Issues)
|
[@dependabot](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Adependabot+updated%3A2023-11-27..2023-12-26&type=Issues)
|
[@ianthomas23](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Aianthomas23+updated%3A2023-11-27..2023-12-26&type=Issues)
|
[@jjvraw](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Ajjvraw+updated%3A2023-11-27..2023-12-26&type=Issues)
|
[@NewUserHa](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3ANewUserHa+updated%3A2023-11-27..2023-12-26&type=Issues)
|
[@pre-commit-ci](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Apre-commit-ci+updated%3A2023-11-27..2023-12-26&type=Issues)
+
## 6.27.1
([Full
Changelog](https://github.com/ipython/ipykernel/compare/v6.27.0...f9c517e868462d05d6854204c2ad0a244db1cd19))
@@ -16,8 +68,6 @@
[@blink1073](https://github.com/search?q=repo%3Aipython%2Fipykernel+involves%3Ablink1073+updated%3A2023-11-21..2023-11-27&type=Issues)
-<!-- <END NEW CHANGELOG ENTRY> -->
-
## 6.27.0
([Full
Changelog](https://github.com/ipython/ipykernel/compare/v6.26.0...465d34483103d23f471a4795fe5fabb9cf7ac3f5))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/PKG-INFO
new/ipykernel-6.29.0/PKG-INFO
--- old/ipykernel-6.27.1/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/ipykernel-6.29.0/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: ipykernel
-Version: 6.27.1
+Version: 6.29.0
Summary: IPython Kernel for Jupyter
Project-URL: Homepage, https://ipython.org
Author-email: IPython Development Team <[email protected]>
@@ -42,10 +42,6 @@
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Programming Language :: Python :: 3.11
Requires-Python: >=3.8
Requires-Dist: appnope; platform_system == 'Darwin'
Requires-Dist: comm>=0.1.1
@@ -57,7 +53,7 @@
Requires-Dist: nest-asyncio
Requires-Dist: packaging
Requires-Dist: psutil
-Requires-Dist: pyzmq>=20
+Requires-Dist: pyzmq>=24
Requires-Dist: tornado>=6.1
Requires-Dist: traitlets>=5.4.0
Provides-Extra: cov
@@ -82,7 +78,7 @@
Requires-Dist: flaky; extra == 'test'
Requires-Dist: ipyparallel; extra == 'test'
Requires-Dist: pre-commit; extra == 'test'
-Requires-Dist: pytest-asyncio; extra == 'test'
+Requires-Dist: pytest-asyncio==0.23.2; extra == 'test'
Requires-Dist: pytest-cov; extra == 'test'
Requires-Dist: pytest-timeout; extra == 'test'
Requires-Dist: pytest>=7.0; extra == 'test'
@@ -110,7 +106,7 @@
and then from the root directory
```bash
-pytest ipykernel
+pytest
```
## Running tests with coverage
@@ -120,7 +116,7 @@
and then from the root directory
```bash
-pytest ipykernel -vv -s --cov ipykernel --cov-branch --cov-report
term-missing:skip-covered --durations 10
+pytest -vv -s --cov ipykernel --cov-branch --cov-report
term-missing:skip-covered --durations 10
```
## About the IPython Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/README.md
new/ipykernel-6.29.0/README.md
--- old/ipykernel-6.27.1/README.md 2020-02-02 01:00:00.000000000 +0100
+++ new/ipykernel-6.29.0/README.md 2020-02-02 01:00:00.000000000 +0100
@@ -20,7 +20,7 @@
and then from the root directory
```bash
-pytest ipykernel
+pytest
```
## Running tests with coverage
@@ -30,7 +30,7 @@
and then from the root directory
```bash
-pytest ipykernel -vv -s --cov ipykernel --cov-branch --cov-report
term-missing:skip-covered --durations 10
+pytest -vv -s --cov ipykernel --cov-branch --cov-report
term-missing:skip-covered --durations 10
```
## About the IPython Development Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/ipykernel/_version.py
new/ipykernel-6.29.0/ipykernel/_version.py
--- old/ipykernel-6.27.1/ipykernel/_version.py 2020-02-02 01:00:00.000000000
+0100
+++ new/ipykernel-6.29.0/ipykernel/_version.py 2020-02-02 01:00:00.000000000
+0100
@@ -5,7 +5,7 @@
from typing import List
# Version string must appear intact for hatch versioning
-__version__ = "6.27.1"
+__version__ = "6.29.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/ipykernel-6.27.1/ipykernel/debugger.py
new/ipykernel-6.29.0/ipykernel/debugger.py
--- old/ipykernel-6.27.1/ipykernel/debugger.py 2020-02-02 01:00:00.000000000
+0100
+++ new/ipykernel-6.29.0/ipykernel/debugger.py 2020-02-02 01:00:00.000000000
+0100
@@ -605,6 +605,7 @@
"stoppedThreads": list(self.stopped_threads),
"richRendering": True,
"exceptionPaths": ["Python Exceptions"],
+ "copyToGlobals": True,
},
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/ipykernel/iostream.py
new/ipykernel-6.29.0/ipykernel/iostream.py
--- old/ipykernel-6.27.1/ipykernel/iostream.py 2020-02-02 01:00:00.000000000
+0100
+++ new/ipykernel-6.29.0/ipykernel/iostream.py 2020-02-02 01:00:00.000000000
+0100
@@ -5,6 +5,7 @@
import asyncio
import atexit
+import contextvars
import io
import os
import sys
@@ -12,7 +13,7 @@
import traceback
import warnings
from binascii import b2a_hex
-from collections import deque
+from collections import defaultdict, deque
from io import StringIO, TextIOBase
from threading import local
from typing import Any, Callable, Deque, Dict, Optional
@@ -412,7 +413,7 @@
name : str {'stderr', 'stdout'}
the name of the standard stream to replace
pipe : object
- the pip object
+ the pipe object
echo : bool
whether to echo output
watchfd : bool (default, True)
@@ -446,13 +447,19 @@
self.pub_thread = pub_thread
self.name = name
self.topic = b"stream." + name.encode()
- self.parent_header = {}
+ self._parent_header: contextvars.ContextVar[Dict[str, Any]] =
contextvars.ContextVar(
+ "parent_header"
+ )
+ self._parent_header.set({})
+ self._thread_to_parent = {}
+ self._thread_to_parent_header = {}
+ self._parent_header_global = {}
self._master_pid = os.getpid()
self._flush_pending = False
self._subprocess_flush_pending = False
self._io_loop = pub_thread.io_loop
self._buffer_lock = threading.RLock()
- self._buffer = StringIO()
+ self._buffers = defaultdict(StringIO)
self.echo = None
self._isatty = bool(isatty)
self._should_watch = False
@@ -495,6 +502,30 @@
msg = "echo argument must be a file-like object"
raise ValueError(msg)
+ @property
+ def parent_header(self):
+ try:
+ # asyncio-specific
+ return self._parent_header.get()
+ except LookupError:
+ try:
+ # thread-specific
+ identity = threading.current_thread().ident
+ # retrieve the outermost (oldest ancestor,
+ # discounting the kernel thread) thread identity
+ while identity in self._thread_to_parent:
+ identity = self._thread_to_parent[identity]
+ # use the header of the oldest ancestor
+ return self._thread_to_parent_header[identity]
+ except KeyError:
+ # global (fallback)
+ return self._parent_header_global
+
+ @parent_header.setter
+ def parent_header(self, value):
+ self._parent_header_global = value
+ return self._parent_header.set(value)
+
def isatty(self):
"""Return a bool indicating whether this is an 'interactive' stream.
@@ -598,28 +629,28 @@
if self.echo is not sys.__stderr__:
print(f"Flush failed: {e}", file=sys.__stderr__)
- data = self._flush_buffer()
- if data:
- # FIXME: this disables Session's fork-safe check,
- # since pub_thread is itself fork-safe.
- # There should be a better way to do this.
- self.session.pid = os.getpid()
- content = {"name": self.name, "text": data}
- msg = self.session.msg("stream", content,
parent=self.parent_header)
-
- # Each transform either returns a new
- # message or None. If None is returned,
- # the message has been 'used' and we return.
- for hook in self._hooks:
- msg = hook(msg)
- if msg is None:
- return
-
- self.session.send(
- self.pub_thread,
- msg,
- ident=self.topic,
- )
+ for parent, data in self._flush_buffers():
+ if data:
+ # FIXME: this disables Session's fork-safe check,
+ # since pub_thread is itself fork-safe.
+ # There should be a better way to do this.
+ self.session.pid = os.getpid()
+ content = {"name": self.name, "text": data}
+ msg = self.session.msg("stream", content, parent=parent)
+
+ # Each transform either returns a new
+ # message or None. If None is returned,
+ # the message has been 'used' and we return.
+ for hook in self._hooks:
+ msg = hook(msg)
+ if msg is None:
+ return
+
+ self.session.send(
+ self.pub_thread,
+ msg,
+ ident=self.topic,
+ )
def write(self, string: str) -> Optional[int]: # type:ignore[override]
"""Write to current stream after encoding if necessary
@@ -630,6 +661,7 @@
number of items from input parameter written to stream.
"""
+ parent = self.parent_header
if not isinstance(string, str):
msg = f"write() argument must be str, not {type(string)}" #
type:ignore[unreachable]
@@ -649,7 +681,7 @@
is_child = not self._is_master_process()
# only touch the buffer in the IO thread to avoid races
with self._buffer_lock:
- self._buffer.write(string)
+ self._buffers[frozenset(parent.items())].write(string)
if is_child:
# mp.Pool cannot be trusted to flush promptly (or ever),
# and this helps.
@@ -675,19 +707,20 @@
"""Test whether the stream is writable."""
return True
- def _flush_buffer(self):
+ def _flush_buffers(self):
"""clear the current buffer and return the current buffer data."""
- buf = self._rotate_buffer()
- data = buf.getvalue()
- buf.close()
- return data
+ buffers = self._rotate_buffers()
+ for frozen_parent, buffer in buffers.items():
+ data = buffer.getvalue()
+ buffer.close()
+ yield dict(frozen_parent), data
- def _rotate_buffer(self):
+ def _rotate_buffers(self):
"""Returns the current buffer and replaces it with an empty buffer."""
with self._buffer_lock:
- old_buffer = self._buffer
- self._buffer = StringIO()
- return old_buffer
+ old_buffers = self._buffers
+ self._buffers = defaultdict(StringIO)
+ return old_buffers
@property
def _hooks(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/ipykernel/ipkernel.py
new/ipykernel-6.29.0/ipykernel/ipkernel.py
--- old/ipykernel-6.27.1/ipykernel/ipkernel.py 2020-02-02 01:00:00.000000000
+0100
+++ new/ipykernel-6.29.0/ipykernel/ipkernel.py 2020-02-02 01:00:00.000000000
+0100
@@ -2,6 +2,7 @@
import asyncio
import builtins
+import gc
import getpass
import os
import signal
@@ -14,6 +15,7 @@
import comm
from IPython.core import release
from IPython.utils.tokenutil import line_at_cursor, token_at_cursor
+from jupyter_client.session import extract_header
from traitlets import Any, Bool, HasTraits, Instance, List, Type, observe,
observe_compat
from zmq.eventloop.zmqstream import ZMQStream
@@ -22,6 +24,7 @@
from .compiler import XCachingCompiler
from .debugger import Debugger, _is_debugpy_available
from .eventloops import _use_appnope
+from .iostream import OutStream
from .kernelbase import Kernel as KernelBase
from .kernelbase import _accepts_parameters
from .zmqshell import ZMQInteractiveShell
@@ -151,6 +154,14 @@
appnope.nope()
+ self._new_threads_parent_header = {}
+ self._initialize_thread_hooks()
+
+ if hasattr(gc, "callbacks"):
+ # while `gc.callbacks` exists since Python 3.3, pypy does not
+ # implement it even as of 3.9.
+ gc.callbacks.append(self._clean_thread_parent_frames)
+
help_links = List(
[
{
@@ -341,6 +352,12 @@
# restore the previous sigint handler
signal.signal(signal.SIGINT, save_sigint)
+ async def execute_request(self, stream, ident, parent):
+ """Override for cell output - cell reconciliation."""
+ parent_header = extract_header(parent)
+ self._associate_new_top_level_threads_with(parent_header)
+ await super().execute_request(stream, ident, parent)
+
async def do_execute(
self,
code,
@@ -706,6 +723,83 @@
self.shell.reset(False)
return dict(status="ok")
+ def _associate_new_top_level_threads_with(self, parent_header):
+ """Store the parent header to associate it with new top-level
threads"""
+ self._new_threads_parent_header = parent_header
+
+ def _initialize_thread_hooks(self):
+ """Store thread hierarchy and thread-parent_header associations."""
+ stdout = self._stdout
+ stderr = self._stderr
+ kernel_thread_ident = threading.get_ident()
+ kernel = self
+ _threading_Thread_run = threading.Thread.run
+ _threading_Thread__init__ = threading.Thread.__init__
+
+ def run_closure(self: threading.Thread):
+ """Wrap the `threading.Thread.start` to intercept thread identity.
+
+ This is needed because there is no "start" hook yet, but there
+ might be one in the future: https://bugs.python.org/issue14073
+
+ This is a no-op if the `self._stdout` and `self._stderr` are not
+ sub-classes of `OutStream`.
+ """
+
+ try:
+ parent = self._ipykernel_parent_thread_ident #
type:ignore[attr-defined]
+ except AttributeError:
+ return
+ for stream in [stdout, stderr]:
+ if isinstance(stream, OutStream):
+ if parent == kernel_thread_ident:
+ stream._thread_to_parent_header[
+ self.ident
+ ] = kernel._new_threads_parent_header
+ else:
+ stream._thread_to_parent[self.ident] = parent
+ _threading_Thread_run(self)
+
+ def init_closure(self: threading.Thread, *args, **kwargs):
+ _threading_Thread__init__(self, *args, **kwargs)
+ self._ipykernel_parent_thread_ident = threading.get_ident() #
type:ignore[attr-defined]
+
+ threading.Thread.__init__ = init_closure # type:ignore[method-assign]
+ threading.Thread.run = run_closure # type:ignore[method-assign]
+
+ def _clean_thread_parent_frames(
+ self, phase: t.Literal["start", "stop"], info: t.Dict[str, t.Any]
+ ):
+ """Clean parent frames of threads which are no longer running.
+ This is meant to be invoked by garbage collector callback hook.
+
+ The implementation enumerates the threads because there is no "exit"
hook yet,
+ but there might be one in the future:
https://bugs.python.org/issue14073
+
+ This is a no-op if the `self._stdout` and `self._stderr` are not
+ sub-classes of `OutStream`.
+ """
+ # Only run before the garbage collector starts
+ if phase != "start":
+ return
+ active_threads = {thread.ident for thread in threading.enumerate()}
+ for stream in [self._stdout, self._stderr]:
+ if isinstance(stream, OutStream):
+ thread_to_parent_header = stream._thread_to_parent_header
+ for identity in list(thread_to_parent_header.keys()):
+ if identity not in active_threads:
+ try:
+ del thread_to_parent_header[identity]
+ except KeyError:
+ pass
+ thread_to_parent = stream._thread_to_parent
+ for identity in list(thread_to_parent.keys()):
+ if identity not in active_threads:
+ try:
+ del thread_to_parent[identity]
+ except KeyError:
+ pass
+
# This exists only for backwards compatibility - use IPythonKernel instead
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/ipykernel/kernelbase.py
new/ipykernel-6.29.0/ipykernel/kernelbase.py
--- old/ipykernel-6.27.1/ipykernel/kernelbase.py 2020-02-02
01:00:00.000000000 +0100
+++ new/ipykernel-6.29.0/ipykernel/kernelbase.py 2020-02-02
01:00:00.000000000 +0100
@@ -61,6 +61,7 @@
from ipykernel.jsonutil import json_clean
from ._version import kernel_protocol_version
+from .iostream import OutStream
def _accepts_parameters(meth, param_names):
@@ -272,6 +273,13 @@
def __init__(self, **kwargs):
"""Initialize the kernel."""
super().__init__(**kwargs)
+
+ # Kernel application may swap stdout and stderr to OutStream,
+ # which is the case in `IPKernelApp.init_io`, hence `sys.stdout`
+ # can already by different from TextIO at initialization time.
+ self._stdout: OutStream | t.TextIO = sys.stdout
+ self._stderr: OutStream | t.TextIO = sys.stderr
+
# Build dict of handlers for message types
self.shell_handlers = {}
for msg_type in self.msg_types:
@@ -283,6 +291,11 @@
self.control_queue: Queue[t.Any] = Queue()
+ # Storing the accepted parameters for do_execute, used in
execute_request
+ self._do_exec_accepted_params = _accepts_parameters(
+ self.do_execute, ["cell_meta", "cell_id"]
+ )
+
def dispatch_control(self, msg):
self.control_queue.put_nowait(msg)
@@ -724,6 +737,8 @@
store_history = content.get("store_history", not silent)
user_expressions = content.get("user_expressions", {})
allow_stdin = content.get("allow_stdin", False)
+ cell_meta = parent.get("metadata", {})
+ cell_id = cell_meta.get("cellId")
except Exception:
self.log.error("Got bad msg: ")
self.log.error("%s", parent)
@@ -739,12 +754,6 @@
self.execution_count += 1
self._publish_execute_input(code, parent, self.execution_count)
- cell_meta = parent.get("metadata", {})
- cell_id = cell_meta.get("cellId")
-
- # Check which parameters do_execute can accept
- accepts_params = _accepts_parameters(self.do_execute, ["cell_meta",
"cell_id"])
-
# Arguments based on the do_execute signature
do_execute_args = {
"code": code,
@@ -754,9 +763,9 @@
"allow_stdin": allow_stdin,
}
- if accepts_params["cell_meta"]:
+ if self._do_exec_accepted_params["cell_meta"]:
do_execute_args["cell_meta"] = cell_meta
- if accepts_params["cell_id"]:
+ if self._do_exec_accepted_params["cell_id"]:
do_execute_args["cell_id"] = cell_id
# Call do_execute with the appropriate arguments
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/ipykernel/kernelspec.py
new/ipykernel-6.29.0/ipykernel/kernelspec.py
--- old/ipykernel-6.27.1/ipykernel/kernelspec.py 2020-02-02
01:00:00.000000000 +0100
+++ new/ipykernel-6.29.0/ipykernel/kernelspec.py 2020-02-02
01:00:00.000000000 +0100
@@ -66,7 +66,7 @@
"argv": make_ipkernel_cmd(extra_arguments=extra_arguments),
"display_name": "Python %i (ipykernel)" % sys.version_info[0],
"language": "python",
- "metadata": {"debugger": _is_debugpy_available},
+ "metadata": {"debugger": True},
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/pyproject.toml
new/ipykernel-6.29.0/pyproject.toml
--- old/ipykernel-6.27.1/pyproject.toml 2020-02-02 01:00:00.000000000 +0100
+++ new/ipykernel-6.29.0/pyproject.toml 2020-02-02 01:00:00.000000000 +0100
@@ -17,10 +17,6 @@
"License :: OSI Approved :: BSD License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
- "Programming Language :: Python :: 3.10",
- "Programming Language :: Python :: 3.11",
]
urls = {Homepage = "https://ipython.org"}
requires-python = ">=3.8"
@@ -36,7 +32,7 @@
"tornado>=6.1",
"matplotlib-inline>=0.1",
'appnope;platform_system=="Darwin"',
- "pyzmq>=20",
+ "pyzmq>=24",
"psutil",
"packaging",
]
@@ -57,7 +53,7 @@
"flaky",
"ipyparallel",
"pre-commit",
- "pytest-asyncio",
+ "pytest-asyncio==0.23.2",
"pytest-timeout"
]
cov = [
@@ -175,6 +171,9 @@
"ignore:unclosed event loop:ResourceWarning",
"ignore:There is no current event loop:DeprecationWarning",
"module:Jupyter is migrating its paths to use standard
platformdirs:DeprecationWarning",
+
+ # Ignore datetime warning.
+ "ignore:datetime.datetime.utc:DeprecationWarning",
]
[tool.coverage.report]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/tests/test_debugger.py
new/ipykernel-6.29.0/tests/test_debugger.py
--- old/ipykernel-6.27.1/tests/test_debugger.py 2020-02-02 01:00:00.000000000
+0100
+++ new/ipykernel-6.29.0/tests/test_debugger.py 2020-02-02 01:00:00.000000000
+0100
@@ -6,8 +6,13 @@
seq = 0
-# Skip if debugpy is not available
-pytest.importorskip("debugpy")
+# Tests support debugpy not being installed, in which case the tests don't do
anything useful
+# functionally as the debug message replies are usually empty dictionaries,
but they confirm that
+# ipykernel doesn't block, or segfault, or raise an exception.
+try:
+ import debugpy
+except ImportError:
+ debugpy = None
def wait_for_debug_request(kernel, command, arguments=None, full_reply=False):
@@ -85,15 +90,21 @@
"locale": "en",
},
)
- assert reply["success"]
+ if debugpy:
+ assert reply["success"]
+ else:
+ assert reply == {}
def test_attach_debug(kernel_with_debug):
reply = wait_for_debug_request(
kernel_with_debug, "evaluate", {"expression": "'a' + 'b'", "context":
"repl"}
)
- assert reply["success"]
- assert reply["body"]["result"] == ""
+ if debugpy:
+ assert reply["success"]
+ assert reply["body"]["result"] == ""
+ else:
+ assert reply == {}
def test_set_breakpoints(kernel_with_debug):
@@ -104,7 +115,11 @@
f(2, 3)"""
r = wait_for_debug_request(kernel_with_debug, "dumpCell", {"code": code})
- source = r["body"]["sourcePath"]
+ if debugpy:
+ source = r["body"]["sourcePath"]
+ else:
+ assert r == {}
+ source = "non-existent path"
reply = wait_for_debug_request(
kernel_with_debug,
@@ -115,20 +130,29 @@
"sourceModified": False,
},
)
- assert reply["success"]
- assert len(reply["body"]["breakpoints"]) == 1
- assert reply["body"]["breakpoints"][0]["verified"]
- assert reply["body"]["breakpoints"][0]["source"]["path"] == source
+ if debugpy:
+ assert reply["success"]
+ assert len(reply["body"]["breakpoints"]) == 1
+ assert reply["body"]["breakpoints"][0]["verified"]
+ assert reply["body"]["breakpoints"][0]["source"]["path"] == source
+ else:
+ assert reply == {}
r = wait_for_debug_request(kernel_with_debug, "debugInfo")
def func(b):
return b["source"]
- assert source in map(func, r["body"]["breakpoints"])
+ if debugpy:
+ assert source in map(func, r["body"]["breakpoints"])
+ else:
+ assert r == {}
r = wait_for_debug_request(kernel_with_debug, "configurationDone")
- assert r["success"]
+ if debugpy:
+ assert r["success"]
+ else:
+ assert r == {}
def test_stop_on_breakpoint(kernel_with_debug):
@@ -139,7 +163,11 @@
f(2, 3)"""
r = wait_for_debug_request(kernel_with_debug, "dumpCell", {"code": code})
- source = r["body"]["sourcePath"]
+ if debugpy:
+ source = r["body"]["sourcePath"]
+ else:
+ assert r == {}
+ source = "some path"
wait_for_debug_request(kernel_with_debug, "debugInfo")
@@ -157,6 +185,10 @@
kernel_with_debug.execute(code)
+ if not debugpy:
+ # Cannot stop on breakpoint if debugpy not installed
+ return
+
# Wait for stop on breakpoint
msg: dict = {"msg_type": "", "content": {}}
while msg.get("msg_type") != "debug_event" or msg["content"].get("event")
!= "stopped":
@@ -175,7 +207,11 @@
f(2, 3)"""
r = wait_for_debug_request(kernel_with_debug, "dumpCell", {"code": code})
- source = r["body"]["sourcePath"]
+ if debugpy:
+ source = r["body"]["sourcePath"]
+ else:
+ assert r == {}
+ source = "some path"
wait_for_debug_request(kernel_with_debug, "debugInfo")
@@ -193,6 +229,10 @@
kernel_with_debug.execute(code)
+ if not debugpy:
+ # Cannot stop on breakpoint if debugpy not installed
+ return
+
# Wait for stop on breakpoint
msg: dict = {"msg_type": "", "content": {}}
while msg.get("msg_type") != "debug_event" or msg["content"].get("event")
!= "stopped":
@@ -216,7 +256,10 @@
def func(v):
return v["name"]
- assert var_name in list(map(func, r["body"]["variables"]))
+ if debugpy:
+ assert var_name in list(map(func, r["body"]["variables"]))
+ else:
+ assert r == {}
reply = wait_for_debug_request(
kernel_with_debug,
@@ -224,7 +267,10 @@
{"variableName": var_name},
)
- assert reply["body"]["data"] == {"text/plain": f"'{value}'"}
+ if debugpy:
+ assert reply["body"]["data"] == {"text/plain": f"'{value}'"}
+ else:
+ assert reply == {}
def test_rich_inspect_at_breakpoint(kernel_with_debug):
@@ -235,7 +281,11 @@
f(2, 3)"""
r = wait_for_debug_request(kernel_with_debug, "dumpCell", {"code": code})
- source = r["body"]["sourcePath"]
+ if debugpy:
+ source = r["body"]["sourcePath"]
+ else:
+ assert r == {}
+ source = "some path"
wait_for_debug_request(
kernel_with_debug,
@@ -253,6 +303,10 @@
kernel_with_debug.execute(code)
+ if not debugpy:
+ # Cannot stop on breakpoint if debugpy not installed
+ return
+
# Wait for stop on breakpoint
msg: dict = {"msg_type": "", "content": {}}
while msg.get("msg_type") != "debug_event" or msg["content"].get("event")
!= "stopped":
@@ -304,7 +358,11 @@
# Init debugger and set breakpoint
r = wait_for_debug_request(kernel_with_debug, "dumpCell", {"code": code})
- source = r["body"]["sourcePath"]
+ if debugpy:
+ source = r["body"]["sourcePath"]
+ else:
+ assert r == {}
+ source = "some path"
wait_for_debug_request(
kernel_with_debug,
@@ -323,6 +381,10 @@
# Execute code
kernel_with_debug.execute(code)
+ if not debugpy:
+ # Cannot stop on breakpoint if debugpy not installed
+ return
+
# Wait for stop on breakpoint
msg: dict = {"msg_type": "", "content": {}}
while msg.get("msg_type") != "debug_event" or msg["content"].get("event")
!= "stopped":
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ipykernel-6.27.1/tests/test_kernel.py
new/ipykernel-6.29.0/tests/test_kernel.py
--- old/ipykernel-6.27.1/tests/test_kernel.py 2020-02-02 01:00:00.000000000
+0100
+++ new/ipykernel-6.29.0/tests/test_kernel.py 2020-02-02 01:00:00.000000000
+0100
@@ -58,6 +58,106 @@
_check_master(kc, expected=True)
+def test_print_to_correct_cell_from_thread():
+ """should print to the cell that spawned the thread, not a subsequently
run cell"""
+ iterations = 5
+ interval = 0.25
+ code = f"""\
+ from threading import Thread
+ from time import sleep
+
+ def thread_target():
+ for i in range({iterations}):
+ print(i, end='', flush=True)
+ sleep({interval})
+
+ Thread(target=thread_target).start()
+ """
+ with kernel() as kc:
+ thread_msg_id = kc.execute(code)
+ _ = kc.execute("pass")
+
+ received = 0
+ while received < iterations:
+ msg = kc.get_iopub_msg(timeout=interval * 2)
+ if msg["msg_type"] != "stream":
+ continue
+ content = msg["content"]
+ assert content["name"] == "stdout"
+ assert content["text"] == str(received)
+ # this is crucial as the parent header decides to which cell the
output goes
+ assert msg["parent_header"]["msg_id"] == thread_msg_id
+ received += 1
+
+
+def test_print_to_correct_cell_from_child_thread():
+ """should print to the cell that spawned the thread, not a subsequently
run cell"""
+ iterations = 5
+ interval = 0.25
+ code = f"""\
+ from threading import Thread
+ from time import sleep
+
+ def child_target():
+ for i in range({iterations}):
+ print(i, end='', flush=True)
+ sleep({interval})
+
+ def parent_target():
+ sleep({interval})
+ Thread(target=child_target).start()
+
+ Thread(target=parent_target).start()
+ """
+ with kernel() as kc:
+ thread_msg_id = kc.execute(code)
+ _ = kc.execute("pass")
+
+ received = 0
+ while received < iterations:
+ msg = kc.get_iopub_msg(timeout=interval * 2)
+ if msg["msg_type"] != "stream":
+ continue
+ content = msg["content"]
+ assert content["name"] == "stdout"
+ assert content["text"] == str(received)
+ # this is crucial as the parent header decides to which cell the
output goes
+ assert msg["parent_header"]["msg_id"] == thread_msg_id
+ received += 1
+
+
+def test_print_to_correct_cell_from_asyncio():
+ """should print to the cell that scheduled the task, not a subsequently
run cell"""
+ iterations = 5
+ interval = 0.25
+ code = f"""\
+ import asyncio
+
+ async def async_task():
+ for i in range({iterations}):
+ print(i, end='', flush=True)
+ await asyncio.sleep({interval})
+
+ loop = asyncio.get_event_loop()
+ loop.create_task(async_task());
+ """
+ with kernel() as kc:
+ thread_msg_id = kc.execute(code)
+ _ = kc.execute("pass")
+
+ received = 0
+ while received < iterations:
+ msg = kc.get_iopub_msg(timeout=interval * 2)
+ if msg["msg_type"] != "stream":
+ continue
+ content = msg["content"]
+ assert content["name"] == "stdout"
+ assert content["text"] == str(received)
+ # this is crucial as the parent header decides to which cell the
output goes
+ assert msg["parent_header"]["msg_id"] == thread_msg_id
+ received += 1
+
+
@pytest.mark.skip(reason="Currently don't capture during test as pytest does
its own capturing")
def test_capture_fd():
"""simple print statement in kernel"""