diff --git a/README.md b/README.md index 943ad46..c75a5de 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ jupyter lab ```bash # create a new conda environment -mamba create -n ipylab -c conda-forge jupyter-packaging nodejs python -y +mamba create -n ipylab -c conda-forge jupyter-packaging nodejs python=3.10 -y # activate the environment conda activate ipylab diff --git a/ipylab/asyncwidget.py b/ipylab/asyncwidget.py index e31d08a..2b4794c 100644 --- a/ipylab/asyncwidget.py +++ b/ipylab/asyncwidget.py @@ -3,8 +3,8 @@ from __future__ import annotations import asyncio -import enum import inspect +import sys import textwrap import types import uuid @@ -17,6 +17,12 @@ import ipylab._frontend as _fe from ipylab.hookspecs import pm +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + from backports.strenum import StrEnum + + __all__ = ["AsyncWidgetBase", "WidgetBase", "register", "pack", "Widget"] @@ -48,7 +54,7 @@ def pack_code(code: str | types.ModuleType) -> str: return code -class TransformMode(enum.StrEnum): +class TransformMode(StrEnum): """The transformation to apply to the result of frontend operations prior to sending. - done: [default] A string '--DONE--' diff --git a/ipylab/commands.py b/ipylab/commands.py index 772e1a5..9ea43ee 100644 --- a/ipylab/commands.py +++ b/ipylab/commands.py @@ -51,16 +51,15 @@ def _observe_commands(self, change): async def _do_operation_for_frontend( self, operation: str, payload: dict, buffers: list ) -> bool | None: - match operation: - case "execute": - command_id = payload.get("id") - cmd = self._execute_callbacks[command_id] - result = cmd(**payload.get("kwgs", {})) - if asyncio.iscoroutine(result): - result = await result - return result - case _: - pm.hook.unhandled_frontend_operation_message(self, operation) + if operation == "execute": + command_id = payload.get("id") + cmd = self._execute_callbacks[command_id] + result = cmd(**payload.get("kwgs", {})) + if asyncio.iscoroutine(result): + result = await result + return result + else: + pm.hook.unhandled_frontend_operation_message(self, operation) def execute( self, diff --git a/ipylab/jupyterfrontend.py b/ipylab/jupyterfrontend.py index be50762..2d8b9c0 100644 --- a/ipylab/jupyterfrontend.py +++ b/ipylab/jupyterfrontend.py @@ -4,9 +4,9 @@ import asyncio import types -from typing import NotRequired, Self, TypedDict from traitlets import Dict, Instance, Tuple, Unicode +from typing_extensions import NotRequired, Self, TypedDict from ipylab.asyncwidget import ( AsyncWidgetBase, @@ -70,10 +70,10 @@ def sessionManager(self) -> SessionManager: async def wait_ready(self, timeout=5) -> Self: """Wait until connected to app indicates it is ready.""" if not self._ready_response.is_set(): - async with asyncio.TaskGroup() as group, asyncio.timeout(timeout): - group.create_task(super().wait_ready()) - group.create_task(self.commands.wait_ready()) - group.create_task(self.command_pallet.wait_ready()) + future = asyncio.gather( + super().wait_ready(), self.commands.wait_ready(), self.command_pallet.wait_ready() + ) + await asyncio.wait_for(future, timeout) return self def _init_python_backend(self) -> str: diff --git a/ipylab/shell.py b/ipylab/shell.py index f04c714..a5b08ca 100644 --- a/ipylab/shell.py +++ b/ipylab/shell.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio -import enum +import sys import typing as t import ipywidgets as ipw @@ -11,6 +11,11 @@ from ipylab import pack from ipylab.jupyterfrontend_subsection import JupyterFrontEndSubsection +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + from backports.strenum import StrEnum + if t.TYPE_CHECKING: from ipywidgets import Widget @@ -18,7 +23,7 @@ __all__ = ["Area", "InsertMode", "Shell"] -class Area(enum.StrEnum): +class Area(StrEnum): # https://github.com/jupyterlab/jupyterlab/blob/da8e7bda5eebd22319f59e5abbaaa9917872a7e8/packages/application/src/shell.ts#L500 main = "main" left = "left" @@ -30,7 +35,7 @@ class Area(enum.StrEnum): menu = "menu" -class InsertMode(enum.StrEnum): +class InsertMode(StrEnum): # ref https://lumino.readthedocs.io/en/latest/api/types/widgets.DockLayout.InsertMode.html split_top = "split-top" split_left = "split-left" diff --git a/pyproject.toml b/pyproject.toml index dea927e..25e1e0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ build-backend = "hatchling.build" name = "ipylab" readme = "README.md" license = { file = "LICENSE" } -requires-python = ">=3.11" +requires-python = ">=3.10" classifiers = [ "Framework :: Jupyter", "Framework :: Jupyter :: JupyterLab", @@ -25,7 +25,13 @@ classifiers = [ ] dynamic = ["version", "description", "authors", "urls", "keywords"] -dependencies = ["jupyterlab>=4.0.9", "ipywidgets>=8.1.0,<9", "pluggy>=1.1"] +dependencies = [ + "jupyterlab>=4.0.9", + "ipywidgets>=8.1.0,<9", + "pluggy>=1.1", + 'backports.strenum;python_version<"3.11"', + 'typing-extensions>=4.9.0', +] [project.optional-dependencies] dev = ["hatch", "ruff", "pre-commit"] @@ -88,7 +94,7 @@ before-build-python = ["jlpm clean:all"] ignore = ["W002"] [tool.ruff] -target-version = "py311" +target-version = "py310" line-length = 100 fix-only = true extend-include = ["*.ipynb"]