Skip to content

Commit c7ae6f1

Browse files
Don’t render JSON by default and add min tests (#81)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 3b4322e commit c7ae6f1

File tree

5 files changed

+73
-20
lines changed

5 files changed

+73
-20
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
python-version: ['3.10', '3.13']
17+
include:
18+
- python-version: '3.10'
19+
- python-version: '3.13'
20+
- python-version: '3.13'
21+
extras: min
1822
steps:
1923
- uses: actions/checkout@v4
2024
with:
@@ -26,7 +30,7 @@ jobs:
2630
- uses: hynek/setup-cached-uv@v2
2731
with:
2832
cache-dependency-path: pyproject.toml
29-
- run: uv pip install --system -e .[test,jupyter]
33+
- run: uv pip install --system -e .[${{ matrix.extras == 'min' && 'test' || 'test,jupyter' }}]
3034
- uses: pavelzw/pytest-action@v2
3135
with:
3236
custom-arguments: --color=yes

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ repos:
1717
hooks:
1818
- id: pyproject-fmt
1919
- repo: https://github.com/biomejs/pre-commit
20-
rev: v0.6.0
20+
rev: v0.6.1
2121
hooks:
2222
- id: biome-format
2323
additional_dependencies: ["@biomejs/[email protected]"]

pyproject.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,17 @@ urls.Source = "https://github.com/flying-sheep/session-info2"
5656
[tool.hatch.version]
5757
source = "vcs"
5858

59+
[[tool.hatch.envs.hatch-test.matrix]]
60+
python = [ "3.10", "3.11", "3.12", "3.13" ]
61+
deps = [ "all", "min" ]
62+
5963
[tool.hatch.envs.hatch-test]
6064
default-args = [ ]
61-
features = [ "test", "jupyter" ]
65+
features = [ "test" ]
6266
extra-dependencies = [ "pyinstrument" ]
67+
overrides.matrix.deps.features = [
68+
{ if = [ "all" ], value = "jupyter" },
69+
]
6370

6471
[tool.hatch.envs.notebook]
6572
# installer = "uv" # https://github.com/astral-sh/uv/issues/6032

src/session_info2/_repr.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from __future__ import annotations
33

44
import json
5-
import traceback
5+
import warnings
66
from textwrap import dedent, indent
77
from types import MappingProxyType
88
from typing import TYPE_CHECKING, Any, Literal, TypeAlias
@@ -153,6 +153,9 @@ def repr_widget(si: SessionInfo) -> dict[str, str]:
153153
)
154154

155155

156+
DEFAULT_EXCLUDE = {"application/json"}
157+
158+
156159
def repr_mimebundle(
157160
si: SessionInfo,
158161
include: Container[str] | None = None,
@@ -170,8 +173,14 @@ def repr_mimebundle(
170173
continue
171174
if exclude is not None and mime in exclude:
172175
continue
176+
if mime in DEFAULT_EXCLUDE and (include is None or mime not in include):
177+
continue
173178
try:
174179
mb[mime] = repr_fn(si)
175-
except ImportError:
176-
traceback.print_exc()
180+
except ImportError as e:
181+
msg = (
182+
f"Failed to import dependencies for {mime} representation. "
183+
f"({type(e).__name__}: {e})"
184+
)
185+
warnings.warn(msg, RuntimeWarning, stacklevel=8)
177186
return mb

tests/test_subprocess.py

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
import json
77
import re
8-
from typing import TYPE_CHECKING, Any
8+
from importlib.util import find_spec
9+
from typing import TYPE_CHECKING, Any, Literal, TypedDict
910

1011
import pytest
1112
from jupyter_client.manager import start_new_async_kernel
@@ -18,7 +19,33 @@
1819

1920
from jupyter_client.asynchronous.client import AsyncKernelClient
2021

21-
Execute = Callable[[str], Awaitable[list[dict[str, str]]]]
22+
class Result(TypedDict): # noqa: D101
23+
msg_type: Literal["execute_result"]
24+
data: dict[str, str]
25+
metadata: dict[str, str]
26+
27+
class Display(TypedDict): # noqa: D101
28+
msg_type: Literal["display_data"]
29+
data: dict[str, str]
30+
metadata: dict[str, str]
31+
32+
class Stream(TypedDict): # noqa: D101
33+
msg_type: Literal["stream"]
34+
name: str
35+
text: str
36+
37+
SimpleMsg = Result | Display | Stream
38+
39+
Execute = Callable[[str], Awaitable[list["SimpleMsg"]]]
40+
41+
42+
HAS_IPYWIDGETS = bool(find_spec("ipywidgets"))
43+
44+
45+
def simple_msg(msg: dict[str, Any]) -> SimpleMsg | None:
46+
if msg["header"]["msg_type"] not in {"execute_result", "display_data", "stream"}:
47+
return None
48+
return dict(msg_type=msg["header"]["msg_type"], **msg["content"]) # type: ignore[return-value]
2249

2350

2451
@pytest.fixture(scope="session")
@@ -33,7 +60,7 @@ async def kernel_client() -> AsyncGenerator[AsyncKernelClient, None]:
3360
async def execute(
3461
kernel_client: AsyncKernelClient, libdir_test: Path
3562
) -> AsyncGenerator[Execute, None]:
36-
async def execute(code: str) -> list[dict[str, str]]:
63+
async def execute(code: str) -> list[SimpleMsg]:
3764
await kernel_client.wait_for_ready()
3865
msgs: list[dict[str, Any]] = []
3966
reply = await kernel_client.execute_interactive( # pyright: ignore[reportUnknownMemberType]
@@ -48,11 +75,7 @@ async def execute(code: str) -> list[dict[str, str]]:
4875
pytest.fail("{ename}: {evalue}\n{traceback}".format_map(content))
4976
elif reply["content"]["status"] != "ok":
5077
pytest.fail(f"Unexpected reply status: {reply['content']['status']}")
51-
return [
52-
msg["content"]["data"]
53-
for msg in msgs
54-
if msg["header"]["msg_type"] == "execute_result"
55-
]
78+
return [smsg for msg in msgs if (smsg := simple_msg(msg)) is not None]
5679

5780
await execute(
5881
f"""
@@ -100,15 +123,25 @@ async def execute(code: str) -> list[dict[str, str]]:
100123
)
101124
async def test_run(execute: Execute, code: str, expected: str) -> None:
102125
await execute(code)
103-
[mimebundle] = await execute(RUN)
104-
assert mimebundle.keys() == {
126+
[*msgs, result] = await execute(RUN)
127+
128+
if HAS_IPYWIDGETS:
129+
assert not msgs
130+
else:
131+
[msg] = msgs
132+
assert msg["msg_type"] == "stream"
133+
assert msg["name"] == "stderr"
134+
assert f"Failed to import dependencies for {MIME_WIDGET}" in msg["text"]
135+
assert "ModuleNotFoundError: No module named 'ipywidgets'" in msg["text"]
136+
137+
assert result["msg_type"] == "execute_result"
138+
assert set(result["data"].keys()) == {
105139
"text/plain",
106140
"text/markdown",
107141
"text/html",
108-
"application/json",
109-
MIME_WIDGET,
142+
*([MIME_WIDGET] if HAS_IPYWIDGETS else []),
110143
}
111-
r = mimebundle["text/plain"]
144+
r = result["data"]["text/plain"]
112145
pkgs, info = r.split("\n----\t----\n") if "----" in r else ("", r)
113146
assert pkgs == expected
114147
# No CPU info by default

0 commit comments

Comments
 (0)