Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade type references for Python 3.9+ #477

Merged
merged 2 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/baseframe/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import os
import sys
import typing as t

sys.path.append(os.path.abspath('../../src'))
from baseframe import _version # isort:skip
Expand Down Expand Up @@ -181,7 +180,7 @@

# -- Options for LaTeX output --------------------------------------------------

latex_elements: t.Dict[str, str] = {
latex_elements: dict[str, str] = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
Expand Down
30 changes: 15 additions & 15 deletions src/baseframe/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import json
import os.path
import typing as t
import typing_extensions as te
from collections.abc import Iterable
from typing import Literal, Optional, Union

import sentry_sdk
from flask import Blueprint, Flask
Expand Down Expand Up @@ -59,7 +59,7 @@
}


def _select_jinja_autoescape(filename: t.Optional[str]) -> bool:
def _select_jinja_autoescape(filename: Optional[str]) -> bool:
"""Return `True` if autoescaping should be active for the given template name."""
if filename is None:
return False
Expand All @@ -83,12 +83,12 @@ class BaseframeBlueprint(Blueprint):
def init_app(
self,
app: Flask,
requires: t.Iterable[str] = (),
ext_requires: t.Iterable[t.Union[str, t.List[str], t.Tuple[str, ...]]] = (),
bundle_js: t.Optional[Bundle] = None,
bundle_css: t.Optional[Bundle] = None,
assetenv: t.Optional[Environment] = None,
theme: te.Literal['bootstrap3', 'mui'] = 'bootstrap3',
requires: Iterable[str] = (),
ext_requires: Iterable[Union[str, list[str], tuple[str, ...]]] = (),
bundle_js: Optional[Bundle] = None,
bundle_css: Optional[Bundle] = None,
assetenv: Optional[Environment] = None,
theme: Literal['bootstrap3', 'mui'] = 'bootstrap3',
error_handlers: bool = True,
) -> None:
"""
Expand Down Expand Up @@ -181,10 +181,10 @@ def init_app(
else:
subdomain = None

ignore_js: t.List[str] = []
ignore_css: t.List[str] = []
ext_js: t.List[t.List[str]] = []
ext_css: t.List[t.List[str]] = []
ignore_js: list[str] = []
ignore_css: list[str] = []
ext_js: list[list[str]] = []
ext_css: list[list[str]] = []
requires = [
item
for itemgroup in ext_requires
Expand All @@ -196,8 +196,8 @@ def init_app(
app.config['ext_js'] = ext_js
app.config['ext_css'] = ext_css

assets_js: t.List[str] = []
assets_css: t.List[str] = []
assets_js: list[str] = []
assets_css: list[str] = []
for item in requires:
name, spec = split_namespec(item)
for alist, ext in [(assets_js, '.js'), (assets_css, '.css')]:
Expand Down
11 changes: 5 additions & 6 deletions src/baseframe/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
# pyright: reportMissingImports = false

import os.path
import typing as t
import typing_extensions as te
from datetime import tzinfo
from typing import cast
from typing import Protocol, Union, cast
from typing_extensions import LiteralString

from flask import current_app, request
from flask_babel import Babel, Domain
Expand Down Expand Up @@ -39,15 +38,15 @@
]


class GetTextProtocol(te.Protocol):
class GetTextProtocol(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: ...
def __call__(self, string: LiteralString, **variables) -> str: ...


DEFAULT_LOCALE = 'en'
Expand Down Expand Up @@ -87,7 +86,7 @@ def get_user_locale() -> str:
) or DEFAULT_LOCALE


def get_timezone(default: t.Union[None, tzinfo, str] = None) -> tzinfo:
def get_timezone(default: Union[None, tzinfo, str] = None) -> tzinfo:
"""Return a timezone suitable for the current context."""
# If this app and request have a user, return user's timezone,
# else return app default timezone
Expand Down
58 changes: 27 additions & 31 deletions src/baseframe/filters.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"""Jinja2 filters."""

import os.path
import typing as t
import typing_extensions as te
from datetime import date, datetime, time, timedelta
from typing import TYPE_CHECKING, cast
from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast
from urllib.parse import urlsplit, urlunsplit

import grapheme
Expand Down Expand Up @@ -60,7 +58,7 @@ def age(dt: datetime) -> str:


@baseframe.app_template_filter('initials')
def initials(text: t.Optional[str]) -> str:
def initials(text: Optional[str]) -> str:
"""Return up to two initials from the given string, for a default avatar image."""
if not text:
return ''
Expand Down Expand Up @@ -101,10 +99,8 @@ def nossl(url: str) -> str:
# TODO: Move this into Hasjob as it's not used elsewhere
@baseframe.app_template_filter('avatar_url')
def avatar_url(
user: t.Any,
size: t.Optional[
t.Union[str, t.List[int], t.Tuple[int, int], t.Tuple[str, str]]
] = None,
user: Any,
size: Optional[Union[str, list[int], tuple[int, int], tuple[str, str]]] = None,
) -> str:
"""Generate an avatar for the given user."""
if isinstance(size, (list, tuple)):
Expand Down Expand Up @@ -135,7 +131,7 @@ def avatar_url(


@baseframe.app_template_filter('render_field_options')
def render_field_options(field: WTField, **kwargs: t.Any) -> str:
def render_field_options(field: WTField, **kwargs: Any) -> str:
"""Remove HTML attributes with falsy values before rendering a field."""
d = {k: v for k, v in kwargs.items() if v is not None and v is not False}
if field.render_kw:
Expand All @@ -145,9 +141,9 @@ def render_field_options(field: WTField, **kwargs: t.Any) -> str:

# TODO: Only used in renderfield.mustache. Re-check whether this is necessary at all.
@baseframe.app_template_filter('to_json')
def form_field_to_json(field: WTField, **kwargs: t.Any) -> t.Dict[str, t.Any]:
def form_field_to_json(field: WTField, **kwargs: Any) -> dict[str, Any]:
"""Render a form field as JSON."""
d: t.Dict[str, t.Any] = {}
d: dict[str, Any] = {}
d['id'] = field.id
d['label'] = field.label.text
d['has_errors'] = bool(field.errors)
Expand All @@ -171,7 +167,7 @@ def field_markdown(text: str) -> Markup:


@baseframe.app_template_filter('ext_asset_url')
def ext_asset_url(asset: t.Union[str, t.List[str]]) -> str:
def ext_asset_url(asset: Union[str, list[str]]) -> str:
"""Return external asset URL for use in templates."""
if isinstance(asset, str):
return ext_assets([asset])
Expand Down Expand Up @@ -232,10 +228,10 @@ def cdata(text: str) -> str:

# TODO: Used only in Hasjob. Move there?
@baseframe.app_template_filter('shortdate')
def shortdate(value: t.Union[datetime, date]) -> str:
def shortdate(value: Union[datetime, date]) -> str:
"""Render a date in short form (deprecated for lack of i18n support)."""
dt: t.Union[datetime, date]
utc_now: t.Union[datetime, date]
dt: Union[datetime, date]
utc_now: Union[datetime, date]
if isinstance(value, datetime):
tz = get_timezone()
if value.tzinfo is None:
Expand All @@ -258,9 +254,9 @@ def shortdate(value: t.Union[datetime, date]) -> str:

# TODO: Only used in Hasjob. Move there?
@baseframe.app_template_filter('longdate')
def longdate(value: t.Union[datetime, date]) -> str:
def longdate(value: Union[datetime, date]) -> str:
"""Render a date in long form (deprecated for lack of i18n support)."""
dt: t.Union[datetime, date]
dt: Union[datetime, date]
if isinstance(value, datetime):
if value.tzinfo is None:
dt = utc.localize(value).astimezone(get_timezone())
Expand All @@ -273,13 +269,13 @@ def longdate(value: t.Union[datetime, date]) -> str:

@baseframe.app_template_filter('date')
def date_filter(
value: t.Union[datetime, date],
value: Union[datetime, date],
format: str = 'medium', # noqa: A002 # pylint: disable=W0622
locale: t.Optional[t.Union[Locale, str]] = None,
locale: Optional[Union[Locale, str]] = None,
usertz: bool = True,
) -> str:
"""Render a localized date."""
dt: t.Union[datetime, date]
dt: Union[datetime, date]
if isinstance(value, datetime) and usertz:
if value.tzinfo is None:
dt = utc.localize(value).astimezone(get_timezone())
Expand All @@ -294,14 +290,14 @@ def date_filter(

@baseframe.app_template_filter('time')
def time_filter(
value: t.Union[datetime, time],
value: Union[datetime, time],
format: str = 'short', # noqa: A002 # pylint: disable=W0622
locale: t.Optional[t.Union[Locale, str]] = None,
locale: Optional[Union[Locale, str]] = None,
usertz: bool = True,
) -> str:
"""Render a localized time."""
# Default format = hh:mm
dt: t.Union[datetime, time]
dt: Union[datetime, time]
if isinstance(value, datetime) and usertz:
if value.tzinfo is None:
dt = utc.localize(value).astimezone(get_timezone())
Expand All @@ -316,13 +312,13 @@ def time_filter(

@baseframe.app_template_filter('datetime')
def datetime_filter(
value: t.Union[datetime, date, time],
value: Union[datetime, date, time],
format: str = 'medium', # noqa: A002 # pylint: disable=W0622
locale: t.Optional[t.Union[Locale, str]] = None,
locale: Optional[Union[Locale, str]] = None,
usertz: bool = True,
) -> str:
"""Render a localized date and time."""
dt: t.Union[datetime, date, time]
dt: Union[datetime, date, time]
if isinstance(value, datetime) and usertz:
if value.tzinfo is None:
dt = utc.localize(value).astimezone(get_timezone())
Expand All @@ -345,16 +341,16 @@ def timestamp_filter(value: datetime) -> float:

@baseframe.app_template_filter('timedelta')
def timedelta_filter(
delta: t.Union[int, timedelta, datetime],
granularity: te.Literal[
delta: Union[int, timedelta, datetime],
granularity: Literal[
'year', 'month', 'week', 'day', 'hour', 'minute', 'second'
] = 'second',
threshold: float = 0.85,
add_direction: bool = False,
format: te.Literal[ # noqa: A002 # pylint: disable=W0622
format: Literal[ # noqa: A002 # pylint: disable=W0622
'narrow', 'short', 'medium', 'long'
] = 'long',
locale: t.Optional[t.Union[Locale, str]] = None,
locale: Optional[Union[Locale, str]] = None,
) -> str:
"""
Render a timedelta or int (representing seconds) as a duration.
Expand Down Expand Up @@ -390,7 +386,7 @@ def timedelta_filter(


@baseframe.app_template_filter('cleanurl')
def cleanurl_filter(url: t.Union[str, furl]) -> furl:
def cleanurl_filter(url: Union[str, furl]) -> furl:
"""Clean a URL visually by removing defaults like scheme and the ``www`` prefix."""
if not isinstance(url, furl):
url = furl(url)
Expand Down
24 changes: 12 additions & 12 deletions src/baseframe/forms/auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

import typing as t
from typing import TYPE_CHECKING, Any, Optional, Union

import wtforms
from flask import (
Expand All @@ -26,7 +26,7 @@
from .fields import SubmitField
from .form import Form

if t.TYPE_CHECKING:
if TYPE_CHECKING:
from flask_sqlalchemy import SQLAlchemy

_submit_str = __("Submit")
Expand All @@ -43,15 +43,15 @@ class ConfirmDeleteForm(Form):
def render_form(
form: Form,
title: str,
message: t.Optional[t.Union[str, Markup]] = None,
formid: t.Optional[str] = None,
message: Optional[Union[str, Markup]] = None,
formid: Optional[str] = None,
submit: str = _submit_str,
cancel_url: t.Optional[str] = None,
cancel_url: Optional[str] = None,
ajax: bool = False,
with_chrome: bool = True,
action: t.Optional[str] = None,
action: Optional[str] = None,
autosave: bool = False,
draft_revision: t.Optional[t.Any] = None,
draft_revision: Optional[Any] = None,
template: str = '',
) -> Response:
"""Render a form."""
Expand Down Expand Up @@ -123,15 +123,15 @@ def render_redirect(url: str, code: int = 302) -> Response:


def render_delete_sqla(
obj: t.Any,
obj: Any,
db: SQLAlchemy,
title: str,
message: str,
success: str = '',
next: t.Optional[str] = None, # noqa: A002 # pylint: disable=W0622
cancel_url: t.Optional[str] = None,
delete_text: t.Optional[str] = None,
cancel_text: t.Optional[str] = None,
next: Optional[str] = None, # noqa: A002 # pylint: disable=W0622
cancel_url: Optional[str] = None,
delete_text: Optional[str] = None,
cancel_text: Optional[str] = None,
) -> Response:
"""Render a delete page for SQLAlchemy objects."""
if not obj:
Expand Down
Loading
Loading