pytest-resources 0.2.0__tar.gz → 0.2.2__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 (36) hide show
  1. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/.gitignore +1 -1
  2. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/CHANGELOG.md +28 -1
  3. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/PKG-INFO +16 -4
  4. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/README.md +15 -3
  5. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/pyproject.toml +1 -1
  6. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/integration/test_synth_library.py +27 -4
  7. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/unit/test_formats.py +10 -0
  8. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/unit/test_nodes_synth.py +12 -0
  9. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/LICENSE +0 -0
  10. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/__init__.py +0 -0
  11. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/errors.py +0 -0
  12. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/formats.py +0 -0
  13. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/hooks.py +0 -0
  14. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/index.py +0 -0
  15. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/loaders.py +0 -0
  16. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/nodes.py +0 -0
  17. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/plugin.py +0 -0
  18. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/py.typed +0 -0
  19. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/src/pytest_resources/synth.py +0 -0
  20. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/integration/test_library.py +0 -0
  21. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/binary/blob.dat +0 -0
  22. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/config/app.ini +0 -0
  23. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/config/manifest.jsonc +0 -0
  24. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/config/settings.toml +0 -0
  25. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/data/movies.csv +0 -0
  26. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/structured/nested/deep.json +0 -0
  27. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/structured/sample.json +0 -0
  28. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/structured/sample2.json +0 -0
  29. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/unstructured/sample.md +0 -0
  30. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/resources/unstructured/sample2.md +0 -0
  31. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/unit/test_hook.py +0 -0
  32. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/unit/test_index.py +0 -0
  33. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/unit/test_init.py +0 -0
  34. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/unit/test_loaders.py +0 -0
  35. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/unit/test_plugin.py +0 -0
  36. {pytest_resources-0.2.0 → pytest_resources-0.2.2}/tests/unit/test_synth.py +0 -0
@@ -210,7 +210,7 @@ tempCodeRunnerFile.py
210
210
  .pypirc
211
211
 
212
212
  # Local release secrets — never commit
213
- gh_token
213
+ *_token
214
214
  *.token
215
215
 
216
216
  # Marimo
@@ -6,6 +6,31 @@ adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.2] - 2026-09-24
10
+
11
+ ### Changed
12
+
13
+ - Documentation: the synthesis surface (`make` / `batch` / `file`) is covered end to end,
14
+ with a new async-testing section (pure-CPU `make`/`batch`, `awalk` over adopted files,
15
+ off-loop `to_thread` for the network-backed `mp3`); the format matrix notes the document
16
+ and image kinds. The coverage floor is raised from 90% to 95% (current: 100%).
17
+
18
+ ### Added
19
+
20
+ - Tests for `TypedDict` model synthesis, the `batch` default size, the extra synthetic file
21
+ suffixes, and the synthesis provider's memoization.
22
+
23
+ No behavioural change to the packaged wheel.
24
+
25
+ ## [0.2.1] - 2026-09-24
26
+
27
+ ### Fixed
28
+
29
+ - The test suite is now cross-platform: file formats that `faker-file`/Pillow cannot
30
+ synthesize on Windows/macOS (missing system font, temporary-file locks) skip with a reason
31
+ instead of failing, and the synthetic-text assertion compares against the loader's raw
32
+ byte decode. No change to shipped behaviour.
33
+
9
34
  ## [0.2.0] - 2026-09-24
10
35
 
11
36
  ### Added
@@ -54,6 +79,8 @@ First release.
54
79
  - A module-lifetime decode cache per tree, and typed misses (`EntryNotFoundError` /
55
80
  `KeyError`) that carry a `difflib` "did you mean" hint.
56
81
 
57
- [unreleased]: https://github.com/damvolkov/pytest-resources/compare/v0.2.0...HEAD
82
+ [unreleased]: https://github.com/damvolkov/pytest-resources/compare/v0.2.2...HEAD
83
+ [0.2.2]: https://github.com/damvolkov/pytest-resources/releases/tag/v0.2.2
84
+ [0.2.1]: https://github.com/damvolkov/pytest-resources/releases/tag/v0.2.1
58
85
  [0.2.0]: https://github.com/damvolkov/pytest-resources/releases/tag/v0.2.0
59
86
  [0.1.0]: https://github.com/damvolkov/pytest-resources/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pytest-resources
3
- Version: 0.2.0
3
+ Version: 0.2.2
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
@@ -39,12 +39,21 @@ extensible to **any** canonical `bytes -> object` loader, and anything else hand
39
39
  raw `bytes`.
40
40
 
41
41
  You point it at one or more directories of sample files; it recursively indexes the tree
42
- without reading anything, and you reach, filter and randomise parsed values the way they
43
- read:
42
+ without reading anything, and you reach, filter, randomise and synthesize parsed values
43
+ the way they read:
44
44
 
45
45
  ```python
46
+ from dataclasses import dataclass
47
+
46
48
  import pytest_resources as pr
47
49
 
50
+
51
+ @dataclass
52
+ class User:
53
+ name: str
54
+ age: int
55
+
56
+
48
57
  def test_profile(resources: pr.Resources):
49
58
  user = resources.structured.user # sample dir -> dict (decoded, cached)
50
59
  body = resources.unstructured.intro # a .md -> str
@@ -69,7 +78,10 @@ uv add --group test "pytest-resources[random]" # both
69
78
  ```
70
79
 
71
80
  The `resources` fixture auto-loads via the `pytest11` entry point — no imports in your
72
- `conftest.py`.
81
+ `conftest.py`. Everything (index, navigation, synthesis) is async-test friendly:
82
+ `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.
73
85
 
74
86
  ## What you get
75
87
 
@@ -6,12 +6,21 @@ extensible to **any** canonical `bytes -> object` loader, and anything else hand
6
6
  raw `bytes`.
7
7
 
8
8
  You point it at one or more directories of sample files; it recursively indexes the tree
9
- without reading anything, and you reach, filter and randomise parsed values the way they
10
- read:
9
+ without reading anything, and you reach, filter, randomise and synthesize parsed values
10
+ the way they read:
11
11
 
12
12
  ```python
13
+ from dataclasses import dataclass
14
+
13
15
  import pytest_resources as pr
14
16
 
17
+
18
+ @dataclass
19
+ class User:
20
+ name: str
21
+ age: int
22
+
23
+
15
24
  def test_profile(resources: pr.Resources):
16
25
  user = resources.structured.user # sample dir -> dict (decoded, cached)
17
26
  body = resources.unstructured.intro # a .md -> str
@@ -36,7 +45,10 @@ uv add --group test "pytest-resources[random]" # both
36
45
  ```
37
46
 
38
47
  The `resources` fixture auto-loads via the `pytest11` entry point — no imports in your
39
- `conftest.py`.
48
+ `conftest.py`. Everything (index, navigation, synthesis) is async-test friendly:
49
+ `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.
40
52
 
41
53
  ## What you get
42
54
 
@@ -160,7 +160,7 @@ branch = true
160
160
  parallel = true
161
161
 
162
162
  [tool.coverage.report]
163
- fail_under = 90
163
+ fail_under = 95
164
164
  show_missing = true
165
165
  skip_covered = true
166
166
  exclude_also = ["if TYPE_CHECKING:", "@overload", "raise NotImplementedError", '^\s*\.\.\.$']
@@ -8,7 +8,7 @@ same loader table as any authored resource.
8
8
  from __future__ import annotations
9
9
 
10
10
  from dataclasses import dataclass
11
- from typing import TYPE_CHECKING, Any
11
+ from typing import TYPE_CHECKING, Any, TypedDict
12
12
 
13
13
  import attrs
14
14
  import msgspec
@@ -45,6 +45,11 @@ class Book:
45
45
  pages: int
46
46
 
47
47
 
48
+ class Row(TypedDict):
49
+ k: str
50
+ v: int
51
+
52
+
48
53
  @pytest.fixture
49
54
  def tree(tmp_path: Path) -> Resources:
50
55
  return pr.build_resources(tmp_path)
@@ -62,6 +67,18 @@ def test_make_autodetects_every_model_family(tree: Resources, model: type[Any])
62
67
  assert isinstance(tree.make(model), model)
63
68
 
64
69
 
70
+ def test_make_builds_a_typeddict_model(tree: Resources) -> None:
71
+ row = tree.make(Row)
72
+ assert isinstance(row, dict)
73
+ assert set(row) == {"k", "v"}
74
+ assert isinstance(row["k"], str)
75
+ assert isinstance(row["v"], int)
76
+
77
+
78
+ def test_batch_defaults_to_ten(tree: Resources) -> None:
79
+ assert len(tree.batch(User)) == 10
80
+
81
+
65
82
  def test_batch_returns_n_instances(tree: Resources) -> None:
66
83
  squad = tree.batch(User, 4)
67
84
  assert [type(item) for item in squad] == [User] * 4
@@ -120,7 +137,10 @@ def test_make_is_reproducible_under_a_seed(tree: Resources) -> None:
120
137
  )
121
138
  @pytest.mark.filterwarnings("ignore:imghdr was removed in Python 3.13:DeprecationWarning")
122
139
  def test_file_writes_a_real_non_empty_file(tree: Resources, kind: FileType) -> None:
123
- path = tree.file(kind)
140
+ try:
141
+ path = tree.file(kind)
142
+ except OSError as exc:
143
+ pytest.skip(f"faker-file cannot synthesize {kind.value} here: {exc}") # needs a system font / no temp lock
124
144
  assert path.exists()
125
145
  assert path.stat().st_size > 0
126
146
  assert pr.FileType.of_path(path) is kind
@@ -133,7 +153,7 @@ def test_file_resolves_a_suffix_from_a_filename(tree: Resources) -> None:
133
153
 
134
154
  def test_named_file_is_adopted_and_decoded_like_an_authored_one(tree: Resources) -> None:
135
155
  path = tree.file("txt", name="note")
136
- assert tree.note == path.read_text()
156
+ assert tree.note == path.read_bytes().decode() # the node value is exactly the loader's decode
137
157
  assert path in tree.paths("*.txt")
138
158
 
139
159
 
@@ -148,7 +168,10 @@ def test_csv_synthetic_stays_raw_bytes_until_a_loader_is_bound(tree: Resources)
148
168
 
149
169
 
150
170
  def test_choice_and_walk_see_synthetic_files(tree: Resources) -> None:
151
- path = tree.file("png", name="logo")
171
+ try:
172
+ path = tree.file("png", name="logo")
173
+ except OSError as exc:
174
+ pytest.skip(f"faker-file cannot synthesize png here: {exc}") # needs a system font
152
175
  assert tree.choice("*.png") == path.read_bytes()
153
176
  assert path in list(tree.walk("*.png"))
154
177
 
@@ -16,6 +16,16 @@ from pytest_resources import FileType
16
16
  (".cfg", FileType.INI),
17
17
  (".jsonl", FileType.NDJSON),
18
18
  (".md", FileType.MARKDOWN),
19
+ (".pdf", FileType.PDF),
20
+ (".docx", FileType.DOCX),
21
+ (".xlsx", FileType.XLSX),
22
+ (".jpg", FileType.JPEG),
23
+ (".jpeg", FileType.JPEG),
24
+ (".tif", FileType.TIFF),
25
+ (".tiff", FileType.TIFF),
26
+ (".webp", FileType.WEBP),
27
+ (".eml", FileType.EML),
28
+ (".zip", FileType.ZIP),
19
29
  ],
20
30
  )
21
31
  def test_filetype_of_known_suffix(suffix: str, expected: FileType) -> None:
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING
9
9
  import pytest
10
10
 
11
11
  from pytest_resources import build_resources
12
+ from pytest_resources.synth import SynthProvider
12
13
 
13
14
  if TYPE_CHECKING:
14
15
  from pytest_resources.nodes import Resources
@@ -50,6 +51,17 @@ def test_synth_module_stays_unloaded_until_first_use(tree: Resources) -> None:
50
51
  assert tree._synth is None
51
52
 
52
53
 
54
+ def test_synth_is_built_once_and_memoized(tree: Resources) -> None:
55
+ tree.make(int)
56
+ assert isinstance(tree._synth, SynthProvider)
57
+ assert tree._common_synth() is tree._synth
58
+
59
+
60
+ def test_assigned_synth_is_not_rebuilt(tree: Resources) -> None:
61
+ tree._synth = stub = _StubProvider()
62
+ assert tree._common_synth() is stub
63
+
64
+
53
65
  def test_make_forwards_spec_seed_and_fields(tree: Resources) -> None:
54
66
  tree._synth = stub = _StubProvider()
55
67
  assert tree.make(User, seed=5, name="ada") == "OBJECT"