refactor(eccc): harden language matching for bare primary subtags - #60
Conversation
|
Hi maintainers — gentle bump on this fix for #59. Adds BCP 47 prefix fallback for language matching in ECC alert parsing so that region-specific language tags (e.g. zh-CN) match their base language. CI is passing. Would appreciate a review when time allows. Thanks! |
seevee
left a comment
There was a problem hiding this comment.
Thanks for picking this up, and sorry for the slow review.
I dug into #59 before reviewing this and I don't think its premise holds. The full explanation is in my comment there, but the short version for this PR:
config_flow.pyrestricts the ECCC language option tovol.In(["auto", "en-CA", "fr-CA"]), sozh-Hanscannot be configured.autoresolves incoordinator.pyto"fr-CA" if hass.config.language.startswith("fr") else "en-CA".- NAAD CAP documents carry exactly one
en-CAand onefr-CA<info>block per area group.
So preferred_lang reaching eccc.py is always en-CA or fr-CA, and one of the two blocks always matches it exactly. The zh-Hans against zh-CN case in the description cannot occur, and on real ECCC data the new prefix branch is unreachable: the exact-match check above it always wins. This is a no-op rather than a fix. It is harmless, since en and fr primary subtags never collide, but it is not doing what the PR says it does.
Worth saying that your version is better than the sketch in #59. That sketch guarded the fallback with "-" in preferred and "-" in info_lang, which would have excluded en against en-CA. You dropped it, and that happens to be the one comparison here with real value.
Which is why I would rather reframe this than close it. There is a latent gap: if a CAP body ever declared bare en instead of en-CA, today's == misses it and falls through to doc.infos[0], which can be the French block. I have not seen NAAD do that, but prefix matching would make it safe. I would merge this as that hardening, with two changes:
- Retitle and rewrite the description around the bare
encase, dropping thezh-CNframing.refactor(eccc):fits, since there is no observed defect being fixed. - Add tests for
_language_matches. It is a new helper with no coverage in a suite of 478 tests, and the bareenagainsten-CAcase is exactly the behavior worth pinning down so a future refactor does not quietly drop it.en-CAagainstfr-CAas a negative case, plus the empty-string guards, would round it out.
One process note. The test plan says tests/test_eccc_provider.py gives "54 passed, 1 pre-existing failure (unrelated asyncio fixture issue)". On main that file is 118 passed with no failures, and the full suite is green. That gap usually means the suite ran somewhere other than the pinned environment. Setup is in AGENTS.md under "Build & Test Commands": a venv seeded from requirements_test.txt, which is what CI uses. Could you re-run there and update the numbers?
If an assistant helped draft this, that is welcome. CONTRIBUTING.md just asks that it be disclosed as a tool with an Assisted-by: trailer on the commit rather than a Co-authored-by:.
Happy to merge once it is reframed and tested. If you would rather not carry it further, no problem at all, and I will close it alongside #59.
The ECCC feed consistently carries en-CA and fr-CA info blocks, and config_flow restricts the language option to those two values, so exact-match selection works today. However, if a CAP body ever declared a bare 'en' instead of 'en-CA', the existing == check would miss it and fall through to doc.infos[0], which can be the French block. _language_matches now falls back to comparing the primary subtag (the part before the first '-') when the exact match fails, so 'en' matches 'en-CA' and vice-versa. This is hardening, not a fix for an observed defect — the ECCC feed has not been seen to do this, but prefix matching makes the selection safe if it ever does. Adds five tests covering exact match, bare-primary against region-tag, en-CA/fr-CA negative case, empty-string guards, and case insensitivity. Re-run in the pinned venv (requirements_test.txt): 123 passed, 0 failed in test_eccc_provider.py. Ruff check and format clean. Assisted-by: GitHub Copilot:glm-5.2
731ee8c to
79a5339
Compare
|
Thanks for the thorough review — really appreciate you tracing the full path from config_flow through coordinator to the actual feed data. You're right that the zh-CN scenario can't occur, and I've reframed the PR accordingly. Changes made:
Squashed into a single commit. Happy to make any further adjustments. |
seevee
left a comment
There was a problem hiding this comment.
Looks good! Thanks for the tweaks and the fast turnaround!
Please let me know or assign if if you need help on anything for fixes or issues; happy to contribute. |
Thanks for the detailed review — really appreciate you digging into #59 and tracing the full path from config_flow through coordinator to the feed itself. You're right that the zh-CN scenario in the original framing can't occur, and I've reframed this accordingly.
What this actually does
The ECCC feed carries en-CA and fr-CA, config_flow restricts to those two, and auto resolves to one of them. Exact-match selection works today. But if a CAP body ever declared a bare
eninstead ofen-CA, the existing==check would miss it and fall through todoc.infos[0], which can be the French block. This PR hardens against that by falling back to primary-subtag comparison when the exact match fails.This is hardening, not a fix for an observed defect. I haven't seen NAAD do this either, but prefix matching makes the selection safe if it ever does.
Changes from review feedback
Reframed title and description around the bare
encase, dropping the zh-CN framing. Usingrefactor(eccc):since there's no observed defect.Added tests for
_language_matches— five cases:en-CA==en-CA)envsen-CA, both directions)en-CAvsfr-CA)Re-ran in the pinned venv (Python 3.14,
requirements_test.txt): 123 passed, 0 failed intest_eccc_provider.py. The earlier "54 passed, 1 pre-existing failure" was from a different environment — in the pinned venv it's 118 on main and 123 with the new tests. Ruff check and format both clean.Added
Assisted-by:trailer per CONTRIBUTING.md — usedAssisted-by: GitHub Copilot:glm-5.2instead ofCo-authored-by:.Fixes #59