Skip to content

Commit eb03250

Browse files
committed
feat: add --snapshot-ignore-file-extensions argument to support DVC
1 parent f8cb4d2 commit eb03250

File tree

8 files changed

+105
-7
lines changed

8 files changed

+105
-7
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ These are the cli options exposed to `pytest` by the plugin.
141141
| `--snapshot-no-colors` | Disable test results output highlighting. Equivalent to setting the environment variables `ANSI_COLORS_DISABLED` or `NO_COLOR` | Disabled by default if not in terminal. |
142142
| `--snapshot-patch-pycharm-diff`| Override PyCharm's default diffs viewer when looking at snapshot diffs. See [IDE Integrations](#ide-integrations) | `False` |
143143
| `--snapshot-diff-mode` | Configures how diffs are displayed on assertion failure. If working with very large snapshots, disabling the diff can improve performance. | `detailed` |
144+
| `--snapshot-ignore-file-extensions` | Comma separated list of file extensions to ignore when walking the file tree and discovering used/unused snapshots. | No extensions are ignored by default. |
144145

145146
### Assertion Options
146147

@@ -462,6 +463,22 @@ The generated snapshot:
462463
}
463464
```
464465

466+
#### Ignoring File Extensions (e.g. DVC Integration)
467+
468+
If using a tool such as [DVC](https://dvc.org/) or other tool where you need to ignore files by file extension, you can update your `pytest.ini` like so:
469+
470+
```ini
471+
[pytest]
472+
addopts = --snapshot-ignore-file-extensions dvc
473+
```
474+
475+
A comma separated list is supported, like so:
476+
477+
```ini
478+
[pytest]
479+
addopts = --snapshot-ignore-file-extensions dvc,tmp,zip
480+
```
481+
465482
### Extending Syrupy
466483

467484
- [Custom defaults](https://github.com/syrupy-project/syrupy/tree/main/tests/examples/test_custom_defaults.py)

src/syrupy/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ def pytest_addoption(parser: "pytest.Parser") -> None:
102102
dest="diff_mode",
103103
help="Controls how diffs are represented on snapshot assertion failure",
104104
)
105+
group.addoption(
106+
"--snapshot-ignore-file-extensions",
107+
dest="ignore_file_extensions",
108+
help="Comma separated list of file extensions to ignore when discovering snapshots",
109+
type=lambda v: v.split(","),
110+
)
105111

106112

107113
def __terminal_color(config: "pytest.Config") -> "ContextManager[None]":
@@ -143,7 +149,10 @@ def pytest_sessionstart(session: Any) -> None:
143149
Initialize snapshot session before tests are collected and ran.
144150
https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_sessionstart
145151
"""
146-
session.config._syrupy = SnapshotSession(pytest_session=session)
152+
session.config._syrupy = SnapshotSession(
153+
pytest_session=session,
154+
ignore_file_extensions=session.config.option.ignore_file_extensions,
155+
)
147156
global _syrupy
148157
_syrupy = session.config._syrupy
149158
session.config._syrupy.start()

src/syrupy/extensions/base.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,16 @@ def is_snapshot_location(self, *, location: str) -> bool:
108108
return location.endswith(self._file_extension)
109109

110110
def discover_snapshots(
111-
self, *, test_location: "PyTestLocation"
111+
self, *, test_location: "PyTestLocation", ignore_extensions: list[str] | None
112112
) -> "SnapshotCollections":
113113
"""
114114
Returns all snapshot collections in test site
115115
"""
116116
discovered = SnapshotCollections()
117-
for filepath in walk_snapshot_dir(self.dirname(test_location=test_location)):
117+
for filepath in walk_snapshot_dir(
118+
self.dirname(test_location=test_location),
119+
ignore_extensions=ignore_extensions,
120+
):
118121
if self.is_snapshot_location(location=filepath):
119122
snapshot_collection = self._read_snapshot_collection(
120123
snapshot_location=filepath

src/syrupy/report.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ def __post_init__(self) -> None:
112112
locations_discovered[test_location].add(extension_class)
113113
self.discovered.merge(
114114
assertion.extension.discover_snapshots(
115-
test_location=assertion.test_location
115+
test_location=assertion.test_location,
116+
ignore_extensions=assertion.session.ignore_file_extensions,
116117
)
117118
)
118119

src/syrupy/session.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ class ItemStatus(Enum):
5353
@dataclass
5454
class SnapshotSession:
5555
pytest_session: "pytest.Session"
56+
57+
# List of file extensions to ignore during discovery/processing
58+
ignore_file_extensions: list[str] | None = None
59+
5660
# Snapshot report generated on finish
5761
report: Optional["SnapshotReport"] = None
5862
# All the collected test items
@@ -212,7 +216,8 @@ def register_request(self, assertion: "SnapshotAssertion") -> None:
212216
discovered_extensions = {
213217
discovered.location: assertion.extension
214218
for discovered in assertion.extension.discover_snapshots(
215-
test_location=assertion.test_location
219+
test_location=assertion.test_location,
220+
ignore_extensions=self.ignore_file_extensions,
216221
)
217222
if discovered.has_snapshots
218223
}

src/syrupy/utils.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,15 @@ def is_xdist_controller() -> bool:
3030
return bool(worker_count and int(worker_count) > 0 and not is_xdist_worker())
3131

3232

33-
def walk_snapshot_dir(root: str) -> Iterator[str]:
33+
def walk_snapshot_dir(
34+
root: str | Path, *, ignore_extensions: list[str] | None = None
35+
) -> Iterator[str]:
36+
ignore_exts: set[str] = set(ignore_extensions or [])
37+
3438
for filepath in Path(root).rglob("*"):
3539
if not filepath.name.startswith(".") and filepath.is_file():
40+
if filepath.suffixes and filepath.suffixes[-1][1:] in ignore_exts:
41+
continue
3642
yield str(filepath)
3743

3844

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from pathlib import Path
2+
3+
4+
def test_ignore_file_extensions(testdir):
5+
# Generate initial snapshot file
6+
testdir.makepyfile(
7+
test_file=(
8+
"""
9+
def test_case(snapshot):
10+
assert snapshot == "some-value"
11+
"""
12+
),
13+
)
14+
result = testdir.runpytest("-v", "--snapshot-update")
15+
result.stdout.re_match_lines((r"1 snapshot generated\.",))
16+
assert result.ret == 0
17+
18+
# Duplicate the snapshot file with "dvc" file extension
19+
snapshot_file = Path(testdir.tmpdir, "__snapshots__", "test_file.ambr")
20+
dvc_file = snapshot_file.with_suffix(".ambr.dvc")
21+
dvc_file.write_text(snapshot_file.read_text())
22+
23+
# Run with ignored file extension
24+
result = testdir.runpytest(
25+
"-v", "--snapshot-details", "--snapshot-ignore-file-extensions=dvc"
26+
)
27+
result.stdout.no_re_match_line(r".*1 snapshot unused.*")

tests/syrupy/test_utils.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def makefiles(testdir, filetree, root=""):
1414
filepath = Path(root).joinpath(filename)
1515
if isinstance(contents, dict):
1616
testdir.mkdir(filepath)
17-
makefiles(testdir, contents, filepath)
17+
makefiles(testdir, contents, str(filepath))
1818
else:
1919
name, ext = str(filepath.with_name(filepath.stem)), filepath.suffix
2020
testdir.makefile(ext, **{name: contents})
@@ -50,6 +50,36 @@ def test_walk_dir_skips_non_snapshot_path(testfiles):
5050
}
5151

5252

53+
def test_walk_dir_ignores_ignored_extensions(testdir):
54+
filetree = {
55+
"file1.txt": "file1",
56+
"file2.txt": "file2",
57+
"__snapshots__": {
58+
"snapfile1.ambr": "",
59+
"snapfile1.ambr.dvc": "",
60+
"snapfolder": {"snapfile2.svg": "<svg></svg>"},
61+
},
62+
}
63+
makefiles(testdir, filetree)
64+
65+
discovered_files = {
66+
str(Path(p).relative_to(Path.cwd()))
67+
for p in walk_snapshot_dir(
68+
Path(testdir.tmpdir).joinpath("__snapshots__"), ignore_extensions=["dvc"]
69+
)
70+
}
71+
72+
assert discovered_files == {
73+
str(Path("__snapshots__").joinpath("snapfile1.ambr")),
74+
str(Path("__snapshots__").joinpath("snapfolder", "snapfile2.svg")),
75+
}
76+
77+
assert (
78+
str(Path("__snapshots__").joinpath("snapfile1.ambr.dvc"))
79+
not in discovered_files
80+
)
81+
82+
5383
def dummy_member():
5484
return 123
5585

0 commit comments

Comments
 (0)