Refine Python tools task interface
Build custom container images / build (map[base_image:php:8-fpm-alpine build_args:PHP_VERSION=8
context:php8-pgsql fingerprint_command:{ apk info -v | LC_ALL=C sort; find /usr/local/lib/php/extensions /usr/local/etc/php/conf.d -type f -exec sha256sum {} + | LC_ALL=C sort; }
name:ph… (push) Successful in 39s
Build custom container images / build (map[base_image:postgres:18 build_args:PG_VERSION=18
POSTGIS_VERSION=3
VCHORD_VERSION=0.5.3
context:postgres fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; find /usr/lib/postgresql -type f -exec sha256su… (push) Successful in 53s
Build custom container images / build (map[base_image:python:3 build_args:PYTHON_VERSION=3
context:python-tools fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; }
name:python-tools oci_labels:org.opencontainers.ima… (push) Successful in 1m17s
Build custom container images / build (map[base_image:php:8-fpm-alpine build_args:PHP_VERSION=8
context:php8-pgsql fingerprint_command:{ apk info -v | LC_ALL=C sort; find /usr/local/lib/php/extensions /usr/local/etc/php/conf.d -type f -exec sha256sum {} + | LC_ALL=C sort; }
name:ph… (push) Successful in 39s
Build custom container images / build (map[base_image:postgres:18 build_args:PG_VERSION=18
POSTGIS_VERSION=3
VCHORD_VERSION=0.5.3
context:postgres fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; find /usr/lib/postgresql -type f -exec sha256su… (push) Successful in 53s
Build custom container images / build (map[base_image:python:3 build_args:PYTHON_VERSION=3
context:python-tools fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; }
name:python-tools oci_labels:org.opencontainers.ima… (push) Successful in 1m17s
This commit is contained in:
@@ -117,7 +117,12 @@ class TaskCatalog:
|
||||
group_path,
|
||||
{
|
||||
path.name for path in group_path.iterdir()
|
||||
if path.is_file() and not path.is_symlink() and path.suffix == ".py" and not path.name.startswith((".", "_"))
|
||||
if (
|
||||
path.is_file()
|
||||
and not path.is_symlink()
|
||||
and path.suffix == ".py"
|
||||
and not path.name.startswith((".", "_"))
|
||||
)
|
||||
},
|
||||
)
|
||||
tasks = tuple(self._tasks_in_group(group_path, manifest.declarations))
|
||||
@@ -125,7 +130,7 @@ class TaskCatalog:
|
||||
Group(
|
||||
id=group_path.name,
|
||||
path=group_path,
|
||||
display_name=manifest.label or display_name(group_path.name),
|
||||
display_name=manifest.title or display_name(group_path.name),
|
||||
tasks=tasks,
|
||||
)
|
||||
)
|
||||
@@ -191,8 +196,12 @@ class TaskCatalog:
|
||||
group_id=group_path.name,
|
||||
filename=path.name,
|
||||
path=path,
|
||||
display_name=display_name(path.stem),
|
||||
description=read_description(path),
|
||||
display_name=declaration.title if declaration and declaration.title else display_name(path.stem),
|
||||
description=(
|
||||
declaration.description
|
||||
if declaration and declaration.description is not None
|
||||
else read_description(path)
|
||||
),
|
||||
inputs=(
|
||||
(*declaration.inputs, declaration.execution.field)
|
||||
if declaration and declaration.execution
|
||||
@@ -287,7 +296,7 @@ def read_description(path: Path) -> str | None:
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GroupManifest:
|
||||
label: str | None
|
||||
title: str | None
|
||||
declarations: dict[str, "TaskDeclaration"]
|
||||
|
||||
|
||||
@@ -299,22 +308,22 @@ def load_input_manifest(
|
||||
|
||||
manifest_path = group_path / "task-inputs.json"
|
||||
if not manifest_path.exists():
|
||||
return GroupManifest(label=None, declarations={})
|
||||
return GroupManifest(title=None, declarations={})
|
||||
if manifest_path.is_symlink():
|
||||
raise ValueError(f"Input manifest cannot be a symlink: {manifest_path}")
|
||||
try:
|
||||
data = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as error:
|
||||
raise ValueError(f"Could not read input manifest {manifest_path}: {error}") from error
|
||||
if not isinstance(data, dict) or set(data) - {"version", "label", "tasks"}:
|
||||
if not isinstance(data, dict) or set(data) - {"version", "title", "tasks"}:
|
||||
raise ValueError(
|
||||
f"Input manifest {manifest_path} has an invalid structure."
|
||||
)
|
||||
if data.get("version") != 1 or not isinstance(data.get("tasks"), dict):
|
||||
raise ValueError(f"Input manifest {manifest_path} has an unsupported structure.")
|
||||
label = data.get("label")
|
||||
if label is not None and (not isinstance(label, str) or not label.strip()):
|
||||
raise ValueError(f"Input manifest {manifest_path} label must be non-empty text.")
|
||||
title = data.get("title")
|
||||
if title is not None and (not isinstance(title, str) or not title.strip()):
|
||||
raise ValueError(f"Input manifest {manifest_path} title must be non-empty text.")
|
||||
|
||||
unknown_tasks = set(data["tasks"]) - task_filenames
|
||||
if unknown_tasks:
|
||||
@@ -322,7 +331,7 @@ def load_input_manifest(
|
||||
raise ValueError(f"Input manifest {manifest_path} names unknown tasks: {names}.")
|
||||
|
||||
return GroupManifest(
|
||||
label=label,
|
||||
title=title,
|
||||
declarations={
|
||||
filename: parse_task_declaration(manifest_path, filename, declaration)
|
||||
for filename, declaration in data["tasks"].items()
|
||||
@@ -332,6 +341,8 @@ def load_input_manifest(
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TaskDeclaration:
|
||||
title: str | None
|
||||
description: str | None
|
||||
inputs: tuple[InputField, ...]
|
||||
wait_for_result: bool
|
||||
download_artifacts: bool
|
||||
@@ -345,13 +356,19 @@ def parse_task_declaration(
|
||||
) -> TaskDeclaration:
|
||||
if (
|
||||
not isinstance(declaration, dict)
|
||||
or set(declaration) - {"inputs", "wait_for_result", "download_artifacts", "execution"}
|
||||
or set(declaration) - {"title", "description", "inputs", "wait_for_result", "download_artifacts", "execution"}
|
||||
):
|
||||
raise ValueError(f"{manifest_path} task {filename} has an invalid declaration.")
|
||||
raw_inputs = declaration.get("inputs", [])
|
||||
if not isinstance(raw_inputs, list):
|
||||
raise ValueError(f"{manifest_path} task {filename} inputs must be a list.")
|
||||
fields = tuple(parse_input_field(manifest_path, filename, raw) for raw in raw_inputs)
|
||||
title = declaration.get("title")
|
||||
if title is not None and (not isinstance(title, str) or not title.strip()):
|
||||
raise ValueError(f"{manifest_path} task {filename} title must be non-empty text.")
|
||||
description = declaration.get("description")
|
||||
if description is not None and (not isinstance(description, str) or not description.strip()):
|
||||
raise ValueError(f"{manifest_path} task {filename} description must be non-empty text.")
|
||||
names = [field.name for field in fields]
|
||||
if len(names) != len(set(names)):
|
||||
raise ValueError(f"{manifest_path} task {filename} has duplicate input names.")
|
||||
@@ -365,6 +382,8 @@ def parse_task_declaration(
|
||||
if download_artifacts and not wait_for_result:
|
||||
raise ValueError(f"{manifest_path} task {filename} download_artifacts requires wait_for_result.")
|
||||
return TaskDeclaration(
|
||||
title=title,
|
||||
description=description,
|
||||
inputs=fields,
|
||||
wait_for_result=wait_for_result,
|
||||
download_artifacts=download_artifacts,
|
||||
@@ -381,7 +400,7 @@ def parse_execution_mode(
|
||||
if raw is None:
|
||||
return None
|
||||
required_keys = {
|
||||
"field", "label", "dry_run_value", "dry_run_label", "execute_value", "execute_label",
|
||||
"field", "dry_run_value", "dry_run_label", "execute_value", "execute_label",
|
||||
}
|
||||
if not isinstance(raw, dict) or set(raw) != required_keys:
|
||||
raise ValueError(f"{manifest_path} task {filename} has an invalid execution declaration.")
|
||||
@@ -395,7 +414,7 @@ def parse_execution_mode(
|
||||
raise ValueError(f"{manifest_path} task {filename} execution values must differ.")
|
||||
field = InputField(
|
||||
name=field_name,
|
||||
label=values["label"],
|
||||
label="Run mode",
|
||||
type="choice",
|
||||
required=True,
|
||||
default=values["dry_run_value"],
|
||||
|
||||
Reference in New Issue
Block a user