Skip to content

Commit

Permalink
typing: enable explicit-override and add @typing.override
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasjuhrich committed Oct 11, 2024
1 parent 1720d11 commit 0012f09
Show file tree
Hide file tree
Showing 38 changed files with 128 additions and 0 deletions.
3 changes: 3 additions & 0 deletions hades_logs/app.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import typing as t

from celery import Celery


Expand All @@ -22,6 +24,7 @@ class HadesCelery(Celery):
set, behavior of :meth:`signature` is unchanged.
"""

@t.override
def __init__(self, *a, task_default_exchange, result_exchange, routing_key, **kw):
super().__init__(*a, **kw)
self.routing_key = routing_key
Expand Down
4 changes: 4 additions & 0 deletions ldap_sync/concepts/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""
import dataclasses
import logging
import typing as t

from . import types
from .record import Record # shadowing…
Expand All @@ -29,6 +30,7 @@ class Action:
default_factory=lambda: logging.getLogger("ldap_sync.action")
)

@t.override
def __repr__(self) -> str:
return f"<{type(self).__name__} {self.record_dn}>"

Expand All @@ -38,6 +40,7 @@ class AddAction(Action):

nonempty_attrs: types.NormalizedAttributes

@t.override
def __init__(self, record: Record) -> None:
# We don't want to add e.g. an empty `mail` field
super().__init__(record_dn=record.dn)
Expand All @@ -53,6 +56,7 @@ class ModifyAction(Action):
#: where the value is a list if the corresponding attribute is not single-valued.
modifications: types.NormalizedAttributes

@t.override
def __repr__(self) -> str:
attr_string = ", ".join(self.modifications.keys())
return f"<{type(self).__name__} {self.record_dn} [{attr_string}]>"
Expand Down
4 changes: 4 additions & 0 deletions ldap_sync/concepts/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import dataclasses
import typing
import typing as t

from ldap3.utils.conv import escape_filter_chars

Expand Down Expand Up @@ -92,18 +93,21 @@ def __init__(self, dn: DN, attrs: Attributes) -> None:
def __getitem__(self, item: str) -> typing.Any:
return self.attrs.__getitem__(item)

@t.override
def __init_subclass__(cls, **kwargs: dict[str, typing.Any]) -> None:
if "SYNCED_ATTRIBUTES" not in cls.__dict__:
raise TypeError("Subclasses of Record must implement the SYNCED_ATTRIBUTES field")
super().__init_subclass__(**kwargs)

# `__eq__` must be total, hence no type restrictions/hints
@t.override
def __eq__(self, other: object) -> bool:
try:
return self.dn == other.dn and self.attrs == other.attrs # type: ignore
except AttributeError:
return False

@t.override
def __repr__(self) -> str:
return f"<{type(self).__name__} dn={self.dn}>"

Expand Down
3 changes: 3 additions & 0 deletions pycroft/helpers/errorcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class DigitSumModNCode(ErrorCode):
def __init__(self, mod: int):
self.mod = mod

@t.override
def calculate(self, number: int) -> int:
return sum(digits(number)) % self.mod

Expand All @@ -66,9 +67,11 @@ class Mod97Code(ErrorCode):
scheme.
"""

@t.override
def calculate(self, number: int) -> int:
return 98 - (number * 100) % 97

@t.override
def is_valid(self, number: int, code: int) -> bool:
return (number * 100 + code) % 97 == 1

Expand Down
1 change: 1 addition & 0 deletions pycroft/helpers/i18n/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@


class Formattable(typing.Protocol):
@t.override
def __format__(self, format_spec: str) -> str:
...

Expand Down
8 changes: 8 additions & 0 deletions pycroft/helpers/i18n/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,16 @@ def localize(self, options: Options = None) -> str:


class ErroneousMessage(Message):
@t.override
def __init__(self, text):
super().__init__(None)
self.text = text

@t.override
def _base_dict(self):
raise AssertionError("ErroneousMessage should never be serialized")

@t.override
def _gettext(self):
return self.text

Expand All @@ -132,9 +135,11 @@ def __init__(self, message, domain=None):
super().__init__(domain)
self.message = message

@t.override
def _base_dict(self):
return {"message": self.message}

@t.override
def _gettext(self):
if self.domain:
return dgettext(self.domain, self.message)
Expand All @@ -145,15 +150,18 @@ def _gettext(self):
class NumericalMessage(Message):
__slots__ = ("singular", "plural", "n")

@t.override
def __init__(self, singular, plural, n, domain=None):
super().__init__(domain)
self.singular = singular
self.plural = plural
self.n = n

@t.override
def _base_dict(self):
return {"singular": self.singular, "plural": self.plural, "n": self.n}

@t.override
def _gettext(self):
if self.domain:
return dngettext(self.domain, self.singular, self.plural, self.n)
Expand Down
16 changes: 16 additions & 0 deletions pycroft/helpers/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,13 @@ def pg_identifier(self) -> str:
return ''
return str(self.value)

@t.override
def __new__(cls, value: TWithInfinity, is_closed: bool) -> Bound[T]:
if value is NegativeInfinity or value is PositiveInfinity:
is_closed = False
return tuple.__new__(cls, (value, is_closed))

@t.override
def __hash__(self) -> int:
return hash((self[0], self[1]))

Expand Down Expand Up @@ -122,6 +124,7 @@ def __gt__(self, other: Bound[T]) -> bool: # type: ignore
def __ge__(self, other: Bound[T]) -> bool: # type: ignore
return other <= self

@t.override
def __eq__(self, other: object) -> bool:
return (isinstance(other, Bound) and
self.value == other.value and
Expand All @@ -130,10 +133,12 @@ def __eq__(self, other: object) -> bool:
def __sub__(self, other):
return self.value - other.value

@t.override
def __repr__(self):
return "{}.{}({!r}, {!r})".format(
self.__module__, self.__class__.__name__, self.value, self.closed)

@t.override
def __str__(self):
return str(self.value)

Expand Down Expand Up @@ -201,6 +206,7 @@ def from_explicit_data(
return cls(Bound(_convert_begin(lower), lower_closed),
Bound(_convert_end(upper), upper_closed))

@t.override
def __hash__(self):
return hash((self[0], self[1]))

Expand Down Expand Up @@ -248,6 +254,7 @@ def length(self) -> t.Any: # actually return type of `T.__sub__`
"""
return None if self.unbounded else self.upper_bound - self.lower_bound

@t.override
def __eq__(self, other: object) -> bool:
return (isinstance(other, Interval) and
self.lower_bound == other.lower_bound and
Expand All @@ -267,13 +274,15 @@ def __contains__(self, point: T) -> bool: # type: ignore
bound = Bound[T](point, True)
return self.lower_bound <= bound <= self.upper_bound

@t.override
def __str__(self):
return "{}{},{}{}".format(
'[' if self.lower_bound.closed else '(',
self.lower_bound.pg_identifier, self.upper_bound.pg_identifier,
']' if self.upper_bound.closed else ')',
)

@t.override
def __repr__(self):
if self.lower_bound.closed:
if self.upper_bound.closed:
Expand Down Expand Up @@ -589,12 +598,14 @@ def __init__(
):
self._intervals = _mangle_argument(intervals)

@t.override
def __hash__(self):
return hash(self._intervals)

def __bool__(self):
return True if self._intervals else False

@t.override
def __len__(self):
return len(self._intervals)

Expand All @@ -614,22 +625,27 @@ def length(self):
(i.length for i in self._intervals)
)

@t.override
def __iter__(self) -> t.Iterator[Interval[T]]:
return iter(self._intervals)

@t.override
def __getitem__(self, item):
return self._intervals[item]

@t.override
def __eq__(self, other):
return (isinstance(other, IntervalSet) and
self._intervals == other._intervals)

@t.override
def __repr__(self):
return "{}.{}({!r})".format(
self.__module__,
self.__class__.__name__,
self._intervals)

@t.override
def __str__(self):
return "{{{0}}}".format(", ".join(str(i) for i in self._intervals))

Expand Down
1 change: 1 addition & 0 deletions pycroft/helpers/printing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Building(t.Protocol):


class Address(t.Protocol):
@t.override
def __format__(self, format_spec: str) -> str:
...

Expand Down
1 change: 1 addition & 0 deletions pycroft/lib/facilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ class RoomAddressSuggestion:
state: str
country: str

@t.override
def __str__(self) -> str:
return f"{self.street} {self.number}, {self.zip_code} {self.city}," \
+ (f" {self.state}," if self.state else "") \
Expand Down
2 changes: 2 additions & 0 deletions pycroft/lib/finance/payment_in_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ def take_actions_for_payment_in_default_users(
def get_pid_csv() -> str:
"""Generate a CSV file containing all members with negative balance
(“payment in default”)."""


from pycroft.lib.user import encode_type2_user_id

users = get_negative_members()
Expand Down
2 changes: 2 additions & 0 deletions pycroft/lib/net.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
from pycroft.model.types import IPAddress

class SubnetFullException(PycroftLibException):
@t.override
def __init__(self) -> None:
super().__init__("Subnet full")


class MacExistsException(PycroftLibException):
@t.override
def __init__(self) -> None:
super().__init__("MAC address already exists")

Expand Down
4 changes: 4 additions & 0 deletions pycroft/lib/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
class TaskImpl[TTask: Task, TParams: TaskParams](ABC):
name: str

@t.override
def __init_subclass__(cls, **kwargs: t.Any) -> None:
_abc_in_bases = any(base == ABC for base in cls.__bases__)
if not _abc_in_bases and not hasattr(cls, "name"):
Expand Down Expand Up @@ -90,6 +91,7 @@ class UserMoveOutTaskImpl(UserTaskImpl[UserMoveOutParams]):
name = "Auszug"
type = TaskType.USER_MOVE_OUT

@t.override
def _execute(self, task: UserTask, parameters: UserMoveOutParams) -> None:
from pycroft.lib import user as lib_user
if task.user.room is None:
Expand All @@ -109,6 +111,7 @@ class UserMoveTaskImpl(UserTaskImpl[UserMoveParams]):
name = "Umzug"
type = TaskType.USER_MOVE

@t.override
def _execute(self, task: UserTask, parameters: UserMoveParams) -> None:
from pycroft.lib import user as lib_user
from pycroft.lib.facilities import get_room
Expand Down Expand Up @@ -143,6 +146,7 @@ class UserMoveInTaskImpl(UserTaskImpl):
name = "Einzug"
type = TaskType.USER_MOVE_IN

@t.override
def _execute(self, task: UserTask, parameters: UserMoveInParams) -> None:
from pycroft.lib import user as lib_user
from pycroft.lib.facilities import get_room
Expand Down
8 changes: 8 additions & 0 deletions pycroft/lib/user/exc.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import typing as t

from ..exc import PycroftLibException


Expand All @@ -6,32 +8,38 @@ class HostAliasExists(ValueError):


class LoginTakenException(PycroftLibException):
@t.override
def __init__(self, login: str | None = None) -> None:
msg = "Login already taken" if not login else f"Login {login!r} already taken"
super().__init__(msg)


class EmailTakenException(PycroftLibException):
@t.override
def __init__(self) -> None:
super().__init__("E-Mail address already in use")


class UserExistsInRoomException(PycroftLibException):
@t.override
def __init__(self) -> None:
super().__init__("A user with a similar name already lives in this room")


class UserExistsException(PycroftLibException):
@t.override
def __init__(self) -> None:
super().__init__("This user already exists")


class NoTenancyForRoomException(PycroftLibException):
@t.override
def __init__(self) -> None:
super().__init__("This user has no tenancy in that room")


class MoveInDateInvalidException(PycroftLibException):
@t.override
def __init__(self) -> None:
super().__init__(
"The move-in date is invalid (in the past or more than 6 months in the future)"
Expand Down
2 changes: 2 additions & 0 deletions pycroft/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
:copyright: (c) 2011 by AG DSN.
"""
import typing as t
from datetime import timezone, tzinfo

import psycopg2.extensions
Expand Down Expand Up @@ -48,6 +49,7 @@ class UTCTZInfoCursorFactory(psycopg2.extensions.cursor):
than use a class attribute.
"""

@t.override
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tzinfo_factory = UTCTZInfoFactory
Expand Down
Loading

0 comments on commit 0012f09

Please sign in to comment.