diff --git a/src/pydocvi/audit/placeholders.py b/src/pydocvi/audit/placeholders.py index 03ab9bd..1c37ca0 100644 --- a/src/pydocvi/audit/placeholders.py +++ b/src/pydocvi/audit/placeholders.py @@ -244,13 +244,21 @@ def p07_code_is_byte_identical(corpus: Corpus) -> Iterator[Finding]: @check("P08", Group.PLACEHOLDERS, hard=True, title="no fence or horizontal rule") def p08_no_fence(corpus: Corpus) -> Iterator[Finding]: - """No ``msgstr`` opens a fence or a ``---``. + """No ``msgstr`` opens a fence or a ``---`` that its ``msgid`` did not. Both are a model formatting its answer rather than answering, and both are valid reST that renders as something the English does not. + + Unless the English opened the same way, which is the clause this check was + missing. Every other rule in this module reads the pair, and this one read + the translation alone, so a ``msgid`` of ``---`` copied through as ``---`` + was reported as a model drawing a rule under its answer. Two entries in the + corpus are that: a literal ``---`` in ``c-api/call.po`` and the inheritance + diagram in ``howto/mro.po``, which opens with a line of dashes because it is + a picture of a class hierarchy. """ for one, entry in corpus.translated(): - if _FENCE.match(entry.msgstr): + if _FENCE.match(entry.msgstr) and not _FENCE.match(entry.msgid): yield Finding( check="P08", path=corpus.relative(one.path), diff --git a/src/pydocvi/textguard.py b/src/pydocvi/textguard.py index 33078a6..68c6cda 100644 --- a/src/pydocvi/textguard.py +++ b/src/pydocvi/textguard.py @@ -78,9 +78,18 @@ class _Guard: ) #: A fence or a rule opening the answer. The model presenting its work rather -#: than doing it, which is the same failure wearing different clothes. No -#: licence: the corpus has literal blocks, but a protected one reaches this -#: module as a placeholder and never as three backticks. +#: than doing it, which is the same failure wearing different clothes. +#: +#: Licensed by the source opening the same way, which it did not used to be. The +#: reasoning for having no licence was that the corpus has literal blocks but a +#: protected one reaches this module as a placeholder and never as three +#: backticks, and that is true of the translate path and not of the audit. +#: ``L03`` hands over the raw ``msgstr`` and the raw ``msgid``, with nothing +#: protected, so an entry whose ``msgid`` is ``---`` arrived here as ``---`` and +#: was read as a model drawing a rule. Two entries in the corpus are exactly +#: that: a literal ``---`` in ``c-api/call.po`` and the inheritance diagram in +#: ``howto/mro.po``, both copied through correctly and both reported as +#: narration by the check that exists to catch a model talking. _FENCE = re.compile(r"^\s*(?:```|~~~|---)") #: A parenthetical aside about the translation itself, in either language. @@ -116,7 +125,7 @@ def find(text: str, source: str = "") -> Narration | None: without making the decision any different. """ fence = _FENCE.match(text) - if fence: + if fence and not _FENCE.match(source): return Narration(phrase=fence.group(0).strip(), where=0) hits = [ diff --git a/tests/test_audit_placeholders.py b/tests/test_audit_placeholders.py index 0499a2b..683d536 100644 --- a/tests/test_audit_placeholders.py +++ b/tests/test_audit_placeholders.py @@ -173,6 +173,16 @@ def test_an_ordinary_translation_is_clean(self) -> None: corpus = over("Return a list.", "Trả về một danh sách.") assert findings(placeholders.p08_no_fence, corpus) == [] + def test_a_rule_the_english_opened_is_not_the_model_drawing_one(self) -> None: + """Every other rule in this module reads the pair and this one read the + translation alone, so a ``msgid`` of ``---`` copied through as ``---`` + was reported. Two entries in the corpus are that.""" + assert findings(placeholders.p08_no_fence, over("---", "---")) == [] + + def test_a_diagram_that_opens_with_dashes_survives_being_copied(self) -> None: + art = " -----------\n| O |\n| / \\ |" + assert findings(placeholders.p08_no_fence, over(art, art)) == [] + class TestEveryCheckReportsWhereItLooked: def test_a_finding_carries_the_file_the_line_and_the_segment(self) -> None: diff --git a/tests/test_textguard.py b/tests/test_textguard.py index 3c062d8..d318093 100644 --- a/tests/test_textguard.py +++ b/tests/test_textguard.py @@ -116,10 +116,23 @@ def test_a_phrase_with_no_licence_fires_however_the_source_reads(self) -> None: documentation does not say "as an AI", so nothing can excuse it.""" assert not textguard.clean("As an AI, I note this.", "As an AI system would note that.") - def test_a_fence_is_never_licensed(self) -> None: - """A literal block reaches this module as a placeholder, never as three - backticks, so a fence in an answer is always the model presenting.""" - assert not textguard.clean("```\nTrả về danh sách.\n```", "```\nsorted(x)\n```") + def test_a_fence_the_source_did_not_open_is_the_model_presenting(self) -> None: + assert not textguard.clean("```\nTrả về danh sách.\n```", "Return a list.") + + def test_a_fence_the_source_opened_is_licensed_like_any_other_phrase(self) -> None: + """This asserted the opposite until August 2026, on the grounds that a + literal block reaches this module as a placeholder and never as three + backticks. True of the translate path and not of the audit, which hands + over the raw pair: ``L03`` reported the ``---`` in ``c-api/call.po`` and + the class diagram in ``howto/mro.po`` as a model drawing a rule, when + both are the English copied through exactly.""" + assert textguard.clean("---", "---") + assert textguard.clean(" ----\n| O |", " ----\n| O |") + + def test_a_fence_with_no_source_is_still_narration(self) -> None: + """Passing no source licenses nothing, which is the right answer for a + caller holding a string and no idea what it was made from.""" + assert not textguard.clean("---\nTrả về danh sách.") def test_the_leftmost_phrase_is_the_one_reported_not_the_first_listed(self) -> None: found = textguard.find("Trả về danh sách. Xin lỗi. Here is the translation.")