This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new 41525a3 Get JSON from distribution platforms, and improve HTML
generation
41525a3 is described below
commit 41525a3ed930d30298141fbc237ea3047d32dba0
Author: Sean B. Palmer <[email protected]>
AuthorDate: Wed Aug 6 17:30:59 2025 +0100
Get JSON from distribution platforms, and improve HTML generation
---
atr/htm.py | 100 ++++++++++++++++++++++++++++++++++++++++++++++
atr/routes/distribute.py | 102 ++++++++++++++++++++++++++++-------------------
atr/worker.py | 1 -
3 files changed, 160 insertions(+), 43 deletions(-)
diff --git a/atr/htm.py b/atr/htm.py
new file mode 100644
index 0000000..13df5f3
--- /dev/null
+++ b/atr/htm.py
@@ -0,0 +1,100 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+import htpy
+
+if TYPE_CHECKING:
+ from collections.abc import Callable
+
+
+class BlockElementGetable:
+ def __init__(self, block: Block, element: htpy.Element):
+ self.block = block
+ self.element = element
+
+ def __getitem__(self, *items: htpy.Element | str) -> htpy.Element:
+ element = self.element[*items]
+ for i in range(len(self.block.elements) - 1, -1, -1):
+ if self.block.elements[i] is self.element:
+ self.block.elements[i] = element
+ return element
+ self.block.append(element)
+ return element
+
+
+class BlockElementCallable:
+ def __init__(self, block: Block, constructor: Callable[..., htpy.Element]):
+ self.block = block
+ self.constructor = constructor
+
+ def __call__(self, *args, **kwargs) -> BlockElementGetable:
+ element = self.constructor(*args, **kwargs)
+ self.block.append(element)
+ return BlockElementGetable(self.block, element)
+
+ def __getitem__(self, *items: Any) -> htpy.Element:
+ element = self.constructor()[*items]
+ self.block.append(element)
+ return element
+
+
+class Block:
+ def __init__(self, element: htpy.Element | None = None, *elements:
htpy.Element):
+ self.element = element
+ self.elements = list(elements)
+
+ def __str__(self) -> str:
+ return f"{self.element}{self.elements}"
+
+ def __repr__(self) -> str:
+ return f"{self.element!r}[*{self.elements!r}]"
+
+ def append(self, element: htpy.Element) -> None:
+ self.elements.append(element)
+
+ def collect(self) -> htpy.Element:
+ if self.element is None:
+ return htpy.div[*self.elements]
+ return self.element[*self.elements]
+
+ @property
+ def h1(self) -> BlockElementCallable:
+ return BlockElementCallable(self, htpy.h1)
+
+ @property
+ def h2(self) -> BlockElementCallable:
+ return BlockElementCallable(self, htpy.h2)
+
+ @property
+ def h3(self) -> BlockElementCallable:
+ return BlockElementCallable(self, htpy.h3)
+
+ @property
+ def p(self) -> BlockElementCallable:
+ return BlockElementCallable(self, htpy.p)
+
+ @property
+ def pre(self) -> BlockElementCallable:
+ return BlockElementCallable(self, htpy.pre)
+
+ @property
+ def table(self) -> BlockElementCallable:
+ return BlockElementCallable(self, htpy.table)
diff --git a/atr/routes/distribute.py b/atr/routes/distribute.py
index 6455a03..7c056d7 100644
--- a/atr/routes/distribute.py
+++ b/atr/routes/distribute.py
@@ -21,11 +21,15 @@ import dataclasses
import enum
import json
+import aiohttp
import htpy
+import pydantic
import quart
import atr.db as db
import atr.forms as forms
+import atr.htm as htm
+import atr.models.schema as schema
import atr.models.sql as sql
import atr.routes as routes
import atr.template as template
@@ -143,60 +147,74 @@ async def _distribute_page(*, project: str, version: str,
form: DistributeForm)
# if release.project.status != sql.ProjectStatus.ACTIVE:
# raise RuntimeError(f"Project {project} is not active")
form_content = forms.render_columns(form, action=quart.request.path,
descriptions=True)
- introduction = [
- htpy.p[
- "Record a manual distribution during the ",
- htpy.span(".atr-phase-three.atr-phase-label")["FINISH"],
- " phase using the form below.",
- ],
- htpy.p["Please note that this form is a work in progress and not fully
functional."],
+ block = htm.Block()
+ block.p[
+ "Record a manual distribution during the ",
+ htpy.span(".atr-phase-three.atr-phase-label")["FINISH"],
+ " phase using the form below.",
]
- content = _page("Record a manual distribution", *introduction,
form_content)
+ block.p["Please note that this form is a work in progress and not fully
functional."]
+ content = _page("Record a manual distribution", *block.elements,
form_content)
return await template.blank("Distribute", content=content)
+# Lax to ignore csrf_token and submit
+class Data(schema.Lax):
+ platform: Platform
+ owner_namespace: str | None = None
+ package: str
+ version: str
+
+ @pydantic.field_validator("owner_namespace", mode="before")
+ @classmethod
+ def empty_to_none(cls, v):
+ return None if v is None or (isinstance(v, str) and v.strip() == "")
else v
+
+
async def _distribute_post_validated(form: DistributeForm) -> str:
- data = {
- "platform": form.platform.data,
- "owner_namespace": form.owner_namespace.data,
- "package": form.package.data,
- "version": form.version.data,
- }
- table = _distribute_post_table(data)
- pre_json_results = htpy.pre[
- json.dumps({k: str(v.name if isinstance(v, enum.Enum) else v) for k, v
in data.items()}, indent=2)
- ]
- content = _page(
- "Submitted values",
- htpy.div[
- table,
- htpy.h2["As JSON"],
- pre_json_results,
- ],
- htpy.pre[
- form.platform.data.value.template_url.format(
- owner_namespace=data["owner_namespace"],
- package=data["package"],
- version=data["version"],
- ),
- ],
+ block = htm.Block()
+
+ # Submitted values
+ block.h2["Submitted values"]
+ data = Data.model_validate(form.data)
+ _distribute_post_table(block, data)
+
+ # As JSON
+ block.h2["As JSON"]
+ block.pre[data.model_dump_json(indent=2)]
+
+ # API URL
+ block.h2["API URL"]
+ api_url = form.platform.data.value.template_url.format(
+ owner_namespace=data.owner_namespace,
+ package=data.package,
+ version=data.version,
)
- return await template.blank("Distribution Submitted", content=content)
+ block.pre[api_url]
+
+ # API response
+ block.h2["API response"]
+ async with aiohttp.ClientSession() as session:
+ async with session.get(api_url) as response:
+ response.raise_for_status()
+ json_results = await response.json()
+ block.pre[json.dumps(json_results, indent=2)]
+
+ content = _page("Distribution submitted", block.collect())
+ return await template.blank("Distribution submitted", content=content)
-def _distribute_post_table(data: dict[str, str | Platform]) -> htpy.Element:
- def row(label: str, value: str | Platform) -> htpy.Element:
- if isinstance(value, Platform):
- return htpy.tr[htpy.th[label], htpy.td[value.name]]
+def _distribute_post_table(block: htm.Block, data: Data) -> None:
+ def row(label: str, value: str) -> htpy.Element:
return htpy.tr[htpy.th[label], htpy.td[value]]
tbody = htpy.tbody[
- row("Platform", data["platform"]),
- row("Owner or Namespace", data["owner_namespace"] or "(blank)"),
- row("Package", data["package"]),
- row("Version", data["version"]),
+ row("Platform", data.platform.name),
+ row("Owner or Namespace", data.owner_namespace or "(blank)"),
+ row("Package", data.package),
+ row("Version", data.version),
]
- return htpy.table(".table.table-striped.table-bordered")[tbody]
+ block.table(".table.table-striped.table-bordered")[tbody]
def _page(title_str: str, *content: htpy.Element) -> htpy.Element:
diff --git a/atr/worker.py b/atr/worker.py
index d6f9c7e..f8ee447 100644
--- a/atr/worker.py
+++ b/atr/worker.py
@@ -58,7 +58,6 @@ def main() -> None:
os.chdir(conf.STATE_DIR)
_setup_logging()
-
log.info(f"Starting worker process with pid {os.getpid()}")
tasks: list[asyncio.Task] = []
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]