Skip to content

Commit 52f85ba

Browse files
Assertion rewriting & failure reports (#36)
1 parent 200c68b commit 52f85ba

File tree

12 files changed

+683
-415
lines changed

12 files changed

+683
-415
lines changed

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,24 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## [0.3.0]
9+
10+
### Added
11+
12+
- Implemented assertion rewriting [#36][pr-36]
13+
14+
### Fixed
15+
16+
- Provide meaningful reports for failing test cases [#36][pr-36]
917

1018
### Changed
1119

20+
- Simplified xfail_for interface to take testname as keyword and reason as value [#36][pr-36]
1221
- Consolidated, simplified tests [#29][pr-29] & made them faster [#31][pr-31]
1322

1423
[pr-29]: https://github.com/MusicalNinjaDad/pytest-ipynb2/pull/29
1524
[pr-31]: https://github.com/MusicalNinjaDad/pytest-ipynb2/pull/31
25+
[pr-36]: https://github.com/MusicalNinjaDad/pytest-ipynb2/pull/36
1626

1727
## [0.2.1] - 2025-02-19
1828

__pyversion__

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.2.2
1+
0.3.0

justfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ check:
4949
@- just test
5050
@- just type-check
5151

52+
lint-main:
53+
uv run ruff check . --config=ruff-main.toml
54+
5255
# format and fix linting errors with ruff
5356
fix:
5457
- uv run ruff check . --fix

pyproject.toml

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
[build-system]
4040
requires = ["setuptools"]
41+
build-backend = "setuptools.build_meta"
4142

4243
[tool.setuptools.packages.find]
4344
where = ["."]
@@ -49,13 +50,13 @@
4950
[dependency-groups]
5051
lint = ["ruff"]
5152
typing = [
52-
{include-group = "test"},
53+
{ include-group = "test" },
5354
"pytype; python_version <= '3.12'",
5455
"typing_extensions", # required for Python3.10 and below
5556
]
5657
test = ["pytest-doctest-mkdocstrings"]
5758
cov = [
58-
{include-group = "test"},
59+
{ include-group = "test" },
5960
"pytest-cov",
6061
]
6162
doc = [
@@ -66,11 +67,11 @@
6667
"pymdown-extensions",
6768
]
6869
dev = [
69-
{include-group = "lint"},
70-
{include-group = "typing"},
71-
{include-group = "test"},
72-
{include-group = "cov"},
73-
{include-group = "doc"},
70+
{ include-group = "lint" },
71+
{ include-group = "typing" },
72+
{ include-group = "test" },
73+
{ include-group = "cov" },
74+
{ include-group = "doc" },
7475
"jupyter", # python's toml decoder won't accept a text entry at the start of the array, only at the end!!
7576
]
7677

@@ -90,11 +91,11 @@
9091
source = ["pytest_ipynb2"]
9192

9293
[tool.coverage.report]
93-
exclude_also = [
94-
"def __repr__",
95-
"if TYPE_CHECKING:",
96-
"except ImportError:",
97-
"@overload",
94+
exclude_also = [
95+
"def __repr__",
96+
"if TYPE_CHECKING:",
97+
"except ImportError:",
98+
"@overload",
9899
]
99100

100101
[tool.coverage.html]
@@ -144,7 +145,6 @@ exclude_also = [
144145
]
145146

146147
[tool.ruff.lint.per-file-ignores]
147-
# Additional ignores for tests
148148
"**/test_*.py" = [
149149
"INP001", # Missing __init__.py
150150
"ANN", # Missing type annotations
@@ -165,10 +165,6 @@ exclude_also = [
165165
"F401", # Unused imports are fine: using __init__.py to expose them with implicit __ALL__
166166
]
167167

168-
"tests/assets/expected.pyi" = [
169-
"ANN001", "ANN201", # Cannot automatically generate type annotations
170-
]
171-
172168
"**/*.ipynb" = [
173169
"T201", # Allow `print` in Jupyter notebooks
174170
"ANN", # Missing type annotations

pytest_ipynb2/_parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
class SourceList(list):
2020
"""
21-
A `list` with non-continuous indices for storing the contents of cells.
21+
A `list[str]` with non-continuous indices for storing the contents of cells.
2222
2323
- use a full slice `sourcelist[:]`, not list(sourcelist) to get contents.
2424
- supports `.ids()` analog to a mapping.keys(), yielding only cell-ids with source.

pytest_ipynb2/_pytester_helpers.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class _DummyNode:
128128
A dummy node for a `CollectionTree`, used by `CollectionTree.from_dict()`.
129129
130130
Compares equal to a genuine `pytest.Node` if:
131-
- `type(Node)` == `_DummyNode.nodetype` (strict, subclasses will not match)
131+
- `isinstance(Node,_DummyNode.nodetype)`
132132
- `repr(Node)` == `_DummyNode.name`.
133133
"""
134134

@@ -143,7 +143,7 @@ def __eq__(self, other: pytest.Item | pytest.Collector | Self) -> bool:
143143
sametype = self.nodetype == other.nodetype
144144
except AttributeError:
145145
samename = self.name == repr(other)
146-
sametype = self.nodetype is type(other)
146+
sametype = isinstance(other, self.nodetype)
147147
return samename and sametype
148148

149149
def __repr__(self) -> str:
@@ -269,6 +269,7 @@ def example_dir(
269269
nbformat.write(nb=nbnode, fp=pytester.path / f"{notebook}.ipynb")
270270
cached_dir = example_dir_cache[example] = ExampleDir(pytester=pytester)
271271
else:
272+
# 1st keyword is the test name (incl. any parametrized id)
272273
msg = f"Using cached {cached_dir.path} for {next(iter(request.keywords))}"
273274
warn(msg, stacklevel=1)
274275
return example_dir_cache[example]
@@ -279,15 +280,15 @@ def add_ipytest_magic(source: str) -> str:
279280
return f"%%ipytest\n\n{source}"
280281

281282

282-
def pytest_configure(config: pytest.Config) -> None: # pragma: no cover
283+
def pytest_configure(config: pytest.Config) -> None: # pragma: no cover
283284
# Tests will be needed if this ever becomes public functionality
284285
"""Register autoskip & xfail_for marks."""
285286
config.addinivalue_line("markers", "autoskip: automatically skip test if expected results not provided")
286287
config.addinivalue_line("markers", "xfail_for: xfail specified tests dynamically")
287288

288289

289290
@pytest.hookimpl(tryfirst=True)
290-
def pytest_runtest_setup(item: pytest.Function) -> None: # pragma: no cover
291+
def pytest_runtest_setup(item: pytest.Function) -> None: # pragma: no cover
291292
# Tests will be needed if this ever becomes public functionality
292293
if item.get_closest_marker("autoskip"):
293294
test_name = item.originalname.removeprefix("test_")
@@ -296,12 +297,12 @@ def pytest_runtest_setup(item: pytest.Function) -> None: # pragma: no cover
296297
item.add_marker(pytest.mark.skip(reason="No expected results"))
297298

298299

299-
def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: # pragma: no cover
300+
def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: # pragma: no cover
300301
# Tests will be needed if this ever becomes public functionality
301302
"""xfail on presence of a custom marker: `xfail_for(tests:list[str], reasons:list[str])`.""" # noqa: D403
302303
for item in items:
303304
test_name = item.originalname.removeprefix("test_")
304305
if xfail_for := item.get_closest_marker("xfail_for"):
305-
for xfail_test, reason in zip(xfail_for.kwargs.get("tests"), xfail_for.kwargs.get("reasons")):
306+
for xfail_test, reason in xfail_for.kwargs.items():
306307
if xfail_test == test_name:
307308
item.add_marker(pytest.mark.xfail(reason=reason, strict=True))

0 commit comments

Comments
 (0)