From d8026751fb61e46955650e5745c686e19fa58459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Fri, 8 Nov 2024 10:41:02 +0100 Subject: [PATCH] fix(checks): do not assume all format checks have a valid regexp While doing changes here, the type annotations were improved for check tests. Fixes WEBLATE-15ZT --- pyproject.toml | 8 -------- weblate/checks/format.py | 7 +++++-- weblate/checks/tests/test_checks.py | 20 ++++++++++---------- weblate/checks/tests/test_fluent_checks.py | 2 +- weblate/checks/tests/test_icu_checks.py | 21 ++++++++++++++++----- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c8500dc2455d..02f1ca5707fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -332,14 +332,6 @@ plugins = [ "mypy_drf_plugin.main" ] -[[tool.mypy.overrides]] -# TODO: remove this once core can be validated with mypy -ignore_errors = true -module = [ - "weblate.*.tests", - "weblate.*.tests.*" -] - [[tool.mypy.overrides]] disallow_untyped_defs = true ignore_missing_imports = true diff --git a/weblate/checks/format.py b/weblate/checks/format.py index 4352994035dd..72a75db70ab3 100644 --- a/weblate/checks/format.py +++ b/weblate/checks/format.py @@ -337,7 +337,7 @@ def extract_string_python_brace(match: re.Match) -> str: class BaseFormatCheck(TargetCheck): """Base class for format string checks.""" - regexp: Pattern[str] + regexp: Pattern[str] | None = None plural_parameter_regexp: Pattern[str] | None = None default_disabled = True normalize_remove: set[str] = set() @@ -357,7 +357,6 @@ def check_generator( # Use plural as source in case singular misses format string and plural has it if ( len(sources) > 1 - and self.regexp and not self.extract_matches(sources[0]) and self.extract_matches(sources[1]) ): @@ -414,6 +413,8 @@ def extract_string(self, match: re.Match) -> str: return extract_string_simple(match) def extract_matches(self, string: str) -> list[str]: + if self.regexp is None: + return [] return [ self.cleanup_string(self.extract_string(match)) for match in self.regexp.finditer(string) @@ -469,6 +470,8 @@ def check_single(self, source: str, target: str, unit: Unit) -> bool: def check_highlight(self, source: str, unit: Unit): if self.should_skip(unit): return + if self.regexp is None: + return match_objects = self.regexp.finditer(source) for match in match_objects: yield (match.start(), match.end(), match.group()) diff --git a/weblate/checks/tests/test_checks.py b/weblate/checks/tests/test_checks.py index c56803161fca..98d86ead46ce 100644 --- a/weblate/checks/tests/test_checks.py +++ b/weblate/checks/tests/test_checks.py @@ -16,7 +16,7 @@ from weblate.lang.models import Language, Plural if TYPE_CHECKING: - from weblate.checks.base import Check + from weblate.checks.base import BaseCheck class MockLanguage(Language): @@ -92,14 +92,14 @@ class MockUnit: def __init__( self, - id_hash=None, - flags="", - code="cs", - source="", - note="", - is_source=None, - target="", - context="", + id_hash: str | None = None, + flags: str | Flags = "", + code: str = "cs", + source: str | list[str] = "", + note: str = "", + is_source: bool | None = None, + target: str | list[str] = "", + context: str = "", ) -> None: if id_hash is None: id_hash = random.randint(0, 65536) # noqa: S311 @@ -147,7 +147,7 @@ def source_string(self): class CheckTestCase(SimpleTestCase): """Generic test, also serves for testing base class.""" - check: None | Check = None + check: None | BaseCheck = None default_lang = "cs" def setUp(self) -> None: diff --git a/weblate/checks/tests/test_fluent_checks.py b/weblate/checks/tests/test_fluent_checks.py index 73053f4d353c..b33adf166c22 100644 --- a/weblate/checks/tests/test_fluent_checks.py +++ b/weblate/checks/tests/test_fluent_checks.py @@ -34,7 +34,7 @@ def __init__( source: str, target: str = "", fluent_type: str | None = None, - unit_id: str | None = None, + unit_id: str = "", is_source: bool = False, ) -> None: self._fluent_type = fluent_type diff --git a/weblate/checks/tests/test_icu_checks.py b/weblate/checks/tests/test_icu_checks.py index 8bca37e97482..37b1e46e3cfc 100644 --- a/weblate/checks/tests/test_icu_checks.py +++ b/weblate/checks/tests/test_icu_checks.py @@ -4,6 +4,8 @@ """Tests for ICU MessageFormat checks.""" +from __future__ import annotations + from weblate.checks.icu import ICUMessageFormatCheck, ICUSourceCheck from weblate.checks.tests.test_checks import CheckTestCase, MockUnit @@ -11,11 +13,13 @@ class ICUMessageFormatCheckTest(CheckTestCase): check = ICUMessageFormatCheck() - id_hash = "icu_message_format" - flag = "icu-message-format" - flags = None + id_hash: str = "icu_message_format" + flag: str = "icu-message-format" + flags: None | str = None - def get_mock(self, source=None, flags=None): + def get_mock( + self, source: str | list[str] | None = None, flags: str | None = None + ) -> MockUnit: if not flags and self.flags: flags = self.flags elif flags and self.flags: @@ -24,7 +28,10 @@ def get_mock(self, source=None, flags=None): flags = f"{self.flag}, icu-flags:{flags}" if flags else self.flag return MockUnit( - self.id_hash, flags=flags, source=source, is_source=source is not None + self.id_hash, + flags=flags, + source=source if source is not None else "", + is_source=source is not None, ) def test_plain(self) -> None: @@ -276,6 +283,10 @@ def test_check_no_highlight(self) -> None: self.assertListEqual(highlights, []) + def test_check_target_plural(self) -> None: + unit = self.get_mock(["{count} apple", "{count} apples"]) + self.assertFalse(self.check.check_target_unit(unit.sources, unit.sources, unit)) + # This is a sub-class of our existing test set because this format is an extension # of the other format and it should handle all existing syntax properly.