| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,8 +14,10 @@ | |||
| 14 | 14 | knowable without running anything: `source` is an `asset:`/`output:` | |
| 15 | 15 | reference or a literal path the run may read (`dw/probe_paths.py`), probed | |
| 16 | 16 | header-only (`probe_metadata`, B9); `num_frames` and `overlap` are literal | |
| 17 | - after substitution; and `videos` is a list - which is what `gather:<step>` | ||
| 18 | - becomes once `for_each` has expanded, one entry per member. The rule itself | ||
| 17 | + whole numbers after substitution - an int, an integral float or a numeric | ||
| 18 | + string, read by `whole_number` as the run reads them (#785); and `videos` is | ||
| 19 | + a list - which is what `gather:<step>` becomes once `for_each` has expanded, | ||
| 20 | + one entry per member. The rule itself | ||
| 19 | 21 | is `window_count_problem` in `dw/task_domains.py`, the one the task calls at | |
| 20 | 22 | run time, so the two cannot drift. | |
| 21 | 23 | ||
@@ -28,24 +30,23 @@ | |||
| 28 | 30 | report, so it is skipped here rather than reported twice. | |
| 29 | 31 | """ | |
| 30 | 32 | ||
| 31 | - import numbers | ||
| 32 | - | ||
| 33 | 33 | from .for_each import MEMBER_SEPARATOR, render_path | |
| 34 | 34 | from .media import probe_metadata | |
| 35 | 35 | from .probe_paths import resolve_probe_path | |
| 36 | 36 | from .references import author_index | |
| 37 | - from .task_domains import window_count_problem | ||
| 38 | - | ||
| 39 | - | ||
| 40 | - def _literal_whole(value): | ||
| 41 | - """`value` as an int when it is a literal whole number, else None.""" | ||
| 42 | - if isinstance(value, bool): | ||
| 37 | + from .task_domains import whole_number, window_count_problem | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + def _literal_whole(value, name): | ||
| 41 | + """`value` as an int when it is a literal whole number, read by | ||
| 42 | + `whole_number`'s rules (`33`, `33.0`, `"33"`, `"33.0"` are all 33), else | ||
| 43 | + None. A value that is not a whole number - a fraction, a bool, an | ||
| 44 | + unresolved reference - is skipped here: `join_windows_errors` refuses it | ||
| 45 | + with the "whole number" message, so it is not reported twice (#785).""" | ||
| 46 | + try: | ||
| 47 | + return whole_number(value, name, "join_windows") | ||
| 48 | + except ValueError: | ||
| 43 | 49 | return None | |
| 44 | - if isinstance(value, numbers.Integral): | ||
| 45 | - return int(value) | ||
| 46 | - if isinstance(value, float) and value.is_integer(): | ||
| 47 | - return int(value) | ||
| 48 | - return None | ||
| 49 | 50 | ||
| 50 | 51 | ||
| 51 | 52 | def _frame_count(path, probe): | |
@@ -87,8 +88,8 @@ def window_count_errors( | |||
| 87 | 88 | videos = arguments.get("videos") | |
| 88 | 89 | if not isinstance(videos, list) or not videos: | |
| 89 | 90 | continue | |
| 90 | - num_frames = _literal_whole(arguments.get("num_frames")) | ||
| 91 | - overlap = _literal_whole(arguments.get("overlap")) | ||
| 91 | + num_frames = _literal_whole(arguments.get("num_frames"), "num_frames") | ||
| 92 | + overlap = _literal_whole(arguments.get("overlap"), "overlap") | ||
| 92 | 93 | if num_frames is None or overlap is None: | |
| 93 | 94 | continue | |
| 94 | 95 | if num_frames <= 0 or overlap < 0 or overlap >= num_frames: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,8 @@ | |||
| 6 | 6 | step) rather than a template. | |
| 7 | 7 | """ | |
| 8 | 8 | ||
| 9 | + import pytest | ||
| 10 | + | ||
| 9 | 11 | from dw.media import probe_metadata | |
| 10 | 12 | from dw.window_count_errors import window_count_errors | |
| 11 | 13 | from dw.workflow import workflow_from_definition | |
@@ -55,12 +57,13 @@ def window_workflow(window_count, num_frames=NUM_FRAMES, overlap=OVERLAP): | |||
| 55 | 57 | } | |
| 56 | 58 | ||
| 57 | 59 | ||
| 58 | - def validate(monkeypatch, tmp_path, window_count, frames=50): | ||
| 60 | + def validate(monkeypatch, tmp_path, window_count, frames=50, **numbers): | ||
| 59 | 61 | import os | |
| 60 | 62 | ||
| 61 | 63 | base_dir = workflow_dir_with_asset(monkeypatch, tmp_path, ("long.mp4", frames)) | |
| 62 | 64 | workflow = workflow_from_definition( | |
| 63 | - window_workflow(window_count), os.path.join(base_dir, "workflow.json") | ||
| 65 | + window_workflow(window_count, **numbers), | ||
| 66 | + os.path.join(base_dir, "workflow.json"), | ||
| 64 | 67 | ) | |
| 65 | 68 | return [ | |
| 66 | 69 | problem | |
@@ -110,6 +113,45 @@ def test_a_long_list_names_the_drop(self, monkeypatch, tmp_path): | |||
| 110 | 113 | assert "got 5" in problems[0]["message"] | |
| 111 | 114 | assert "drop 1 entry (index 4)" in problems[0]["message"] | |
| 112 | 115 | ||
| 116 | + @pytest.mark.parametrize( | ||
| 117 | + "num_frames, overlap", | ||
| 118 | + [ | ||
| 119 | + (NUM_FRAMES, OVERLAP), | ||
| 120 | + (str(NUM_FRAMES), str(OVERLAP)), | ||
| 121 | + (float(NUM_FRAMES), float(OVERLAP)), | ||
| 122 | + (f"{NUM_FRAMES}.0", float(OVERLAP)), | ||
| 123 | + (f"{NUM_FRAMES}.0", f"{OVERLAP}.0"), | ||
| 124 | + ], | ||
| 125 | + ids=["int", "string", "float", "float-string+float", "float-strings"], | ||
| 126 | + ) | ||
| 127 | + def test_every_whole_number_form_gets_the_int_refusal( | ||
| 128 | + self, monkeypatch, tmp_path, num_frames, overlap | ||
| 129 | + ): | ||
| 130 | + """C-F354h (#785): "17", 17.0 and "17.0" are read by whole_number's | ||
| 131 | + rules, as the run reads them, so validate refuses the short list the | ||
| 132 | + same way it does for 17 instead of skipping the check.""" | ||
| 133 | + problems = validate( | ||
| 134 | + monkeypatch, tmp_path, 3, num_frames=num_frames, overlap=overlap | ||
| 135 | + ) | ||
| 136 | + | ||
| 137 | + assert problems == [ | ||
| 138 | + { | ||
| 139 | + "path": "steps[1]", | ||
| 140 | + "message": "join_windows needs 4 windows for a 50-frame source" | ||
| 141 | + " with num_frames 17 and overlap 4 (stride 13), got 3" | ||
| 142 | + " - add 1 entry (index 3)", | ||
| 143 | + } | ||
| 144 | + ] | ||
| 145 | + | ||
| 146 | + def test_a_fractional_string_is_left_to_the_whole_number_refusal( | ||
| 147 | + self, monkeypatch, tmp_path | ||
| 148 | + ): | ||
| 149 | + """A non-whole value names no count; the domain check refuses it.""" | ||
| 150 | + problems = validate(monkeypatch, tmp_path, 3, num_frames="17.5") | ||
| 151 | + | ||
| 152 | + assert not [p for p in problems if "windows for a" in p["message"]] | ||
| 153 | + assert any("whole number for 'num_frames'" in p["message"] for p in problems) | ||
| 154 | + | ||
| 113 | 155 | def test_an_unreadable_literal_source_is_refused_at_its_path( | |
| 114 | 156 | self, monkeypatch, tmp_path | |
| 115 | 157 | ): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments