Release govoplan-docs v0.1.23: unify help discovery and batch semantic reads
Module Package Release / publish-packages (push) Successful in 12s

This commit is contained in:
2026-09-08 01:32:36 +02:00
parent 1ae23b4e9d
commit cbe434de02
18 changed files with 842 additions and 81 deletions
+73 -16
View File
@@ -396,13 +396,21 @@ def semantic_entry_history(
def current_revision(
session: Session,
entry: SemanticDocumentationEntry,
*,
revisions: Mapping[str, SemanticDocumentationRevision] | None = None,
) -> SemanticDocumentationRevision:
revision = (
session.get(SemanticDocumentationRevision, entry.current_revision_id)
if entry.current_revision_id
else None
)
if revision is None or revision.entry_id != entry.id:
revision = None
if entry.current_revision_id:
revision = (
revisions.get(entry.current_revision_id)
if revisions is not None
else session.get(SemanticDocumentationRevision, entry.current_revision_id)
)
if (
revision is None
or revision.entry_id != entry.id
or revision.tenant_id != entry.tenant_id
):
raise SemanticDocumentationError(
"Semantic documentation current revision is unavailable."
)
@@ -412,14 +420,60 @@ def current_revision(
def published_revision(
session: Session,
entry: SemanticDocumentationEntry,
*,
revisions: Mapping[str, SemanticDocumentationRevision] | None = None,
) -> SemanticDocumentationRevision | None:
if not entry.published_revision_id:
return None
revision = session.get(
SemanticDocumentationRevision,
entry.published_revision_id,
revision = (
revisions.get(entry.published_revision_id)
if revisions is not None
else session.get(SemanticDocumentationRevision, entry.published_revision_id)
)
return revision if revision is not None and revision.entry_id == entry.id else None
if (
revision is None
or revision.entry_id != entry.id
or revision.tenant_id != entry.tenant_id
or revision.lifecycle_state != "published"
):
return None
return revision
def prefetch_semantic_revisions(
session: Session,
principal: object,
*,
entries: Sequence[SemanticDocumentationEntry],
editor: bool,
) -> dict[str, SemanticDocumentationRevision]:
"""Load only the revisions this request may project, in bounded SQL batches.
This is request-local data loading, never an authorization cache. Payload
projection still validates each entry/revision pair and its owner subject.
Readers have no reason to load the bodies of pending unpublished drafts.
"""
tenant_id = _principal_tenant_id(principal)
revision_ids = sorted({
revision_id
for entry in entries
if entry.tenant_id == tenant_id
for revision_id in (
entry.published_revision_id,
entry.current_revision_id if editor else None,
)
if revision_id
})
revisions: dict[str, SemanticDocumentationRevision] = {}
for offset in range(0, len(revision_ids), 400):
rows = session.query(SemanticDocumentationRevision).filter(
SemanticDocumentationRevision.tenant_id == tenant_id,
SemanticDocumentationRevision.id.in_(revision_ids[offset:offset + 400]),
)
if not editor:
rows = rows.filter(SemanticDocumentationRevision.lifecycle_state == "published")
revisions.update((revision.id, revision) for revision in rows)
return revisions
def entry_subject_reference(
@@ -487,12 +541,16 @@ def semantic_entry_payload(
entry: SemanticDocumentationEntry,
editor: bool,
requested_locale: str | None = None,
revisions: Mapping[str, SemanticDocumentationRevision] | None = None,
) -> dict[str, object] | None:
current = current_revision(session, entry)
published = published_revision(session, entry)
selected = current if editor else published
if entry.tenant_id != _principal_tenant_id(principal):
return None
published = published_revision(session, entry, revisions=revisions)
selected = current_revision(session, entry, revisions=revisions) if editor else published
if selected is None:
return None
if not content_visible_to_principal(selected.content, principal):
return None
resolution = resolve_entry_subject(
session,
registry,
@@ -505,8 +563,6 @@ def semantic_entry_payload(
unavailable = resolution.availability == "temporarily_unavailable"
if unavailable and not editor:
return None
if not content_visible_to_principal(selected.content, principal):
return None
subject = resolution.subject
required_scopes = subject.required_scopes if subject is not None else ()
if any(not _principal_has(principal, scope) for scope in required_scopes):
@@ -523,7 +579,7 @@ def semantic_entry_payload(
"lifecycle_state": entry.lifecycle_state,
"effective_state": selected.lifecycle_state,
"pending_draft": bool(
published is not None and current.id != published.id
published is not None and entry.current_revision_id != published.id
),
"current_revision": entry.current_revision,
"selected_revision": selected.revision,
@@ -965,6 +1021,7 @@ __all__ = [
"entry_subject_reference",
"get_semantic_entry",
"list_semantic_entries",
"prefetch_semantic_revisions",
"publication_policy",
"publish_semantic_entry",
"published_revision",