Update Python tools
This commit is contained in:
@@ -11,7 +11,7 @@ from typing import Any
|
||||
|
||||
|
||||
FIELD_NAME = re.compile(r"[a-z][a-z0-9_]*\Z")
|
||||
FIELD_TYPES = frozenset({"text", "integer", "date", "datetime", "choice", "multi_choice"})
|
||||
FIELD_TYPES = frozenset({"text", "integer", "date", "datetime", "choice", "multi_choice", "file"})
|
||||
|
||||
|
||||
def display_name(value: str) -> str:
|
||||
@@ -28,6 +28,7 @@ class Task:
|
||||
description: str | None
|
||||
inputs: tuple["InputField", ...]
|
||||
wait_for_result: bool = False
|
||||
download_artifacts: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -58,6 +59,8 @@ class InputField:
|
||||
step: int | None
|
||||
pattern: str | None
|
||||
options: tuple[InputOption, ...]
|
||||
accept: tuple[str, ...]
|
||||
maximum_bytes: int | None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -154,6 +157,7 @@ class TaskCatalog:
|
||||
description=read_description(path),
|
||||
inputs=declaration.inputs if declaration else (),
|
||||
wait_for_result=declaration.wait_for_result if declaration else False,
|
||||
download_artifacts=declaration.download_artifacts if declaration else False,
|
||||
)
|
||||
)
|
||||
return tasks
|
||||
@@ -275,6 +279,7 @@ def load_input_manifest(
|
||||
class TaskDeclaration:
|
||||
inputs: tuple[InputField, ...]
|
||||
wait_for_result: bool
|
||||
download_artifacts: bool
|
||||
|
||||
|
||||
def parse_task_declaration(
|
||||
@@ -282,7 +287,11 @@ def parse_task_declaration(
|
||||
filename: str,
|
||||
declaration: Any,
|
||||
) -> TaskDeclaration:
|
||||
if not isinstance(declaration, dict) or not {"inputs"} <= set(declaration) or set(declaration) - {"inputs", "wait_for_result"}:
|
||||
if (
|
||||
not isinstance(declaration, dict)
|
||||
or not {"inputs"} <= set(declaration)
|
||||
or set(declaration) - {"inputs", "wait_for_result", "download_artifacts"}
|
||||
):
|
||||
raise ValueError(f"{manifest_path} task {filename} has an invalid declaration.")
|
||||
raw_inputs = declaration["inputs"]
|
||||
if not isinstance(raw_inputs, list):
|
||||
@@ -294,7 +303,16 @@ def parse_task_declaration(
|
||||
wait_for_result = declaration.get("wait_for_result", False)
|
||||
if not isinstance(wait_for_result, bool):
|
||||
raise ValueError(f"{manifest_path} task {filename} wait_for_result must be true or false.")
|
||||
return TaskDeclaration(inputs=fields, wait_for_result=wait_for_result)
|
||||
download_artifacts = declaration.get("download_artifacts", False)
|
||||
if not isinstance(download_artifacts, bool):
|
||||
raise ValueError(f"{manifest_path} task {filename} download_artifacts must be true or false.")
|
||||
if download_artifacts and not wait_for_result:
|
||||
raise ValueError(f"{manifest_path} task {filename} download_artifacts requires wait_for_result.")
|
||||
return TaskDeclaration(
|
||||
inputs=fields,
|
||||
wait_for_result=wait_for_result,
|
||||
download_artifacts=download_artifacts,
|
||||
)
|
||||
|
||||
|
||||
def parse_input_field(manifest_path: Path, filename: str, raw: Any) -> InputField:
|
||||
@@ -302,7 +320,7 @@ def parse_input_field(manifest_path: Path, filename: str, raw: Any) -> InputFiel
|
||||
raise ValueError(f"{manifest_path} task {filename} has a non-object input field.")
|
||||
allowed = {
|
||||
"name", "label", "type", "required", "default", "minimum", "maximum",
|
||||
"step", "pattern", "options",
|
||||
"step", "pattern", "options", "accept", "maximum_bytes",
|
||||
}
|
||||
unknown = set(raw) - allowed
|
||||
if unknown:
|
||||
@@ -344,6 +362,20 @@ def parse_input_field(manifest_path: Path, filename: str, raw: Any) -> InputFiel
|
||||
except re.error as error:
|
||||
raise ValueError(f"{manifest_path} task {filename} input {name} has an invalid pattern: {error}.") from error
|
||||
|
||||
accept = parse_file_accept(manifest_path, filename, name, field_type, raw.get("accept"))
|
||||
maximum_bytes = raw.get("maximum_bytes")
|
||||
if field_type == "file":
|
||||
if pattern is not None or raw.get("default") is not None:
|
||||
raise ValueError(f"{manifest_path} task {filename} file input {name} cannot define a pattern or default.")
|
||||
if maximum_bytes is not None and (
|
||||
not isinstance(maximum_bytes, int)
|
||||
or isinstance(maximum_bytes, bool)
|
||||
or maximum_bytes < 1
|
||||
):
|
||||
raise ValueError(f"{manifest_path} task {filename} file input {name} maximum_bytes must be positive.")
|
||||
elif maximum_bytes is not None:
|
||||
raise ValueError(f"{manifest_path} task {filename} input {name} only file fields accept maximum_bytes.")
|
||||
|
||||
options = parse_options(manifest_path, filename, name, field_type, raw.get("options"))
|
||||
default = parse_default(manifest_path, filename, name, field_type, options, raw.get("default"))
|
||||
validate_default(
|
||||
@@ -368,9 +400,34 @@ def parse_input_field(manifest_path: Path, filename: str, raw: Any) -> InputFiel
|
||||
step=numeric_values["step"],
|
||||
pattern=pattern,
|
||||
options=options,
|
||||
accept=accept,
|
||||
maximum_bytes=maximum_bytes,
|
||||
)
|
||||
|
||||
|
||||
def parse_file_accept(
|
||||
manifest_path: Path,
|
||||
filename: str,
|
||||
name: str,
|
||||
field_type: str,
|
||||
raw_accept: Any,
|
||||
) -> tuple[str, ...]:
|
||||
if field_type != "file":
|
||||
if raw_accept is not None:
|
||||
raise ValueError(f"{manifest_path} task {filename} input {name} only file fields accept accept.")
|
||||
return ()
|
||||
if raw_accept is None:
|
||||
return ()
|
||||
if (
|
||||
not isinstance(raw_accept, list)
|
||||
or not raw_accept
|
||||
or not all(isinstance(value, str) and re.fullmatch(r"\.[a-z0-9]{1,10}", value) for value in raw_accept)
|
||||
or len(set(raw_accept)) != len(raw_accept)
|
||||
):
|
||||
raise ValueError(f"{manifest_path} task {filename} file input {name} has invalid accept values.")
|
||||
return tuple(raw_accept)
|
||||
|
||||
|
||||
def parse_options(
|
||||
manifest_path: Path,
|
||||
filename: str,
|
||||
|
||||
Reference in New Issue
Block a user