Replaced importing the "typing" module as "t" with importing the individual names in the "typing" module. Since Python 3.9 introduced type hinting generics in standard collections, we do not have as many names to import now. This is also to be consistent with the practices of most major and standard packages and examples.
This commit is contained in:
parent
ee5b447c23
commit
cda9e4e3c6
@ -17,15 +17,15 @@
|
||||
"""The console commands for the account management.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from secrets import randbelow
|
||||
from typing import Any
|
||||
|
||||
import click
|
||||
import sqlalchemy as sa
|
||||
|
||||
from accounting import db
|
||||
from accounting.models import BaseAccount, Account, AccountL10n
|
||||
from accounting.utils.user import get_user_pk
|
||||
import sqlalchemy as sa
|
||||
|
||||
AccountData = tuple[int, str, int, str, str, str, bool]
|
||||
"""The format of the account data, as a list of (ID, base account code, number,
|
||||
@ -63,8 +63,8 @@ def init_accounts_command(username: str) -> None:
|
||||
existing_id.add(new_id)
|
||||
return new_id
|
||||
|
||||
data: list[dict[str, t.Any]] = []
|
||||
l10n_data: list[dict[str, t.Any]] = []
|
||||
data: list[dict[str, Any]] = []
|
||||
l10n_data: list[dict[str, Any]] = []
|
||||
for base in bases_to_add:
|
||||
l10n: dict[str, str] = {x.locale: x.title for x in base.l10n}
|
||||
account_id: int = get_new_id()
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
"""
|
||||
import csv
|
||||
import typing as t
|
||||
from typing import Any
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
@ -39,11 +39,11 @@ def init_currencies_command(username: str) -> None:
|
||||
return
|
||||
|
||||
creator_pk: int = get_user_pk(username)
|
||||
currency_data: list[dict[str, t.Any]] = [{"code": x["code"],
|
||||
"name_l10n": x["name"],
|
||||
"created_by_id": creator_pk,
|
||||
"updated_by_id": creator_pk}
|
||||
for x in to_add]
|
||||
currency_data: list[dict[str, Any]] = [{"code": x["code"],
|
||||
"name_l10n": x["name"],
|
||||
"created_by_id": creator_pk,
|
||||
"updated_by_id": creator_pk}
|
||||
for x in to_add]
|
||||
locales: list[str] = [x[5:] for x in to_add[0] if x.startswith("l10n-")]
|
||||
l10n_data: list[dict[str, str]] = [{"currency_code": x["code"],
|
||||
"locale": y,
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
"""
|
||||
import datetime as dt
|
||||
import typing as t
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TypeVar, Generic, Type
|
||||
|
||||
import sqlalchemy as sa
|
||||
from flask_babel import LazyString
|
||||
@ -29,13 +29,13 @@ from wtforms import DateField, FieldList, FormField, TextAreaField, \
|
||||
from wtforms.validators import DataRequired, ValidationError
|
||||
|
||||
from accounting import db
|
||||
from accounting.journal_entry.utils.account_option import AccountOption
|
||||
from accounting.journal_entry.utils.description_editor import DescriptionEditor
|
||||
from accounting.journal_entry.utils.original_line_items import \
|
||||
get_selectable_original_line_items
|
||||
from accounting.locale import lazy_gettext
|
||||
from accounting.models import JournalEntry, Account, JournalEntryLineItem, \
|
||||
JournalEntryCurrency
|
||||
from accounting.journal_entry.utils.account_option import AccountOption
|
||||
from accounting.journal_entry.utils.original_line_items import \
|
||||
get_selectable_original_line_items
|
||||
from accounting.journal_entry.utils.description_editor import DescriptionEditor
|
||||
from accounting.utils.random_id import new_id
|
||||
from accounting.utils.strip_text import strip_multiline_text
|
||||
from accounting.utils.user import get_current_user_pk
|
||||
@ -123,7 +123,7 @@ class JournalEntryForm(FlaskForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.is_modified: bool = False
|
||||
"""Whether the journal entry is modified during populate_obj()."""
|
||||
self.collector: t.Type[LineItemCollector] = LineItemCollector
|
||||
self.collector: Type[LineItemCollector] = LineItemCollector
|
||||
"""The line item collector. The default is the base abstract
|
||||
collector only to provide the correct type. The subclass forms should
|
||||
provide their own collectors."""
|
||||
@ -155,7 +155,7 @@ class JournalEntryForm(FlaskForm):
|
||||
self.__set_date(obj, self.date.data)
|
||||
obj.note = self.note.data
|
||||
|
||||
collector_cls: t.Type[LineItemCollector] = self.collector
|
||||
collector_cls: Type[LineItemCollector] = self.collector
|
||||
collector: collector_cls = collector_cls(self, obj)
|
||||
collector.collect()
|
||||
|
||||
@ -309,11 +309,11 @@ class JournalEntryForm(FlaskForm):
|
||||
return db.session.scalar(select)
|
||||
|
||||
|
||||
T = t.TypeVar("T", bound=JournalEntryForm)
|
||||
T = TypeVar("T", bound=JournalEntryForm)
|
||||
"""A journal entry form variant."""
|
||||
|
||||
|
||||
class LineItemCollector(t.Generic[T], ABC):
|
||||
class LineItemCollector(Generic[T], ABC):
|
||||
"""The line item collector."""
|
||||
|
||||
def __init__(self, form: T, obj: JournalEntry):
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
"""
|
||||
import re
|
||||
import typing as t
|
||||
from typing import Literal
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
@ -124,12 +124,12 @@ class DescriptionTag:
|
||||
class DescriptionType:
|
||||
"""A description type"""
|
||||
|
||||
def __init__(self, type_id: t.Literal["general", "travel", "bus"]):
|
||||
def __init__(self, type_id: Literal["general", "travel", "bus"]):
|
||||
"""Constructs a description type.
|
||||
|
||||
:param type_id: The type ID, either "general", "travel", or "bus".
|
||||
"""
|
||||
self.id: t.Literal["general", "travel", "bus"] = type_id
|
||||
self.id: Literal["general", "travel", "bus"] = type_id
|
||||
"""The type ID."""
|
||||
self.__tag_dict: dict[str, DescriptionTag] = {}
|
||||
"""A dictionary from the tag name to their corresponding tag."""
|
||||
@ -181,12 +181,12 @@ class DescriptionRecurring:
|
||||
class DescriptionDebitCredit:
|
||||
"""The description on debit or credit."""
|
||||
|
||||
def __init__(self, debit_credit: t.Literal["debit", "credit"]):
|
||||
def __init__(self, debit_credit: Literal["debit", "credit"]):
|
||||
"""Constructs the description on debit or credit.
|
||||
|
||||
:param debit_credit: Either "debit" or "credit".
|
||||
"""
|
||||
self.debit_credit: t.Literal["debit", "credit"] = debit_credit
|
||||
self.debit_credit: Literal["debit", "credit"] = debit_credit
|
||||
"""Either debit or credit."""
|
||||
self.general: DescriptionType = DescriptionType("general")
|
||||
"""The general tags."""
|
||||
@ -194,14 +194,14 @@ class DescriptionDebitCredit:
|
||||
"""The travel tags."""
|
||||
self.bus: DescriptionType = DescriptionType("bus")
|
||||
"""The bus tags."""
|
||||
self.__type_dict: dict[t.Literal["general", "travel", "bus"],
|
||||
self.__type_dict: dict[Literal["general", "travel", "bus"],
|
||||
DescriptionType] \
|
||||
= {x.id: x for x in {self.general, self.travel, self.bus}}
|
||||
"""A dictionary from the type ID to the corresponding tags."""
|
||||
self.recurring: list[DescriptionRecurring] = []
|
||||
"""The recurring transactions."""
|
||||
|
||||
def add_tag(self, tag_type: t.Literal["general", "travel", "bus"],
|
||||
def add_tag(self, tag_type: Literal["general", "travel", "bus"],
|
||||
name: str, account: Account, freq: int) -> None:
|
||||
"""Adds a tag.
|
||||
|
||||
@ -278,7 +278,7 @@ class DescriptionEditor:
|
||||
accounts: dict[int, Account] \
|
||||
= {x.id: x for x in Account.query
|
||||
.filter(Account.id.in_({x.account_id for x in result})).all()}
|
||||
debit_credit_dict: dict[t.Literal["debit", "credit"],
|
||||
debit_credit_dict: dict[Literal["debit", "credit"],
|
||||
DescriptionDebitCredit] \
|
||||
= {x.debit_credit: x for x in {self.debit, self.credit}}
|
||||
for row in result:
|
||||
|
@ -17,19 +17,19 @@
|
||||
"""The operators for different journal entry types.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Type
|
||||
|
||||
from flask import render_template, request, abort
|
||||
from flask_wtf import FlaskForm
|
||||
|
||||
from accounting.models import JournalEntry
|
||||
from accounting.template_globals import default_currency_code
|
||||
from accounting.utils.journal_entry_types import JournalEntryType
|
||||
from accounting.journal_entry.forms import JournalEntryForm, \
|
||||
CashReceiptJournalEntryForm, CashDisbursementJournalEntryForm, \
|
||||
TransferJournalEntryForm
|
||||
from accounting.journal_entry.forms.line_item import LineItemForm
|
||||
from accounting.models import JournalEntry
|
||||
from accounting.template_globals import default_currency_code
|
||||
from accounting.utils.journal_entry_types import JournalEntryType
|
||||
|
||||
|
||||
class JournalEntryOperator(ABC):
|
||||
@ -39,7 +39,7 @@ class JournalEntryOperator(ABC):
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def form(self) -> t.Type[JournalEntryForm]:
|
||||
def form(self) -> Type[JournalEntryForm]:
|
||||
"""Returns the form class.
|
||||
|
||||
:return: The form class.
|
||||
@ -100,7 +100,7 @@ class CashReceiptJournalEntry(JournalEntryOperator):
|
||||
"""The order when checking the journal entry operator."""
|
||||
|
||||
@property
|
||||
def form(self) -> t.Type[JournalEntryForm]:
|
||||
def form(self) -> Type[JournalEntryForm]:
|
||||
"""Returns the form class.
|
||||
|
||||
:return: The form class.
|
||||
@ -170,7 +170,7 @@ class CashDisbursementJournalEntry(JournalEntryOperator):
|
||||
"""The order when checking the journal entry operator."""
|
||||
|
||||
@property
|
||||
def form(self) -> t.Type[JournalEntryForm]:
|
||||
def form(self) -> Type[JournalEntryForm]:
|
||||
"""Returns the form class.
|
||||
|
||||
:return: The form class.
|
||||
@ -243,7 +243,7 @@ class TransferJournalEntry(JournalEntryOperator):
|
||||
"""The order when checking the journal entry operator."""
|
||||
|
||||
@property
|
||||
def form(self) -> t.Type[JournalEntryForm]:
|
||||
def form(self) -> Type[JournalEntryForm]:
|
||||
"""Returns the form class.
|
||||
|
||||
:return: The form class.
|
||||
|
@ -21,8 +21,8 @@ from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
import re
|
||||
import typing as t
|
||||
from decimal import Decimal
|
||||
from typing import Type, Annotated, Self
|
||||
|
||||
import sqlalchemy as sa
|
||||
from babel import Locale
|
||||
@ -34,16 +34,16 @@ from accounting import db
|
||||
from accounting.locale import gettext
|
||||
from accounting.utils.user import user_cls, user_pk_column
|
||||
|
||||
timestamp: t.Type[dt.datetime] \
|
||||
= t.Annotated[dt.datetime, mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())]
|
||||
timestamp: Type[dt.datetime] \
|
||||
= Annotated[dt.datetime, mapped_column(db.DateTime(timezone=True),
|
||||
server_default=db.func.now())]
|
||||
"""The timestamp."""
|
||||
user_pk: t.Type[int] \
|
||||
= t.Annotated[int, mapped_column(db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"))]
|
||||
user_pk: Type[int] \
|
||||
= Annotated[int, mapped_column(db.ForeignKey(user_pk_column,
|
||||
onupdate="CASCADE"))]
|
||||
"""The user primary key."""
|
||||
random_pk: t.Type[int] \
|
||||
= t.Annotated[int, mapped_column(primary_key=True, autoincrement=False)]
|
||||
random_pk: Type[int] \
|
||||
= Annotated[int, mapped_column(primary_key=True, autoincrement=False)]
|
||||
"""The random primary key."""
|
||||
|
||||
|
||||
@ -273,11 +273,11 @@ class Account(db.Model):
|
||||
:return: None.
|
||||
"""
|
||||
AccountL10n.query.filter(AccountL10n.account == self).delete()
|
||||
cls: t.Type[t.Self] = self.__class__
|
||||
cls: Type[Self] = self.__class__
|
||||
cls.query.filter(cls.id == self.id).delete()
|
||||
|
||||
@classmethod
|
||||
def find_by_code(cls, code: str) -> t.Self | None:
|
||||
def find_by_code(cls, code: str) -> Self | None:
|
||||
"""Finds an account by its code.
|
||||
|
||||
:param code: The code.
|
||||
@ -290,7 +290,7 @@ class Account(db.Model):
|
||||
cls.no == int(m.group(2))).first()
|
||||
|
||||
@classmethod
|
||||
def selectable_debit(cls) -> list[t.Self]:
|
||||
def selectable_debit(cls) -> list[Self]:
|
||||
"""Returns the selectable debit accounts.
|
||||
Payable line items can not start from debit.
|
||||
|
||||
@ -313,7 +313,7 @@ class Account(db.Model):
|
||||
.order_by(cls.base_code, cls.no).all()
|
||||
|
||||
@classmethod
|
||||
def selectable_credit(cls) -> list[t.Self]:
|
||||
def selectable_credit(cls) -> list[Self]:
|
||||
"""Returns the selectable debit accounts.
|
||||
Receivable line items can not start from credit.
|
||||
|
||||
@ -335,7 +335,7 @@ class Account(db.Model):
|
||||
.order_by(cls.base_code, cls.no).all()
|
||||
|
||||
@classmethod
|
||||
def cash(cls) -> t.Self:
|
||||
def cash(cls) -> Self:
|
||||
"""Returns the cash account.
|
||||
|
||||
:return: The cash account
|
||||
@ -343,7 +343,7 @@ class Account(db.Model):
|
||||
return cls.find_by_code(cls.CASH_CODE)
|
||||
|
||||
@classmethod
|
||||
def accumulated_change(cls) -> t.Self:
|
||||
def accumulated_change(cls) -> Self:
|
||||
"""Returns the accumulated-change account.
|
||||
|
||||
:return: The accumulated-change account
|
||||
@ -467,7 +467,7 @@ class Currency(db.Model):
|
||||
:return: None.
|
||||
"""
|
||||
CurrencyL10n.query.filter(CurrencyL10n.currency == self).delete()
|
||||
cls: t.Type[t.Self] = self.__class__
|
||||
cls: Type[Self] = self.__class__
|
||||
cls.query.filter(cls.code == self.code).delete()
|
||||
|
||||
|
||||
@ -797,14 +797,14 @@ class JournalEntryLineItem(db.Model):
|
||||
setattr(self, "__balance", value)
|
||||
|
||||
@property
|
||||
def offsets(self) -> list[t.Self]:
|
||||
def offsets(self) -> list[Self]:
|
||||
"""Returns the offset items.
|
||||
|
||||
:return: The offset items.
|
||||
"""
|
||||
if not hasattr(self, "__offsets"):
|
||||
cls: t.Type[t.Self] = self.__class__
|
||||
offsets: list[t.Self] = cls.query.join(JournalEntry)\
|
||||
cls: Type[Self] = self.__class__
|
||||
offsets: list[Self] = cls.query.join(JournalEntry)\
|
||||
.filter(JournalEntryLineItem.original_line_item_id == self.id)\
|
||||
.order_by(JournalEntry.date, JournalEntry.no,
|
||||
cls.is_debit, cls.no).all()
|
||||
@ -831,7 +831,7 @@ class JournalEntryLineItem(db.Model):
|
||||
setattr(self, "__is_offset", value)
|
||||
|
||||
@property
|
||||
def match(self) -> t.Self | None:
|
||||
def match(self) -> Self | None:
|
||||
"""Returns the match of the line item.
|
||||
|
||||
:return: The match of the line item.
|
||||
@ -841,7 +841,7 @@ class JournalEntryLineItem(db.Model):
|
||||
return getattr(self, "__match")
|
||||
|
||||
@match.setter
|
||||
def match(self, value: t.Self) -> None:
|
||||
def match(self, value: Self) -> None:
|
||||
"""Sets the match of the line item.
|
||||
|
||||
:param value: The matcho of the line item.
|
||||
|
@ -21,7 +21,7 @@ This file is largely taken from the NanoParma ERP project, first written in
|
||||
|
||||
"""
|
||||
import datetime as dt
|
||||
import typing as t
|
||||
from collections.abc import Callable
|
||||
|
||||
from accounting.models import JournalEntry
|
||||
from .period import Period
|
||||
@ -32,13 +32,13 @@ from .shortcuts import ThisMonth, LastMonth, SinceLastMonth, ThisYear, \
|
||||
class PeriodChooser:
|
||||
"""The period chooser."""
|
||||
|
||||
def __init__(self, get_url: t.Callable[[Period], str]):
|
||||
def __init__(self, get_url: Callable[[Period], str]):
|
||||
"""Constructs a period chooser.
|
||||
|
||||
:param get_url: The callback to return the URL of the current report in
|
||||
a period.
|
||||
"""
|
||||
self.__get_url: t.Callable[[Period], str] = get_url
|
||||
self.__get_url: Callable[[Period], str] = get_url
|
||||
"""The callback to return the URL of the current report in a period."""
|
||||
|
||||
# Shortcut periods
|
||||
|
@ -20,7 +20,8 @@
|
||||
import calendar
|
||||
import datetime as dt
|
||||
import re
|
||||
import typing as t
|
||||
from collections.abc import Callable
|
||||
from typing import Type
|
||||
|
||||
from .period import Period
|
||||
from .shortcuts import ThisMonth, LastMonth, SinceLastMonth, ThisYear, \
|
||||
@ -39,7 +40,7 @@ def get_period(spec: str | None = None) -> Period:
|
||||
"""
|
||||
if spec is None:
|
||||
return ThisMonth()
|
||||
named_periods: dict[str, t.Type[t.Callable[[], Period]]] = {
|
||||
named_periods: dict[str, Type[Callable[[], Period]]] = {
|
||||
"this-month": lambda: ThisMonth(),
|
||||
"last-month": lambda: LastMonth(),
|
||||
"since-last-month": lambda: SinceLastMonth(),
|
||||
|
@ -21,7 +21,7 @@ This file is largely taken from the NanoParma ERP project, first written in
|
||||
|
||||
"""
|
||||
import datetime as dt
|
||||
import typing as t
|
||||
from typing import Self
|
||||
|
||||
from .description import get_desc
|
||||
from .month_end import month_end
|
||||
@ -119,7 +119,7 @@ class Period:
|
||||
and not self.is_a_day
|
||||
|
||||
@property
|
||||
def before(self) -> t.Self | None:
|
||||
def before(self) -> Self | None:
|
||||
"""Returns the period before this period.
|
||||
|
||||
:return: The period before this period.
|
||||
|
@ -17,8 +17,9 @@
|
||||
"""The page parameters of a report.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Callable
|
||||
from typing import Type
|
||||
from urllib.parse import urlparse, ParseResult, parse_qsl, urlencode, \
|
||||
urlunparse
|
||||
|
||||
@ -52,7 +53,7 @@ class BasePageParams(ABC):
|
||||
"""
|
||||
|
||||
@property
|
||||
def journal_entry_types(self) -> t.Type[JournalEntryType]:
|
||||
def journal_entry_types(self) -> Type[JournalEntryType]:
|
||||
"""Returns the journal entry types.
|
||||
|
||||
:return: The journal entry types.
|
||||
@ -72,7 +73,7 @@ class BasePageParams(ABC):
|
||||
return urlunparse(parts)
|
||||
|
||||
@staticmethod
|
||||
def _get_currency_options(get_url: t.Callable[[Currency], str],
|
||||
def _get_currency_options(get_url: Callable[[Currency], str],
|
||||
active_currency: Currency) -> list[OptionLink]:
|
||||
"""Returns the currency options.
|
||||
|
||||
|
@ -21,7 +21,7 @@ This file is largely taken from the NanoParma ERP project, first written in
|
||||
|
||||
"""
|
||||
import re
|
||||
import typing as t
|
||||
from collections.abc import Iterator
|
||||
|
||||
from flask_babel import LazyString
|
||||
|
||||
@ -190,7 +190,7 @@ class ReportChooser:
|
||||
self.__active_report == ReportType.UNMATCHED,
|
||||
fa_icon="fa-solid fa-file-circle-question")
|
||||
|
||||
def __iter__(self) -> t.Iterator[OptionLink]:
|
||||
def __iter__(self) -> Iterator[OptionLink]:
|
||||
"""Returns the iteration of the reports.
|
||||
|
||||
:return: The iteration of the reports.
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
"""
|
||||
import datetime as dt
|
||||
import typing as t
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from flask_babel import get_locale
|
||||
|
||||
@ -71,7 +71,7 @@ def format_date(value: dt.date) -> str:
|
||||
return "{}/{}({})".format(value.month, value.day, weekday)
|
||||
|
||||
|
||||
def default(value: t.Any, default_value: t.Any = "") -> t.Any:
|
||||
def default(value: Any, default_value: Any = "") -> Any:
|
||||
"""Returns the default value if the given value is None.
|
||||
|
||||
:param value: The value.
|
||||
|
@ -20,10 +20,10 @@ warnings from the IDE.
|
||||
This module should not import any other module from the application.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from typing import Any
|
||||
|
||||
|
||||
def s(message: t.Any) -> str:
|
||||
def s(message: Any) -> str:
|
||||
"""Casts the LazyString message to the string type.
|
||||
|
||||
:param message: The message.
|
||||
|
@ -17,12 +17,13 @@
|
||||
"""The current assets and liabilities account.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from typing import Self
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from accounting import db
|
||||
from accounting.locale import gettext
|
||||
from accounting.models import Account
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
class CurrentAccount:
|
||||
@ -54,7 +55,7 @@ class CurrentAccount:
|
||||
return self.str
|
||||
|
||||
@classmethod
|
||||
def current_assets_and_liabilities(cls) -> t.Self:
|
||||
def current_assets_and_liabilities(cls) -> Self:
|
||||
"""Returns the pseudo account for all current assets and liabilities.
|
||||
|
||||
:return: The pseudo account for all current assets and liabilities.
|
||||
@ -67,7 +68,7 @@ class CurrentAccount:
|
||||
return account
|
||||
|
||||
@classmethod
|
||||
def accounts(cls) -> list[t.Self]:
|
||||
def accounts(cls) -> list[Self]:
|
||||
"""Returns the current assets and liabilities accounts.
|
||||
|
||||
:return: The current assets and liabilities accounts.
|
||||
|
@ -19,7 +19,7 @@
|
||||
This module should not import any other module from the application.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from typing import Any
|
||||
|
||||
from flask import flash
|
||||
from flask_wtf import FlaskForm
|
||||
@ -34,7 +34,7 @@ def flash_form_errors(form: FlaskForm) -> None:
|
||||
__flash_errors(form.errors)
|
||||
|
||||
|
||||
def __flash_errors(error: t.Any) -> None:
|
||||
def __flash_errors(error: Any) -> None:
|
||||
"""Flash all errors recursively.
|
||||
|
||||
:param error: The errors.
|
||||
|
@ -17,7 +17,7 @@
|
||||
"""The SQLAlchemy alias for the offset items.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from typing import Any
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
@ -30,10 +30,10 @@ def offset_alias() -> sa.Alias:
|
||||
:return: The SQLAlchemy alias for the offset items.
|
||||
"""
|
||||
|
||||
def as_from(model_cls: t.Any) -> sa.FromClause:
|
||||
def as_from(model_cls: Any) -> sa.FromClause:
|
||||
return model_cls
|
||||
|
||||
def as_alias(alias: t.Any) -> sa.Alias:
|
||||
def as_alias(alias: Any) -> sa.Alias:
|
||||
return alias
|
||||
|
||||
return as_alias(sa.alias(as_from(JournalEntryLineItem), name="offset"))
|
||||
|
@ -19,7 +19,7 @@
|
||||
This module should not import any other module from the application.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from typing import TypeVar, Generic
|
||||
from urllib.parse import urlparse, parse_qsl, urlencode, urlunparse, \
|
||||
ParseResult
|
||||
|
||||
@ -62,10 +62,10 @@ class Redirection(RequestRedirect):
|
||||
DEFAULT_PAGE_SIZE: int = 10
|
||||
"""The default page size."""
|
||||
|
||||
T = t.TypeVar("T")
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class Pagination(t.Generic[T]):
|
||||
class Pagination(Generic[T]):
|
||||
"""The pagination utility."""
|
||||
|
||||
def __init__(self, items: list[T], is_reversed: bool = False):
|
||||
@ -91,7 +91,7 @@ class Pagination(t.Generic[T]):
|
||||
"""The options to the number of items in a page."""
|
||||
|
||||
|
||||
class AbstractPagination(t.Generic[T]):
|
||||
class AbstractPagination(Generic[T]):
|
||||
"""An abstract pagination."""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -19,21 +19,21 @@
|
||||
This module should not import any other module from the application.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from collections.abc import Callable
|
||||
|
||||
from flask import abort, Blueprint, Response
|
||||
|
||||
from accounting.utils.user import get_current_user, UserUtilityInterface
|
||||
|
||||
|
||||
def has_permission(rule: t.Callable[[], bool]) -> t.Callable:
|
||||
def has_permission(rule: Callable[[], bool]) -> Callable:
|
||||
"""The permission decorator to check whether the current user is allowed.
|
||||
|
||||
:param rule: The permission rule.
|
||||
:return: The view decorator.
|
||||
"""
|
||||
|
||||
def decorator(view: t.Callable) -> t.Callable:
|
||||
def decorator(view: Callable) -> Callable:
|
||||
"""The view decorator to decorate a view with permission tests.
|
||||
|
||||
:param view: The view.
|
||||
@ -61,16 +61,16 @@ def has_permission(rule: t.Callable[[], bool]) -> t.Callable:
|
||||
return decorator
|
||||
|
||||
|
||||
__can_view_func: t.Callable[[], bool] = lambda: True
|
||||
__can_view_func: Callable[[], bool] = lambda: True
|
||||
"""The callback that returns whether the current user can view the accounting
|
||||
data."""
|
||||
__can_edit_func: t.Callable[[], bool] = lambda: True
|
||||
__can_edit_func: Callable[[], bool] = lambda: True
|
||||
"""The callback that returns whether the current user can edit the accounting
|
||||
data."""
|
||||
__can_admin_func: t.Callable[[], bool] = lambda: True
|
||||
__can_admin_func: Callable[[], bool] = lambda: True
|
||||
"""The callback that returns whether the current user can administrate the
|
||||
accounting settings."""
|
||||
_unauthorized_func: t.Callable[[], Response | None] \
|
||||
_unauthorized_func: Callable[[], Response | None] \
|
||||
= lambda: Response(status=403)
|
||||
"""The callback that returns the response to require the user to log in."""
|
||||
|
||||
|
@ -19,13 +19,13 @@
|
||||
This module should not import any other module from the application.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from secrets import randbelow
|
||||
from typing import Type
|
||||
|
||||
from accounting import db
|
||||
|
||||
|
||||
def new_id(cls: t.Type):
|
||||
def new_id(cls: Type):
|
||||
"""Returns a new random ID for the data model.
|
||||
|
||||
:param cls: The data model.
|
||||
|
@ -19,17 +19,17 @@
|
||||
This module should not import any other module from the application.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TypeVar, Generic, Type
|
||||
|
||||
import sqlalchemy as sa
|
||||
from flask import g, Response
|
||||
from flask_sqlalchemy.model import Model
|
||||
|
||||
T = t.TypeVar("T", bound=Model)
|
||||
T = TypeVar("T", bound=Model)
|
||||
|
||||
|
||||
class UserUtilityInterface(t.Generic[T], ABC):
|
||||
class UserUtilityInterface(Generic[T], ABC):
|
||||
"""The interface for the user utilities."""
|
||||
|
||||
@abstractmethod
|
||||
@ -72,7 +72,7 @@ class UserUtilityInterface(t.Generic[T], ABC):
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def cls(self) -> t.Type[T]:
|
||||
def cls(self) -> Type[T]:
|
||||
"""Returns the class of the user data model.
|
||||
|
||||
:return: The class of the user data model.
|
||||
@ -112,7 +112,7 @@ class UserUtilityInterface(t.Generic[T], ABC):
|
||||
|
||||
__user_utils: UserUtilityInterface
|
||||
"""The user utilities."""
|
||||
user_cls: t.Type[Model] = Model
|
||||
user_cls: Type[Model] = Model
|
||||
"""The user class."""
|
||||
user_pk_column: sa.Column = sa.Column(sa.Integer)
|
||||
"""The primary key column of the user class."""
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
"""
|
||||
import csv
|
||||
import typing as t
|
||||
import unittest
|
||||
from typing import Any
|
||||
|
||||
import sqlalchemy as sa
|
||||
from click.testing import Result
|
||||
@ -80,7 +80,7 @@ class ConsoleCommandTestCase(unittest.TestCase):
|
||||
from accounting.models import BaseAccount
|
||||
|
||||
with open(data_dir / "base_accounts.csv") as fp:
|
||||
data: dict[dict[str, t.Any]] \
|
||||
data: dict[dict[str, Any]] \
|
||||
= {x["code"]: {"code": x["code"],
|
||||
"title": x["title"],
|
||||
"l10n": {y[5:]: x[y]
|
||||
@ -135,7 +135,7 @@ class ConsoleCommandTestCase(unittest.TestCase):
|
||||
from accounting.models import Currency
|
||||
|
||||
with open(data_dir / "currencies.csv") as fp:
|
||||
data: dict[dict[str, t.Any]] \
|
||||
data: dict[dict[str, Any]] \
|
||||
= {x["code"]: {"code": x["code"],
|
||||
"name": x["name"],
|
||||
"l10n": {y[5:]: x[y]
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
"""
|
||||
import os
|
||||
import typing as t
|
||||
from secrets import token_urlsafe
|
||||
from typing import Type
|
||||
|
||||
from click.testing import Result
|
||||
from flask import Flask, Blueprint, render_template, redirect, Response, \
|
||||
@ -94,7 +94,7 @@ def create_app(is_testing: bool = False) -> Flask:
|
||||
return redirect(append_next(url_for("auth.login-form")))
|
||||
|
||||
@property
|
||||
def cls(self) -> t.Type[auth.User]:
|
||||
def cls(self) -> Type[auth.User]:
|
||||
return auth.User
|
||||
|
||||
@property
|
||||
|
@ -17,7 +17,7 @@
|
||||
"""The authentication for the Mia! Accounting demonstration website.
|
||||
|
||||
"""
|
||||
import typing as t
|
||||
from collections.abc import Callable
|
||||
|
||||
from flask import Blueprint, render_template, Flask, redirect, url_for, \
|
||||
session, request, g, Response, abort
|
||||
@ -96,7 +96,7 @@ def current_user() -> User | None:
|
||||
return g.user
|
||||
|
||||
|
||||
def admin_required(view: t.Callable) -> t.Callable:
|
||||
def admin_required(view: Callable) -> Callable:
|
||||
"""The view decorator to require the user to be an administrator.
|
||||
|
||||
:param view: The view.
|
||||
|
@ -20,10 +20,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
import typing as t
|
||||
from abc import ABC, abstractmethod
|
||||
from decimal import Decimal
|
||||
from secrets import randbelow
|
||||
from typing import Any
|
||||
|
||||
import sqlalchemy as sa
|
||||
from flask import Flask
|
||||
@ -193,8 +193,8 @@ class BaseTestData(ABC):
|
||||
.filter(User.username == username).first()
|
||||
assert current_user is not None
|
||||
self.__current_user_id: int = current_user.id
|
||||
self.__journal_entries: list[dict[str, t.Any]] = []
|
||||
self.__line_items: list[dict[str, t.Any]] = []
|
||||
self.__journal_entries: list[dict[str, Any]] = []
|
||||
self.__line_items: list[dict[str, Any]] = []
|
||||
self._init_data()
|
||||
|
||||
@abstractmethod
|
||||
@ -258,7 +258,7 @@ class BaseTestData(ABC):
|
||||
assert account is not None
|
||||
debit_no = debit_no + 1
|
||||
line_item.id = self.__new_id(existing_l_id)
|
||||
data: dict[str, t.Any] \
|
||||
data: dict[str, Any] \
|
||||
= {"id": line_item.id,
|
||||
"journal_entry_id": journal_entry_data.id,
|
||||
"is_debit": True,
|
||||
@ -277,7 +277,7 @@ class BaseTestData(ABC):
|
||||
assert account is not None
|
||||
credit_no = credit_no + 1
|
||||
line_item.id = self.__new_id(existing_l_id)
|
||||
data: dict[str, t.Any] \
|
||||
data: dict[str, Any] \
|
||||
= {"id": line_item.id,
|
||||
"journal_entry_id": journal_entry_data.id,
|
||||
"is_debit": False,
|
||||
|
@ -20,7 +20,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import typing as t
|
||||
from typing import Literal
|
||||
|
||||
import httpx
|
||||
from flask import Flask, render_template_string
|
||||
@ -108,7 +108,7 @@ def get_client(app: Flask, username: str) -> tuple[httpx.Client, str]:
|
||||
|
||||
|
||||
def set_locale(client: httpx.Client, csrf_token: str,
|
||||
locale: t.Literal["en", "zh_Hant", "zh_Hans"]) -> None:
|
||||
locale: Literal["en", "zh_Hant", "zh_Hans"]) -> None:
|
||||
"""Sets the current locale.
|
||||
|
||||
:param client: The test client.
|
||||
|
Loading…
Reference in New Issue
Block a user