pytest-resources 0.2.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/CHANGELOG.md +26 -1
  2. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/PKG-INFO +27 -12
  3. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/README.md +26 -11
  4. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/__init__.py +2 -1
  5. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/loaders.py +33 -18
  6. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/synth.py +1 -2
  7. pytest_resources-0.3.0/tests/unit/test_loaders.py +68 -0
  8. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/unit/test_plugin.py +1 -1
  9. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/unit/test_synth.py +1 -1
  10. pytest_resources-0.2.2/tests/unit/test_loaders.py +0 -59
  11. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/.gitignore +0 -0
  12. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/LICENSE +0 -0
  13. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/pyproject.toml +0 -0
  14. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/errors.py +0 -0
  15. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/formats.py +0 -0
  16. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/hooks.py +0 -0
  17. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/index.py +0 -0
  18. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/nodes.py +0 -0
  19. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/plugin.py +0 -0
  20. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/src/pytest_resources/py.typed +0 -0
  21. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/integration/test_library.py +0 -0
  22. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/integration/test_synth_library.py +0 -0
  23. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/binary/blob.dat +0 -0
  24. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/config/app.ini +0 -0
  25. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/config/manifest.jsonc +0 -0
  26. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/config/settings.toml +0 -0
  27. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/data/movies.csv +0 -0
  28. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/structured/nested/deep.json +0 -0
  29. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/structured/sample.json +0 -0
  30. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/structured/sample2.json +0 -0
  31. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/unstructured/sample.md +0 -0
  32. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/resources/unstructured/sample2.md +0 -0
  33. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/unit/test_formats.py +0 -0
  34. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/unit/test_hook.py +0 -0
  35. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/unit/test_index.py +0 -0
  36. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/unit/test_init.py +0 -0
  37. {pytest_resources-0.2.2 → pytest_resources-0.3.0}/tests/unit/test_nodes_synth.py +0 -0
@@ -6,6 +6,29 @@ adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.0] - 2026-09-25
10
+
11
+ ### Changed
12
+
13
+ - **The default loader is now the Python standard library** (`json` + `tomllib`): the core
14
+ ships with zero third-party dependencies. e-serde is no longer auto-selected when present,
15
+ so `JSONC` / `YAML` / `INI` decode to raw `bytes` unless you opt in.
16
+ - The `[serde]` extra is now an explicit opt-in on equal footing with `[objects]` /
17
+ `[files]`, documented as the recommended fast, multi-format official loader.
18
+
19
+ ### Added
20
+
21
+ - `eserde_loaders()` (exported): the e-serde codec table. Register it from `conftest.py`
22
+ via `pytest_resource_loaders` to make e-serde the project's loader; it raises
23
+ `ExtraNotInstalledError` (naming the `[serde]` extra) when the extra is absent.
24
+
25
+ ## [0.2.3] - 2026-09-25
26
+
27
+ ### Fixed
28
+
29
+ - `ExtraNotInstalledError` now emits a single **uv-only** install hint (dropped the
30
+ redundant `pip` line), matching the project's toolchain and the docs.
31
+
9
32
  ## [0.2.2] - 2026-09-24
10
33
 
11
34
  ### Changed
@@ -79,7 +102,9 @@ First release.
79
102
  - A module-lifetime decode cache per tree, and typed misses (`EntryNotFoundError` /
80
103
  `KeyError`) that carry a `difflib` "did you mean" hint.
81
104
 
82
- [unreleased]: https://github.com/damvolkov/pytest-resources/compare/v0.2.2...HEAD
105
+ [unreleased]: https://github.com/damvolkov/pytest-resources/compare/v0.3.0...HEAD
106
+ [0.3.0]: https://github.com/damvolkov/pytest-resources/releases/tag/v0.3.0
107
+ [0.2.3]: https://github.com/damvolkov/pytest-resources/releases/tag/v0.2.3
83
108
  [0.2.2]: https://github.com/damvolkov/pytest-resources/releases/tag/v0.2.2
84
109
  [0.2.1]: https://github.com/damvolkov/pytest-resources/releases/tag/v0.2.1
85
110
  [0.2.0]: https://github.com/damvolkov/pytest-resources/releases/tag/v0.2.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pytest-resources
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: Lazy, typed, attribute-navigable fixtures for test resource files, with pluggable canonical loaders (e-serde by default).
5
5
  Project-URL: Homepage, https://github.com/damvolkov/pytest-resources
6
6
  Project-URL: Source, https://github.com/damvolkov/pytest-resources
@@ -33,10 +33,11 @@ Description-Content-Type: text/markdown
33
33
 
34
34
  # pytest-resources
35
35
 
36
- Lazy, typed, attribute-navigable fixtures for test resource files — JSON / JSONC / YAML /
37
- TOML / INI decoded through [`e-serde`](https://pypi.org/project/e-serde/) by default,
38
- extensible to **any** canonical `bytes -> object` loader, and anything else handed back as
39
- raw `bytes`.
36
+ Lazy, typed, attribute-navigable fixtures for test resource files — decoded through the
37
+ **Python standard library** by default (`json` + `tomllib`, zero extra dependencies),
38
+ extensible to **any** canonical `bytes -> object` loader. The optional
39
+ [`e-serde`](https://pypi.org/project/e-serde/) extra is **recommended** as the fast,
40
+ multi-format official loader; anything without a parser is handed back as raw `bytes`.
40
41
 
41
42
  You point it at one or more directories of sample files; it recursively indexes the tree
42
43
  without reading anything, and you reach, filter, randomise and synthesize parsed values
@@ -70,18 +71,18 @@ def test_profile(resources: pr.Resources):
70
71
  ## Install
71
72
 
72
73
  ```bash
73
- uv add --group test "pytest-resources[serde]" # e-serde default codecs
74
- uv add --group test pytest-resources # stdlib-only (json + tomllib)
74
+ uv add --group test pytest-resources # default: stdlib (json + tomllib), zero extra deps
75
+ uv add --group test "pytest-resources[serde]" # e-serde — fast, multi-format loader (recommended)
75
76
  uv add --group test "pytest-resources[objects]" # resources.make()/batch(): random model objects
76
77
  uv add --group test "pytest-resources[files]" # resources.file(): synthetic files, 25 formats
77
- uv add --group test "pytest-resources[random]" # both
78
+ uv add --group test "pytest-resources[random]" # both synthesis backends
78
79
  ```
79
80
 
80
81
  The `resources` fixture auto-loads via the `pytest11` entry point — no imports in your
81
82
  `conftest.py`. Everything (index, navigation, synthesis) is async-test friendly:
82
83
  `make`/`batch` are pure in-memory CPU and `abuild_resources`/`awalk` keep the loop free;
83
- calling a synthesis method without its extra raises `ExtraNotInstalledError` with the
84
- exact install command — the plugin never hard-depends on polyfactory or faker-file.
84
+ a missing extra raises `ExtraNotInstalledError` with the exact install command — the plugin
85
+ never hard-depends on e-serde, polyfactory or faker-file.
85
86
 
86
87
  ## What you get
87
88
 
@@ -97,7 +98,7 @@ exact install command — the plugin never hard-depends on polyfactory or faker-
97
98
  | **Open-ended fallback** | `CSV`/`TSV`/`PDF`/… and any unknown kind stay raw `bytes` |
98
99
  | **Many roots, one tree** | CLI / ini / `pytest_resources_roots` hook, merged (later wins), auto-created |
99
100
  | **Pluggable codecs** | override or add any kind through the `pytest_resource_loaders` hook |
100
- | **e-serde default, optional** | native Rust/C config codecs when installed; stdlib otherwise |
101
+ | **Stdlib default, e-serde opt-in** | `json`/`tomllib` by default; register `eserde_loaders()` for the fast multi-format backend |
101
102
  | **Friendly misses** | `EntryNotFoundError` / `KeyError` carry a "did you mean" hint |
102
103
 
103
104
  ## Configuring the roots
@@ -139,6 +140,19 @@ def pytest_resource_loaders(register):
139
140
  `register` merges into the session's table, so you can override a default kind as easily
140
141
  as add an unknown one. Kinds left unbound hand back **raw bytes**.
141
142
 
143
+ ### The recommended official loader: e-serde
144
+
145
+ The default is the standard library. Install the `[serde]` extra and register one call to
146
+ make [`e-serde`](https://pypi.org/project/e-serde/) your official loader — ultrarapid,
147
+ native Rust/C, and it decodes JSONC/YAML/INI too:
148
+
149
+ ```python
150
+ from pytest_resources import eserde_loaders
151
+
152
+ def pytest_resource_loaders(register):
153
+ register(eserde_loaders())
154
+ ```
155
+
142
156
  ## Programmatic use
143
157
 
144
158
  Outside pytest, build a tree yourself:
@@ -150,7 +164,8 @@ import pytest_resources as pr
150
164
  tree = pr.build_resources(Path("fixtures")) # sync, one root
151
165
  tree = pr.build_resources([Path("a"), Path("b")]) # merged roots, later wins
152
166
  tree = await pr.abuild_resources(Path("fixtures")) # off the event loop
153
- table = pr.default_loaders() # best available codecs
167
+ table = pr.default_loaders() # stdlib codecs (json + tomllib)
168
+ tree = pr.build_resources(Path("fixtures"), pr.eserde_loaders()) # opt into e-serde instead
154
169
  ```
155
170
 
156
171
  ## Development
@@ -1,9 +1,10 @@
1
1
  # pytest-resources
2
2
 
3
- Lazy, typed, attribute-navigable fixtures for test resource files — JSON / JSONC / YAML /
4
- TOML / INI decoded through [`e-serde`](https://pypi.org/project/e-serde/) by default,
5
- extensible to **any** canonical `bytes -> object` loader, and anything else handed back as
6
- raw `bytes`.
3
+ Lazy, typed, attribute-navigable fixtures for test resource files — decoded through the
4
+ **Python standard library** by default (`json` + `tomllib`, zero extra dependencies),
5
+ extensible to **any** canonical `bytes -> object` loader. The optional
6
+ [`e-serde`](https://pypi.org/project/e-serde/) extra is **recommended** as the fast,
7
+ multi-format official loader; anything without a parser is handed back as raw `bytes`.
7
8
 
8
9
  You point it at one or more directories of sample files; it recursively indexes the tree
9
10
  without reading anything, and you reach, filter, randomise and synthesize parsed values
@@ -37,18 +38,18 @@ def test_profile(resources: pr.Resources):
37
38
  ## Install
38
39
 
39
40
  ```bash
40
- uv add --group test "pytest-resources[serde]" # e-serde default codecs
41
- uv add --group test pytest-resources # stdlib-only (json + tomllib)
41
+ uv add --group test pytest-resources # default: stdlib (json + tomllib), zero extra deps
42
+ uv add --group test "pytest-resources[serde]" # e-serde — fast, multi-format loader (recommended)
42
43
  uv add --group test "pytest-resources[objects]" # resources.make()/batch(): random model objects
43
44
  uv add --group test "pytest-resources[files]" # resources.file(): synthetic files, 25 formats
44
- uv add --group test "pytest-resources[random]" # both
45
+ uv add --group test "pytest-resources[random]" # both synthesis backends
45
46
  ```
46
47
 
47
48
  The `resources` fixture auto-loads via the `pytest11` entry point — no imports in your
48
49
  `conftest.py`. Everything (index, navigation, synthesis) is async-test friendly:
49
50
  `make`/`batch` are pure in-memory CPU and `abuild_resources`/`awalk` keep the loop free;
50
- calling a synthesis method without its extra raises `ExtraNotInstalledError` with the
51
- exact install command — the plugin never hard-depends on polyfactory or faker-file.
51
+ a missing extra raises `ExtraNotInstalledError` with the exact install command — the plugin
52
+ never hard-depends on e-serde, polyfactory or faker-file.
52
53
 
53
54
  ## What you get
54
55
 
@@ -64,7 +65,7 @@ exact install command — the plugin never hard-depends on polyfactory or faker-
64
65
  | **Open-ended fallback** | `CSV`/`TSV`/`PDF`/… and any unknown kind stay raw `bytes` |
65
66
  | **Many roots, one tree** | CLI / ini / `pytest_resources_roots` hook, merged (later wins), auto-created |
66
67
  | **Pluggable codecs** | override or add any kind through the `pytest_resource_loaders` hook |
67
- | **e-serde default, optional** | native Rust/C config codecs when installed; stdlib otherwise |
68
+ | **Stdlib default, e-serde opt-in** | `json`/`tomllib` by default; register `eserde_loaders()` for the fast multi-format backend |
68
69
  | **Friendly misses** | `EntryNotFoundError` / `KeyError` carry a "did you mean" hint |
69
70
 
70
71
  ## Configuring the roots
@@ -106,6 +107,19 @@ def pytest_resource_loaders(register):
106
107
  `register` merges into the session's table, so you can override a default kind as easily
107
108
  as add an unknown one. Kinds left unbound hand back **raw bytes**.
108
109
 
110
+ ### The recommended official loader: e-serde
111
+
112
+ The default is the standard library. Install the `[serde]` extra and register one call to
113
+ make [`e-serde`](https://pypi.org/project/e-serde/) your official loader — ultrarapid,
114
+ native Rust/C, and it decodes JSONC/YAML/INI too:
115
+
116
+ ```python
117
+ from pytest_resources import eserde_loaders
118
+
119
+ def pytest_resource_loaders(register):
120
+ register(eserde_loaders())
121
+ ```
122
+
109
123
  ## Programmatic use
110
124
 
111
125
  Outside pytest, build a tree yourself:
@@ -117,7 +131,8 @@ import pytest_resources as pr
117
131
  tree = pr.build_resources(Path("fixtures")) # sync, one root
118
132
  tree = pr.build_resources([Path("a"), Path("b")]) # merged roots, later wins
119
133
  tree = await pr.abuild_resources(Path("fixtures")) # off the event loop
120
- table = pr.default_loaders() # best available codecs
134
+ table = pr.default_loaders() # stdlib codecs (json + tomllib)
135
+ tree = pr.build_resources(Path("fixtures"), pr.eserde_loaders()) # opt into e-serde instead
121
136
  ```
122
137
 
123
138
  ## Development
@@ -14,7 +14,7 @@ from importlib.metadata import PackageNotFoundError, version
14
14
  from pytest_resources.errors import EntryNotFoundError, ExtraNotInstalledError, ResourceError
15
15
  from pytest_resources.formats import FileType
16
16
  from pytest_resources.index import abuild_resources, build_resources
17
- from pytest_resources.loaders import Loader, default_loaders
17
+ from pytest_resources.loaders import Loader, default_loaders, eserde_loaders
18
18
  from pytest_resources.nodes import ResourceNode, Resources
19
19
 
20
20
  try:
@@ -34,4 +34,5 @@ __all__ = [
34
34
  "abuild_resources",
35
35
  "build_resources",
36
36
  "default_loaders",
37
+ "eserde_loaders",
37
38
  ]
@@ -1,9 +1,10 @@
1
- """pytest_resources.loaders: pluggable decoders, e-serde preferred, stdlib fallback.
1
+ """pytest_resources.loaders: pluggable decoders, stdlib by default, e-serde opt-in.
2
2
 
3
- The system accepts any canonical ``bytes -> object`` loader. ``default_loaders``
4
- returns the best table available: e-serde's unified native codecs when installed,
5
- otherwise the standard library (json + tomllib). Kinds with no parser hand back raw
6
- bytes for the caller to decode.
3
+ The system accepts any canonical ``bytes -> object`` loader. ``default_loaders()``
4
+ returns the standard-library table (``json`` + ``tomllib``) — the light, dependency-free
5
+ default. [`e-serde`](https://pypi.org/project/e-serde/) is an optional ``[serde]`` extra:
6
+ call :func:`eserde_loaders` and register it to swap in the fast, multi-format backend.
7
+ Kinds with no parser hand back raw bytes for the caller to decode.
7
8
  """
8
9
 
9
10
  from __future__ import annotations
@@ -14,6 +15,7 @@ from functools import partial
14
15
  from tomllib import loads as _toml_loads
15
16
  from typing import Any
16
17
 
18
+ from pytest_resources.errors import ExtraNotInstalledError
17
19
  from pytest_resources.formats import FileType
18
20
 
19
21
  Loader = Callable[[bytes], Any]
@@ -39,10 +41,33 @@ def _lines(loader: Loader, data: bytes) -> list[Any]:
39
41
  return [loader(line) for line in data.splitlines() if line.strip()]
40
42
 
41
43
 
42
- def _eserde_loaders() -> dict[FileType, Loader]:
43
- """Config-codec table backed by e-serde (native Rust/C decoders, native dicts out)."""
44
- from eserde import Format, loads # noqa: PLC0415 -- optional fast path, not a core dep
44
+ def default_loaders() -> dict[FileType, Loader]:
45
+ """The default codec table — the standard library only; kinds without one stay bytes."""
46
+ table: dict[FileType, Loader] = dict.fromkeys(FileType, _raw)
47
+ table |= _stdlib_loaders()
48
+ return table
49
+
50
+
51
+ def eserde_loaders() -> dict[FileType, Loader]:
52
+ """The recommended fast table backed by e-serde — needs the optional `[serde]` extra.
53
+
54
+ Register it from a ``conftest.py`` to make e-serde the project's official loader::
55
+
56
+ from pytest_resources import eserde_loaders
57
+
45
58
 
59
+ def pytest_resource_loaders(register):
60
+ register(eserde_loaders())
61
+ """
62
+ try:
63
+ from eserde import Format, loads # noqa: PLC0415 -- optional fast path, not a core dep
64
+ except ImportError as exc:
65
+ msg = (
66
+ "`eserde_loaders()` requires the 'serde' extra.\n"
67
+ 'Install it with: uv add --group test "pytest-resources[serde]"\n'
68
+ "Underlying import failed: eserde"
69
+ )
70
+ raise ExtraNotInstalledError(msg) from exc
46
71
  json_row = partial(loads, format=Format.JSON)
47
72
  return {
48
73
  FileType.JSON: json_row,
@@ -65,13 +90,3 @@ def _stdlib_loaders() -> dict[FileType, Loader]:
65
90
  FileType.MARKDOWN: _text,
66
91
  FileType.TEXT: _text,
67
92
  }
68
-
69
-
70
- def default_loaders() -> dict[FileType, Loader]:
71
- """A fresh codec table: e-serde when importable, else the stdlib; other kinds stay bytes."""
72
- table: dict[FileType, Loader] = dict.fromkeys(FileType, _raw)
73
- try:
74
- table |= _eserde_loaders()
75
- except ImportError:
76
- table |= _stdlib_loaders()
77
- return table
@@ -116,8 +116,7 @@ class SynthProvider:
116
116
  return
117
117
  msg = (
118
118
  f"`Resources.{method}()` requires the '{extra}' extra.\n"
119
- f'Install it with: uv add --group test "pytest-resources[{extra}]" '
120
- f'(pip: pip install "pytest-resources[{extra}]")\n'
119
+ f'Install it with: uv add --group test "pytest-resources[{extra}]"\n'
121
120
  f"Underlying import failed: {module}"
122
121
  )
123
122
  raise ExtraNotInstalledError(msg)
@@ -0,0 +1,68 @@
1
+ """tests/unit: the loader tables — the stdlib default and the opt-in e-serde table."""
2
+
3
+ import json
4
+ import sys
5
+
6
+ import pytest
7
+
8
+ from pytest_resources import FileType
9
+ from pytest_resources.errors import ExtraNotInstalledError
10
+ from pytest_resources.loaders import _lines, _raw, _text, default_loaders, eserde_loaders
11
+
12
+
13
+ def test_default_loaders_covers_every_kind() -> None:
14
+ assert set(default_loaders()) == set(FileType)
15
+
16
+
17
+ def test_default_loaders_is_stdlib() -> None:
18
+ table = default_loaders()
19
+ assert table[FileType.JSON](b'{"a": 1, "b": [1, 2]}') == {"a": 1, "b": [1, 2]}
20
+ assert table[FileType.TOML](b"a = 1\n") == {"a": 1}
21
+ assert table[FileType.NDJSON](b'{"i": 1}\n\n{"i": 2}') == [{"i": 1}, {"i": 2}]
22
+ assert table[FileType.MARKDOWN](b"# t\n") == "# t\n"
23
+
24
+
25
+ @pytest.mark.parametrize("fmt", [FileType.CSV, FileType.YAML, FileType.INI, FileType.JSONC, FileType.HTML])
26
+ def test_default_loaders_leaves_the_rest_as_bytes(fmt: FileType) -> None:
27
+ assert default_loaders()[fmt](b"raw") == b"raw" # no stdlib codec -> honest bytes
28
+
29
+
30
+ def test_default_loaders_does_not_pick_eserde() -> None:
31
+ pytest.importorskip("eserde") # e-serde is optional: the default must ignore it, even when installed
32
+ assert default_loaders()[FileType.JSONC](b'{"a": 1}') == b'{"a": 1}'
33
+
34
+
35
+ @pytest.mark.parametrize(
36
+ ("fmt", "blob", "want"),
37
+ [
38
+ (FileType.JSON, b'{"a": 1}', {"a": 1}),
39
+ (FileType.JSONC, b'{"a": 1 /* c */}', {"a": 1}),
40
+ (FileType.INI, b"[s]\nk = v\n", {"s": {"k": "v"}}),
41
+ (FileType.TOML, b"a = 1\n", {"a": 1}),
42
+ (FileType.YAML, b"a: 1\n", {"a": 1}),
43
+ ],
44
+ )
45
+ def test_eserde_loaders_decodes_config_kinds(fmt: FileType, blob: bytes, want: dict) -> None:
46
+ pytest.importorskip("eserde")
47
+ assert eserde_loaders()[fmt](blob) == want
48
+
49
+
50
+ def test_eserde_loaders_requires_the_extra(monkeypatch: pytest.MonkeyPatch) -> None:
51
+ monkeypatch.setitem(sys.modules, "eserde", None) # forces the ImportError path
52
+ with pytest.raises(ExtraNotInstalledError) as exc:
53
+ eserde_loaders()
54
+ assert "pytest-resources[serde]" in str(exc.value)
55
+ assert "uv add" in str(exc.value)
56
+
57
+
58
+ def test_eserde_loaders_is_importable_without_calling_it() -> None:
59
+ assert callable(eserde_loaders) # importing loaders must not import e-serde eagerly
60
+
61
+
62
+ def test_lines_splits_and_decodes_per_line() -> None:
63
+ assert _lines(json.loads, b'{"i": 1}\n\n{"i": 2}\n') == [{"i": 1}, {"i": 2}]
64
+
65
+
66
+ def test_text_and_raw_helpers() -> None:
67
+ assert _text("café".encode()) == "café"
68
+ assert _raw(b"x") == b"x"
@@ -35,7 +35,7 @@ def test_resources_fixture_is_indexed_tree(resources: Resources) -> None:
35
35
  assert isinstance(resources, pr.Resources)
36
36
  assert resources.structured.sample["id"] == "sample1"
37
37
  assert resources.config.settings["owner"]["name"] == "damien"
38
- assert resources.config.manifest == {"ok": True, "retries": 3}
38
+ assert isinstance(resources.config.manifest, bytes) # JSONC has no stdlib codec -> bytes by default
39
39
  assert isinstance(resources.unstructured.sample, str)
40
40
 
41
41
 
@@ -39,7 +39,7 @@ def test_synth_file_without_files_explains_the_extra(monkeypatch: pytest.MonkeyP
39
39
  provider.file(FileType.PDF)
40
40
  msg = str(excinfo.value)
41
41
  assert "pytest-resources[files]" in msg
42
- assert "pip install" in msg
42
+ assert "uv add" in msg
43
43
  assert "faker-file" in msg
44
44
  assert "file" in msg
45
45
 
@@ -1,59 +0,0 @@
1
- """tests/unit: the pluggable loader table and its e-serde / stdlib backends."""
2
-
3
- import json
4
- import sys
5
-
6
- import pytest
7
-
8
- from pytest_resources import FileType
9
- from pytest_resources.loaders import _lines, _raw, _text, default_loaders
10
-
11
-
12
- def test_default_loaders_covers_every_kind() -> None:
13
- assert set(default_loaders()) == set(FileType)
14
-
15
-
16
- def test_json_kind_decodes_to_native_dict() -> None:
17
- assert default_loaders()[FileType.JSON](b'{"a": 1, "b": [1, 2]}') == {"a": 1, "b": [1, 2]}
18
-
19
-
20
- def test_text_and_markdown_decode_to_str() -> None:
21
- table = default_loaders()
22
- assert table[FileType.TEXT](b"hi") == "hi"
23
- assert isinstance(table[FileType.MARKDOWN](b"# t\n"), str)
24
-
25
-
26
- def test_unbound_kind_hands_back_raw_bytes() -> None:
27
- assert default_loaders()[FileType.CSV](b"a,b") == b"a,b"
28
- assert _raw(b"x") == b"x"
29
-
30
-
31
- def test_lines_splits_and_decodes_per_line() -> None:
32
- assert _lines(json.loads, b'{"i": 1}\n\n{"i": 2}\n') == [{"i": 1}, {"i": 2}]
33
-
34
-
35
- def test_text_helper_decodes_utf8() -> None:
36
- assert _text("café".encode()) == "café"
37
-
38
-
39
- def test_stdlib_fallback_when_eserde_absent(monkeypatch: pytest.MonkeyPatch) -> None:
40
- monkeypatch.setitem(sys.modules, "eserde", None) # forces ImportError inside default_loaders
41
- table = default_loaders()
42
- assert table[FileType.JSON](b'{"a": 1}') == {"a": 1}
43
- assert table[FileType.TOML](b"a = 1\n") == {"a": 1}
44
- assert table[FileType.NDJSON](b'{"i": 1}\n{"i": 2}') == [{"i": 1}, {"i": 2}]
45
- assert table[FileType.INI](b"[s]\nk=v") == b"[s]\nk=v" # not a stdlib codec -> bytes
46
-
47
-
48
- @pytest.mark.parametrize(
49
- ("fmt", "blob", "want"),
50
- [
51
- (FileType.JSONC, b'{"a": 1 /* c */}', {"a": 1}),
52
- (FileType.INI, b"[s]\nk = v\n", {"s": {"k": "v"}}),
53
- (FileType.TOML, b"a = 1\n", {"a": 1}),
54
- (FileType.YAML, b"a: 1\n", {"a": 1}),
55
- ],
56
- )
57
- def test_eserde_native_config_kinds(fmt: FileType, blob: bytes, want: dict) -> None:
58
- pytest.importorskip("eserde")
59
- assert default_loaders()[fmt](blob) == want