refactor(release): make tech_string a derived property

ParsedRelease.tech_string was a stored str field re-computed in two
places (assemble() at parse time, enrich_from_probe() after the probe).
The second site was a reactive fix (e79ca46) for filename builders that
saw a stale value. Turn it into an @property so it stays in sync with
quality/source/codec by construction.

- Drop the field from the dataclass + the key from assemble()'s dict.
- Drop tech_string="" from parse_release's malformed-name fallback.
- Drop the manual recomputation at the end of enrich_from_probe.
- Inject the property into asdict() result in the fixtures runner
  (same treatment as is_season_pack).
- Update tests that passed tech_string= to the constructor; rewrite the
  TestTechString case that mutated p.tech_string manually.
This commit is contained in:
2026-05-21 07:33:53 +02:00
parent 688c37bbec
commit e62dc90bd1
9 changed files with 33 additions and 23 deletions
+5 -5
View File
@@ -46,7 +46,6 @@ def _bare(**overrides) -> ParsedRelease:
source=None,
codec=None,
group="UNKNOWN",
tech_string="",
)
defaults.update(overrides)
return ParsedRelease(**defaults)
@@ -218,8 +217,9 @@ class TestLanguages:
class TestTechString:
"""tech_string drives the filename builders; it must be re-derived
whenever quality / source / codec change."""
"""tech_string is a derived property on ParsedRelease: it always
reflects the current quality/source/codec. Enrichment never writes
it directly — it stays in sync by construction."""
def test_rebuilt_from_filled_quality_and_codec(self):
p = _bare()
@@ -239,9 +239,9 @@ class TestTechString:
assert p.tech_string == "1080p.BluRay.x265"
def test_unchanged_when_no_enrichable_video_info(self):
# No video info → nothing to fill → tech_string stays as it was.
# No video info → nothing to fill → derived tech_string stays as it was.
p = _bare(quality="2160p", source="WEB-DL", codec="x265")
p.tech_string = "2160p.WEB-DL.x265"
assert p.tech_string == "2160p.WEB-DL.x265"
enrich_from_probe(p, MediaInfo())
assert p.tech_string == "2160p.WEB-DL.x265"
+2 -2
View File
@@ -123,7 +123,6 @@ class TestAssemble:
assert fields["source"] == "WEBRip"
assert fields["codec"] == "x265"
assert fields["group"] == "KONTRAST"
assert fields["tech_string"] == "1080p.WEBRip.x265"
assert fields["media_type"] == "movie"
assert fields["site_tag"] is None
@@ -150,7 +149,8 @@ class TestAssemble:
assert fields["season"] == 2
assert fields["episode"] is None # season pack
assert fields["source"] is None # ELiTE omits it
assert fields["tech_string"] == "1080p.x265"
assert fields["quality"] == "1080p"
assert fields["codec"] == "x265"
assert fields["group"] == "ELiTE"
@@ -78,7 +78,6 @@ def _movie(year: int = 2020, **overrides) -> ParsedRelease:
source="BluRay",
codec="x264",
group="GROUP",
tech_string="1080p.BluRay.x264",
media_type=MediaTypeToken.MOVIE,
parse_path=ParsePath.DIRECT,
)
@@ -120,7 +119,6 @@ class TestComputeScore:
source="WEBRip",
codec="x265",
group="KONTRAST",
tech_string="1080p.WEBRip.x265",
media_type=MediaTypeToken.TV_SHOW,
parse_path=ParsePath.DIRECT,
)
@@ -231,7 +229,6 @@ class TestCollectors:
source=None,
codec=None,
group="UNKNOWN",
tech_string="",
media_type=MediaTypeToken.UNKNOWN,
parse_path=ParsePath.DIRECT,
)
+3 -1
View File
@@ -44,8 +44,10 @@ def test_parse_matches_fixture(fixture: ReleaseFixture, tmp_path) -> None:
parsed, _report = parse_release(fixture.release_name, _KB)
result = asdict(parsed)
# ``is_season_pack`` is a @property — asdict() does not include it.
# ``is_season_pack`` and ``tech_string`` are @property values —
# ``asdict()`` does not include them.
result["is_season_pack"] = parsed.is_season_pack
result["tech_string"] = parsed.tech_string
for field, expected in fixture.expected_parsed.items():
assert field in result, (