diff options
| author | Dennis Fink | 2026-08-20 23:01:59 +0200 |
|---|---|---|
| committer | Dennis Fink | 2026-08-20 23:01:59 +0200 |
| commit | 5bfcc3c06ebe6d6694221c25f07a4901572f97f5 (patch) | |
| tree | fd3e9e0c8dfbde16bf96095809a765491c6d0c98 /webmentions_ssg/models.py | |
| parent | 12b8c4b7f1d14556cc459f672b501e901da2d216 (diff) | |
| download | webmentions-ssg-5bfcc3c06ebe6d6694221c25f07a4901572f97f5.tar.gz webmentions-ssg-5bfcc3c06ebe6d6694221c25f07a4901572f97f5.zip | |
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.
Diffstat (limited to '')
| -rw-r--r-- | webmentions_ssg/models.py | 99 |
1 files changed, 98 insertions, 1 deletions
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"<User {self.username}>" @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"<ReceivedWebmention {self.source!r} -> {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"<Source {self.url!r}>" + @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"<SentWebmention {self.target!r}>" + @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) |
