Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8da1333
chore(deps): bump the actions group across 1 directory with 8 updates…
dependabot[bot] May 31, 2026
659c734
Merge branch 'main' of https://github.com/d-v-b/zarr-python
d-v-b Jun 6, 2026
51c994b
Merge branch 'main' of https://github.com/zarr-developers/zarr-python
d-v-b Jun 6, 2026
7732db3
Merge branch 'main' of https://github.com/zarr-developers/zarr-python
d-v-b Jun 7, 2026
913b41b
Merge branch 'main' of https://github.com/zarr-developers/zarr-python
d-v-b Jun 9, 2026
117b7ba
Merge branch 'main' of github.com:d-v-b/zarr-python
d-v-b Jun 12, 2026
d4de75d
Merge branch 'main' of github.com:zarr-developers/zarr-python
d-v-b Jun 12, 2026
86dabd5
Merge branch 'main' of github.com:zarr-developers/zarr-python
d-v-b Jun 12, 2026
a2e6002
Merge branch 'main' of https://github.com/d-v-b/zarr-python
d-v-b Jun 12, 2026
1621e1d
Merge branch 'main' of https://github.com/zarr-developers/zarr-python
d-v-b Jun 16, 2026
db473cd
Merge branch 'main' of https://github.com/zarr-developers/zarr-python
d-v-b Jun 25, 2026
dca5641
Merge branch 'main' of https://github.com/d-v-b/zarr-python
d-v-b Jun 30, 2026
a399213
Merge branch 'main' of https://github.com/zarr-developers/zarr-python
d-v-b Jul 1, 2026
d889d08
test: bind moto server to an ephemeral port to fix xdist deadlock
d-v-b Jul 10, 2026
a608a2c
doc: add changelog fragment for moto ephemeral-port fix
d-v-b Jul 10, 2026
7f7ae53
test: enable pytest strict mode and fix duplicate parametrization ids
d-v-b Jul 9, 2026
297e07c
doc: add changelog fragment for pytest strict mode
d-v-b Jul 10, 2026
2697fa5
test: arm faulthandler watchdog to turn deadlocks into loud failures
d-v-b Jul 10, 2026
0cfd68c
Merge branch 'main' into claude/pytest9-strict-stacked
d-v-b Jul 10, 2026
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
1 change: 1 addition & 0 deletions changes/215.misc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Enable pytest's `strict = true` config option (strict config, markers, xfail, and parametrization ids), replacing the `--strict-config`/`--strict-markers` addopts flags that pytest silently ignored before 9.1, and fix the seven duplicate parametrization ids it surfaced — including restoring float-JSON roundtrip cases that were meant to cover numpy scalars but ran plain-float cases twice instead. Also arm pytest's faulthandler watchdog (`faulthandler_timeout = 600` with `faulthandler_exit_on_timeout`) so a deadlocked test dumps every thread's traceback and fails the run instead of hanging indefinitely.
16 changes: 13 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -426,11 +426,21 @@ module = [
ignore_errors = true

[tool.pytest.ini_options]
minversion = "7"
minversion = "9"
testpaths = ["src", "tests", "docs/user-guide"]
log_cli_level = "INFO"
log_level = "INFO"
xfail_strict = true
# Enables strict_config, strict_markers, strict_xfail, strict_parametrization_ids, and
# any strictness options added in future pytest releases. Note that the equivalent
# `--strict-config`/`--strict-markers` flags were silently ignored when passed via
# addopts before pytest 9.1 (pytest#14442), so this option is the reliable spelling.
strict = true
# Turn deadlocks into loud failures: if a single test exceeds this many seconds, dump
# every thread's traceback and kill the run (exit_on_timeout is new in pytest 9). Sized
# far above the slowest legitimate test (~20s locally; slower under coverage/Windows/
# nightly stateful-hypothesis runs) so only a genuine hang can trip it.
faulthandler_timeout = 600
faulthandler_exit_on_timeout = true
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
doctest_optionflags = [
Expand All @@ -442,7 +452,7 @@ addopts = [
"--benchmark-columns", "min,mean,stddev,outliers,rounds,iterations",
"--benchmark-disable", # benchmark routines run as tests without benchmarking instrumentation
"--durations", "10",
"-ra", "--strict-config", "--strict-markers",
"-ra",
"--doctest-modules",
"--ignore=tests/test_regression/scripts",
"--ignore=src/zarr/_cli",
Expand Down
3 changes: 2 additions & 1 deletion tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -1693,7 +1693,8 @@ def test_default_endianness(
assert endianness_from_numpy_str(byte_order) == endianness # type: ignore[arg-type]


@pytest.mark.parametrize("value", [1, 1.4, "a", b"a", np.array(1)])
# The explicit id for b"a" avoids colliding with the auto-generated id for "a".
@pytest.mark.parametrize("value", [1, 1.4, "a", pytest.param(b"a", id="a-bytes"), np.array(1)])
@pytest.mark.parametrize("zarr_format", [2, 3])
@pytest.mark.filterwarnings("ignore::zarr.core.dtype.common.UnstableSpecificationWarning")
def test_scalar_array(value: Any, zarr_format: ZarrFormat) -> None:
Expand Down
6 changes: 4 additions & 2 deletions tests/test_codecs/test_vlen.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
from zarr.core.metadata.v3 import ArrayV3Metadata
from zarr.storage import StorePath

numpy_str_dtypes: list[type | str | None] = [
# The explicit id for the "str" literal avoids colliding with the auto-generated id for
# the `str` builtin.
numpy_str_dtypes: list[Any] = [
None,
str,
"str",
pytest.param("str", id="str-literal"),
np.dtypes.StrDType,
"S",
"U",
Expand Down
18 changes: 17 additions & 1 deletion tests/test_dtype/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Generate a collection of zdtype instances for use in testing.
import warnings
from collections import Counter
from typing import Any

import numpy as np
Expand Down Expand Up @@ -64,4 +65,19 @@ class TestB(TestExample):
for fixture_name in metafunc.fixturenames:
if hasattr(metafunc.cls, fixture_name):
params = getattr(metafunc.cls, fixture_name)
metafunc.parametrize(fixture_name, params, scope="class", ids=str)
metafunc.parametrize(
fixture_name, params, scope="class", ids=_unique_ids([str(p) for p in params])
)


def _unique_ids(ids: list[str]) -> list[str]:
"""Suffix repeated ids with their positional index so every id is unique.

Distinct parameters can stringify identically: for example `np.dtype("i")` and
`np.dtype("<i4")` both render as `int32` on little-endian platforms, but they are
deliberately distinct test cases. Pytest used to deduplicate colliding ids silently;
`strict_parametrization_ids` makes them a collection error, so we disambiguate
explicitly. Ids that are already unique are left untouched.
"""
counts = Counter(ids)
return [f"{id_}-{idx}" if counts[id_] > 1 else id_ for idx, id_ in enumerate(ids)]
19 changes: 11 additions & 8 deletions tests/test_dtype/test_npy/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@
from zarr.core.common import JSON, ZarrFormat


json_float_v2_roundtrip_cases: tuple[tuple[JSONFloatV2, float | np.floating[Any]], ...] = (
("Infinity", float("inf")),
("Infinity", np.inf),
("-Infinity", float("-inf")),
("-Infinity", -np.inf),
("NaN", float("nan")),
("NaN", np.nan),
(1.0, 1.0),
# Each special value is tested as both a Python float and a numpy scalar. The explicit
# ids are load-bearing: np.float64("inf") stringifies identically to float("inf"), so
# without them these parameter sets would produce duplicate test ids.
json_float_v2_roundtrip_cases: tuple[Any, ...] = (
pytest.param("Infinity", float("inf"), id="Infinity-float"),
pytest.param("Infinity", np.float64("inf"), id="Infinity-float64"),
pytest.param("-Infinity", float("-inf"), id="-Infinity-float"),
pytest.param("-Infinity", np.float64("-inf"), id="-Infinity-float64"),
pytest.param("NaN", float("nan"), id="NaN-float"),
pytest.param("NaN", np.float64("nan"), id="NaN-float64"),
pytest.param(1.0, 1.0, id="1.0-1.0"),
)

json_float_v3_cases = json_float_v2_roundtrip_cases
Expand Down
3 changes: 3 additions & 0 deletions tests/test_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -1713,6 +1713,9 @@ def test_create_nodes_concurrency_limit(store: MemoryStore) -> None:
(zarr.core.group.create_rooted_hierarchy, zarr.core.sync_group.create_rooted_hierarchy),
(zarr.core.group.get_node, zarr.core.sync_group.get_node),
],
# The default ids (from __name__) collide: the method pair and the module-level pair
# for create_hierarchy would both be id'd "create_hierarchy-create_hierarchy".
ids=lambda func: f"{func.__module__.rsplit('.', maxsplit=1)[-1]}.{func.__qualname__}",
)
def test_consistent_signatures(
a_func: Callable[[object], object], b_func: Callable[[object], object]
Expand Down
3 changes: 2 additions & 1 deletion tests/test_metadata/test_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def test_parse_zarr_format_valid() -> None:
assert parse_zarr_format(2) == 2


@pytest.mark.parametrize("data", [None, 1, 3, 4, 5, "3"])
# The explicit id for "3" avoids colliding with the auto-generated id for the int 3.
@pytest.mark.parametrize("data", [None, 1, 3, 4, 5, pytest.param("3", id="3-str")])
def test_parse_zarr_format_invalid(data: Any) -> None:
with pytest.raises(ValueError, match=f"Invalid value. Expected 2. Got {data}"):
parse_zarr_format(data)
Expand Down
Loading