Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ jobs:
- qt5
- qt6
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
Expand Down Expand Up @@ -153,7 +152,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.10"]
python-version: ["3.11"]
steps:
- name: Checkout
uses: actions/checkout@v7
Expand Down Expand Up @@ -183,7 +182,7 @@ jobs:

- uses: actions/setup-python@v7
with:
python-version: "3.10"
python-version: "3.11"

- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
Expand Down
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ repos:
additional_dependencies:
- "pyyaml"
- "packaging"
- "tomli; python_version < '3.11'"

- repo: https://github.com/adamchainz/blacken-docs
rev: "1.20.0"
Expand Down
2 changes: 1 addition & 1 deletion ipykernel/heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def run(self):

while True:
try:
zmq.device(zmq.QUEUE, self.socket, self.socket) # type:ignore[attr-defined]
zmq.device(zmq.QUEUE, self.socket, self.socket)
except zmq.ZMQError as e:
if e.errno == errno.EINTR:
# signal interrupt, resume heartbeat
Expand Down
12 changes: 1 addition & 11 deletions ipykernel/kernelbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,24 +551,14 @@ def schedule_next():
# begin polling the eventloop
schedule_next()

async def _create_control_lock(self):
# This can be removed when minimum python increases to 3.10
self._control_lock = asyncio.Lock()

def start(self):
"""register dispatchers for streams"""
self.io_loop = ioloop.IOLoop.current()

if self.control_stream:
self.control_stream.on_recv(self.dispatch_control, copy=False)

if self.control_thread and sys.version_info < (3, 10):
# Before Python 3.10 we need to ensure the _control_lock is created in the
# thread that uses it. When our minimum python is 3.10 we can remove this
# and always use the else below, or just assign it where it is declared.
self.control_thread.io_loop.add_callback(self._create_control_lock)
else:
self._control_lock = asyncio.Lock()
self._control_lock = asyncio.Lock()

if self.shell_stream:
if self.shell_channel_thread:
Expand Down
2 changes: 1 addition & 1 deletion ipykernel/kernelspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def install(
python_arguments = None

# addresses the debugger warning from debugpy about frozen modules
if sys.version_info >= (3, 11) and platform.python_implementation() == "CPython":
if platform.python_implementation() == "CPython":
if not frozen_modules:
# disable frozen modules
python_arguments = ["-Xfrozen_modules=off"]
Expand Down
34 changes: 5 additions & 29 deletions ipykernel/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
from __future__ import annotations

import asyncio
import sys
import typing as t
from collections.abc import Mapping
from contextvars import copy_context
from functools import partial, wraps
from functools import wraps

if t.TYPE_CHECKING:
from collections.abc import Callable
Expand Down Expand Up @@ -46,36 +45,13 @@ def _async_in_context(
) -> Callable[..., t.Coroutine[T, U, V]]:
"""
Wrapper to run a coroutine in a persistent ContextVar Context.

Backports asyncio.create_task(context=...) behavior from Python 3.11
"""
if context is None:
context = copy_context()

if sys.version_info >= (3, 11):

@wraps(f)
async def run_in_context(*args, **kwargs):
coro = f(*args, **kwargs)
return await asyncio.create_task(coro, context=context)

return run_in_context

# don't need this backport when we require 3.11
# context_holder so we have a modifiable container for later calls
context_holder = [context] # type: ignore[unreachable]

async def preserve_context(f, *args, **kwargs):
"""call a coroutine, preserving the context after it is called"""
try:
return await f(*args, **kwargs)
finally:
# persist changes to the context for future calls
context_holder[0] = copy_context()

@wraps(f)
async def run_in_context_pre311(*args, **kwargs):
ctx = context_holder[0]
return await ctx.run(partial(asyncio.create_task, preserve_context(f, *args, **kwargs)))
async def run_in_context(*args, **kwargs):
coro = f(*args, **kwargs)
return await asyncio.create_task(coro, context=context)

return run_in_context_pre311
return run_in_context
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
]
requires-python = ">=3.10"
requires-python = ">=3.11"
dependencies = [
"debugpy>=1.6.5",
"ipython>=7.23.1",
Expand Down
6 changes: 1 addition & 5 deletions scripts/check_mypy_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@
from __future__ import annotations

import sys

try:
import tomllib
except ImportError:
import tomli as tomllib # type: ignore[no-reuse-def]
import tomllib

import yaml
from packaging.requirements import Requirement
Expand Down
4 changes: 0 additions & 4 deletions tests/test_eventloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,6 @@ def test_cocoa_loop(kernel):
def test_qt_enable_gui(gui, kernel, capsys):
if os.getenv("GITHUB_ACTIONS", None) == "true" and gui == "qt5":
pytest.skip("Qt5 and GitHub action crash CPython")
if gui == "qt6" and sys.version_info < (3, 10):
pytest.skip(
"qt6 fails on 3.9 with AttributeError: module 'PySide6.QtPrintSupport' has no attribute 'QApplication'"
)
if sys.platform == "linux" and gui == "qt6" and os.getenv("GITHUB_ACTIONS", None) == "true":
pytest.skip("qt6 fails on github CI with missing libEGL.so.1")
enable_gui(gui, kernel)
Expand Down
12 changes: 4 additions & 8 deletions tests/test_kernelspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def test_install_env(tmp_path, env):
assert "env" not in spec


@pytest.mark.skipif(sys.version_info < (3, 11) or not is_cpython, reason="requires cPython 3.11")
@pytest.mark.skipif(not is_cpython, reason="requires CPython")
def test_install_frozen_modules_on():
system_jupyter_dir = tempfile.mkdtemp()

Expand All @@ -168,7 +168,7 @@ def test_install_frozen_modules_on():
assert "-Xfrozen_modules=off" not in spec["argv"]


@pytest.mark.skipif(sys.version_info < (3, 11) or not is_cpython, reason="requires cPython 3.11")
@pytest.mark.skipif(not is_cpython, reason="requires CPython")
def test_install_frozen_modules_off():
system_jupyter_dir = tempfile.mkdtemp()

Expand All @@ -182,13 +182,9 @@ def test_install_frozen_modules_off():
assert spec["argv"][1] == "-Xfrozen_modules=off"


@pytest.mark.skipif(
sys.version_info >= (3, 11) or is_cpython,
reason="checks versions older than 3.11 and other Python implementations",
)
@pytest.mark.skipif(is_cpython, reason="checks non-CPython implementations")
def test_install_frozen_modules_no_op():
# ensure we do not add add Xfrozen_modules on older Python versions
# (although cPython does not error out on unknown X options as of 3.8)
# ensure we do not add -Xfrozen_modules on non-CPython implementations
system_jupyter_dir = tempfile.mkdtemp()

with mock.patch("jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH", [system_jupyter_dir]):
Expand Down