Skip to content

fix(title): keep the title when alternative_title is excluded - #942

Merged
Toilal merged 3 commits into
guessit-io:developfrom
Sanjays2402:fix/exclude-alternative-title-keeps-title
Jul 30, 2026
Merged

fix(title): keep the title when alternative_title is excluded#942
Toilal merged 3 commits into
guessit-io:developfrom
Sanjays2402:fix/exclude-alternative-title-keeps-title

Conversation

@Sanjays2402

Copy link
Copy Markdown
Contributor

closes #927

--excludes alternative_title disabled TitleFromPosition outright, so guessit returned no title at all for any filename. The exclusion now only skips the alternative-title split, and the whole title hole is kept as title (Star Wars - The Mandalorian and Grogu).

While fixing it I also had to copy match_tags when tagging the hole rather than aliasing the rule's own list — the split path had been hiding that, and without the copy the shared list accumulated tags across parses and corrupted a later unrelated filename.

Regression case added to enable_disable_properties.yml; it fails on develop and passes with the fix, and the full suite is green.

This change was prepared with AI assistance; the regression test was run locally and fails without the fix.

@Toilal Toilal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, and the second half of the diff is a better find than the PR body lets on. I merged develop into the branch locally and measured it before reviewing, since no CI ran here.

Verification

  • Full suite after merging current develop: 2482 passed, 4 skipped. pytest -m cross_parser green, ruff/mypy clean.
  • Behaviour diff over 2921 names (yaml corpus + cross-parser datasets) in auto mode: 0 changes — the fix is properly confined to the excludes/includes paths.
  • The blast radius is wider than described: on develop, --exclude alternative_title also killed episode_title (Show.Name.S01E02.The.Episode.Title returned neither title nor episode_title), and --includes title returned {}. Both work now.

Findings

1. hole.tags = list(...) fixes a state leak across guessit() calls — please call it out

I reproduced it by dropping the copy from your branch:

match_tags start: ['title', 'equivalent-ignore']
Movies/Foo (2019)/Foo.2019…  → rule.match_tags=['title', 'equivalent-ignore', 'equivalent-ignore']
Bar.2020.720p.mkv            → rule.match_tags=['title', 'equivalent-ignore', 'equivalent-ignore', 'equivalent-ignore']

PreferTitleWithYear's AppendTags(["equivalent-ignore"]) does match.tags.extend(...) (rebulk rules.py:218) on the rule instance's own list, which the cached rebulk shares across every parse. After that, every title produced is born tagged equivalent-ignore, which processors.py:80 reads. Latent on develop (the split path hid it), live as soon as this change lands. The copy belongs exactly where you put it — in TitleBaseRule, so EpisodeTitleFromPosition is covered too.

2. That fix has no test

A yaml entry parses a single string, so it structurally cannot catch cross-parse contamination. A small Python test that runs two guessit(..., {"excludes": ["alternative_title"]}) calls in a row and asserts the second title is untagged (or that match_tags is still ["title"]) would keep the copy from being refactored away silently.

3. Worth pinning: --includes title

It now returns title: "Movie Name 2019 1080p BluRay x264-GRP mkv" — the title hole spans everything, since nothing else is matched to bound it. Consistent with what includes means, and better than the previous {}, but an entry in enable_disable_properties.yml would make the expectation explicit.

4. Nit

Two independent fixes in one commit. Both are justified, and the PR body explains it — but the aliasing fix stands on its own merit and would have read better as its own commit.

Happy to merge once (2) is in.


Review measurements produced with Claude Code assistance.

`--excludes alternative_title` disabled TitleFromPosition entirely, so no
title was produced at all for any filename. The exclusion now only turns off
the alternative-title split: the whole title hole is kept as `title`.

Also copy `match_tags` when tagging the hole instead of aliasing the rule's
own list, which the split path previously masked; without the copy the
shared list accumulated tags across runs and leaked into later parses.

Regression case added to enable_disable_properties.yml.
@Toilal
Toilal force-pushed the fix/exclude-alternative-title-keeps-title branch from 96da579 to 91f0dd9 Compare July 30, 2026 21:18
Toilal and others added 2 commits July 30, 2026 23:26
The title hole used to alias `TitleFromPosition.match_tags` instead of
copying it. The rule instance lives in the cached rebulk and is shared by
every `guessit()` call, so when `PreferTitleWithYear` appended
`equivalent-ignore` to a demoted title it extended that shared list in
place: the list grew at every parse and every later title was born already
tagged `equivalent-ignore`, a tag the processors act upon.

Nothing covered that copy. A yaml corpus entry cannot: each entry parses a
single string, while the leak only appears on the parses that follow, so
the regression test has to be a Python one running several guesses in a row
with `excludes: alternative_title` — at least one on a filepart carrying a
year, which is what triggers `PreferTitleWithYear`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FciPHULDaS2HBNqYXcfMC3
Before the `TitleFromPosition` fix, restricting the run to the single
`title` property returned nothing at all: the rule disabled itself because
`alternative_title` was not among the included properties, so no title was
ever produced. `--include title` now yields a title again.

With only `title` enabled no other property matches, so nothing bounds the
title hole and it legitimately spans the whole name, extension included.
That is the behaviour worth pinning: the entry guards against the rule
going silent again under a restrictive `--include`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FciPHULDaS2HBNqYXcfMC3
@Toilal

Toilal commented Jul 30, 2026

Copy link
Copy Markdown
Member

Heads up: I pushed to your branch directly rather than round-tripping the review — maintainerCanModify was on, so:

  1. Rebased onto current develop (no conflicts). Force-pushed, so git fetch && git reset --hard origin/fix/exclude-alternative-title-keeps-title before you touch it again.
  2. 0096646 — a regression test for the list(self.match_tags or []) copy, in test_api.py next to the sibling state-leak test. It parses three times in a row with --exclude alternative_title and asserts the rule's own match_tags is still ["title"]. Reverting your copy turns it red with assert ['title', 'eq...alent-ignore'] == ['title'].
  3. e09ecc6 — pinned --include title in enable_disable_properties.yml (it used to return {}; the title now spans the whole name, since nothing else is matched to bound the hole).

The tags copy is the part worth highlighting: it is a genuine cross-parse state leak, latent on develop because the split path hid it, and live the moment titles = [hole] becomes reachable. Good instinct to fix it rather than route around it.

Also note the failing build jobs you saw were not your fault: semantic-release exits non-zero on the detached merge commit of a pull_request checkout. Fixed on develop (05cfcd7), and this run picks it up.

@Toilal
Toilal merged commit 502564b into guessit-io:develop Jul 30, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The option --excludes is not working properly with alternative_title , separator '-' interfering as alternative_title propertie

2 participants