From 5bfcc3c06ebe6d6694221c25f07a4901572f97f5 Mon Sep 17 00:00:00 2001 From: Dennis Fink Date: Thu, 20 Aug 2026 23:01:59 +0200 Subject: docs(core): add docstrings and type annotations Document forms, validators, models, and views with PEP 287-style docstrings and complete missing type annotations. Add developer-readable representations for Webmention models and mark the user password property as write-only in its return type. --- webmentions_ssg/models.py | 99 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) (limited to 'webmentions_ssg/models.py') diff --git a/webmentions_ssg/models.py b/webmentions_ssg/models.py index e7ff0be..5d22395 100644 --- a/webmentions_ssg/models.py +++ b/webmentions_ssg/models.py @@ -7,6 +7,7 @@ from __future__ import annotations import uuid from datetime import UTC, datetime from enum import StrEnum +from typing import Never from flask_login import UserMixin from sqlalchemy import ( @@ -27,12 +28,20 @@ from . import Base class SentWebmentionStatus(StrEnum): + """ + Represent the outcome of a Webmention send attempt. + """ + SENT = "sent" UNSUPPORTED = "unsupported" FAILED = "failed" class User(UserMixin, Base): + """ + Represent an authenticated application user. + """ + __tablename__ = "users" id: Mapped[int] = mapped_column(primary_key=True) @@ -40,21 +49,46 @@ class User(UserMixin, Base): password_hash: Mapped[str] = mapped_column(Text(), nullable=False) def __repr__(self) -> str: + """ + Return a developer-readable representation of the user. + + :return: Representation containing the username. + """ return f"" @property - def password(self) -> None: + def password(self) -> Never: + """ + Prevent access to the user's plain-text password. + + :raises AttributeError: Always, because the password is write-only. + """ raise AttributeError("Password is write-only") @password.setter def password(self, password: str) -> None: + """ + Hash and store a new password. + + :param password: Plain-text password to hash. + """ self.password_hash = generate_password_hash(password) def check_password(self, password: str) -> bool: + """ + Check a plain-text password against the stored password hash. + + :param password: Plain-text password to check. + :return: Whether the password matches the stored hash. + """ return check_password_hash(self.password_hash, password) class ReceivedWebmention(Base): + """ + Represent a Webmention received for verification. + """ + __tablename__ = "received_webmentions" uuid: Mapped[uuid.UUID] = mapped_column(Uuid(as_uuid=True), primary_key=True) @@ -78,16 +112,38 @@ class ReceivedWebmention(Base): UniqueConstraint("source", "target", name="uq_webmention_source_target"), ) + def __repr__(self) -> str: + """ + Return a developer-readable representation of the received Webmention. + + :return: Representation containing the identifier, source, and target. + """ + return f" {self.target!r}>" + @property def verified(self) -> bool: + """ + Return whether the Webmention has been successfully verified. + + :return: Whether the Webmention status is ``verified``. + """ return self.status == "verified" @property def created_at(self) -> datetime: + """ + Return the creation time encoded in the Webmention UUID. + + :return: Creation time as a timezone-aware datetime. + """ return uuid7_to_datetime(self.uuid) class Source(Base): + """ + Represent a generated source document containing outgoing Webmentions. + """ + __tablename__ = "sources" uuid: Mapped[uuid.UUID] = mapped_column( @@ -114,12 +170,29 @@ class Source(Base): back_populates="source", cascade="all, delete-orphan", lazy="selectin" ) + def __repr__(self) -> str: + """ + Return a developer-readable representation of the source. + + :return: Representation containing the identifier and public URL. + """ + return f"" + @property def created_at(self) -> datetime: + """ + Return the creation time encoded in the source UUID. + + :return: Creation time as a timezone-aware datetime. + """ return uuid7_to_datetime(self.uuid) class SentWebmention(Base): + """ + Represent an outgoing Webmention associated with a source. + """ + __tablename__ = "sent_webmentions" uuid: Mapped[uuid.UUID] = mapped_column( @@ -181,8 +254,21 @@ class SentWebmention(Base): ), ) + def __repr__(self) -> str: + """ + Return a developer-readable representation of the sent Webmention. + + :return: Representation containing the identifier and target URL. + """ + return f"" + @property def pending(self) -> bool: + """ + Return whether the Webmention has an unprocessed source revision. + + :return: Whether the desired revision still requires processing. + """ return ( self.processed_revision is None or self.processed_revision < self.desired_revision @@ -190,8 +276,19 @@ class SentWebmention(Base): @property def created_at(self) -> datetime: + """ + Return the creation time encoded in the Webmention UUID. + + :return: Creation time as a timezone-aware datetime. + """ return uuid7_to_datetime(self.uuid) def uuid7_to_datetime(identifier: uuid.UUID) -> datetime: + """ + Convert a UUID version 7 timestamp to a datetime. + + :param identifier: UUID whose embedded timestamp should be converted. + :return: Timestamp as a timezone-aware UTC datetime. + """ return datetime.fromtimestamp(identifier.time / 1000, tz=UTC) -- cgit v1.3.1