From b5304540a306716f493d4a3bfe1545242c894708 Mon Sep 17 00:00:00 2001 From: Kiran Jonnalagadda Date: Sun, 31 Dec 2023 18:02:16 +0530 Subject: [PATCH] Specify that gettext functions require a LiteralString; other fixes --- .pre-commit-config.yaml | 6 ++++++ pyproject.toml | 4 ++-- src/baseframe/__init__.py | 7 ++++--- src/baseframe/extensions.py | 17 +++++++++++++++-- src/baseframe/forms/validators.py | 6 ++++-- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dcf92f9a..ba082d6b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -111,6 +111,12 @@ repos: hooks: - id: reformat-gherkin files: \.feature$ + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.9.0.6 + hooks: + - id: shellcheck + args: + - --external-sources - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: diff --git a/pyproject.toml b/pyproject.toml index deb63447..1e34e955 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -122,8 +122,8 @@ ignore_missing_imports = true show_error_codes = true warn_unreachable = true warn_unused_ignores = true -warn_redundant_casts = false -check_untyped_defs = false +warn_redundant_casts = true +check_untyped_defs = true [tool.pytest.ini_options] pythonpath = 'src' diff --git a/src/baseframe/__init__.py b/src/baseframe/__init__.py index cd9fc3ef..e8e05ffa 100644 --- a/src/baseframe/__init__.py +++ b/src/baseframe/__init__.py @@ -1,6 +1,6 @@ """Baseframe init.""" -from typing import Callable, cast +from typing import cast from flask_babel import gettext, lazy_gettext @@ -9,6 +9,7 @@ from .blueprint import * # NOQA from .deprecated import * # NOQA from .extensions import * # NOQA +from .extensions import GetTextProtocol from .filters import * # NOQA from .utils import * # NOQA from .views import * # NOQA @@ -16,8 +17,8 @@ from . import forms # isort:skip # Pretend these return str, not Any or LazyString -_ = cast(Callable[..., str], gettext) -__ = cast(Callable[..., str], lazy_gettext) +_ = cast(GetTextProtocol, gettext) +__ = cast(GetTextProtocol, lazy_gettext) # TODO: baseframe_js and baseframe_css are defined in deprecated.py # and pending removal after an audit of all apps diff --git a/src/baseframe/extensions.py b/src/baseframe/extensions.py index fe2ecab1..51298cc5 100644 --- a/src/baseframe/extensions.py +++ b/src/baseframe/extensions.py @@ -3,6 +3,7 @@ import os.path import typing as t +import typing_extensions as te from datetime import tzinfo from typing import cast @@ -37,6 +38,18 @@ ] +class GetTextProtocol(te.Protocol): + """ + Callable protocol for gettext and lazy_gettext. + + Specify that the first parameter must always be a literal string, not a variable, + and that the return type is a string (though actually a LazyString). + """ + + def __call__(self, string: te.LiteralString, **variables) -> str: + ... + + DEFAULT_LOCALE = 'en' networkbar_cache = Cache(with_jinja2_ext=False) @@ -52,8 +65,8 @@ baseframe_translations = Domain( os.path.join(os.path.dirname(__file__), 'translations'), domain='baseframe' ) -_ = cast(t.Callable[..., str], baseframe_translations.gettext) -__ = cast(t.Callable[..., str], baseframe_translations.lazy_gettext) +_ = cast(GetTextProtocol, baseframe_translations.gettext) +__ = cast(GetTextProtocol, baseframe_translations.lazy_gettext) def get_user_locale() -> str: diff --git a/src/baseframe/forms/validators.py b/src/baseframe/forms/validators.py index 8c2329ba..490dda5d 100644 --- a/src/baseframe/forms/validators.py +++ b/src/baseframe/forms/validators.py @@ -80,8 +80,10 @@ } InvalidUrlPatterns = t.Iterable[t.Tuple[t.Iterable[t.Any], str]] -AllowedListInit = t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], None] -AllowedList = t.Union[t.Iterable[str], None] +AllowedListInit = t.Optional[ + t.Union[t.Iterable[str], t.Callable[[], t.Optional[t.Iterable[str]]]] +] +AllowedList = t.Optional[t.Iterable[str]] def is_empty(value: t.Any) -> bool: