pypeline-runner 1.11.0__py3-none-any.whl → 1.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pypeline/__init__.py +1 -1
- pypeline/domain/config.py +14 -1
- pypeline/domain/execution_context.py +3 -1
- pypeline/domain/project_slurper.py +4 -0
- pypeline/inputs_parser.py +88 -0
- pypeline/kickstart/templates/project/pypeline.yaml +6 -0
- pypeline/main.py +16 -2
- {pypeline_runner-1.11.0.dist-info → pypeline_runner-1.12.0.dist-info}/METADATA +1 -1
- {pypeline_runner-1.11.0.dist-info → pypeline_runner-1.12.0.dist-info}/RECORD +12 -11
- {pypeline_runner-1.11.0.dist-info → pypeline_runner-1.12.0.dist-info}/LICENSE +0 -0
- {pypeline_runner-1.11.0.dist-info → pypeline_runner-1.12.0.dist-info}/WHEEL +0 -0
- {pypeline_runner-1.11.0.dist-info → pypeline_runner-1.12.0.dist-info}/entry_points.txt +0 -0
pypeline/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.
|
|
1
|
+
__version__ = "1.12.0"
|
pypeline/domain/config.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Any, Dict, Optional
|
|
3
|
+
from typing import Any, Dict, Literal, Optional
|
|
4
4
|
|
|
5
5
|
import yaml
|
|
6
6
|
from mashumaro import DataClassDictMixin
|
|
@@ -10,10 +10,23 @@ from yaml.scanner import ScannerError
|
|
|
10
10
|
|
|
11
11
|
from .pipeline import PipelineConfig
|
|
12
12
|
|
|
13
|
+
InputType = Literal["string", "integer", "boolean"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class ProjectInput(DataClassDictMixin):
|
|
18
|
+
"""Represents a single input parameter for a pipeline step similar to GitHub workflows inputs."""
|
|
19
|
+
|
|
20
|
+
type: InputType
|
|
21
|
+
description: Optional[str] = None
|
|
22
|
+
default: Optional[Any] = None
|
|
23
|
+
required: bool = False
|
|
24
|
+
|
|
13
25
|
|
|
14
26
|
@dataclass
|
|
15
27
|
class ProjectConfig(DataClassDictMixin):
|
|
16
28
|
pipeline: PipelineConfig
|
|
29
|
+
inputs: Optional[Dict[str, ProjectInput]] = None
|
|
17
30
|
# This field is intended to keep track of where configuration was loaded from and
|
|
18
31
|
# it is automatically added when configuration is loaded from file
|
|
19
32
|
file: Optional[Path] = None
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import List, Optional
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
5
|
|
|
6
6
|
from py_app_dev.core.data_registry import DataRegistry
|
|
7
7
|
from py_app_dev.core.subprocess import SubprocessExecutor
|
|
@@ -16,6 +16,8 @@ class ExecutionContext:
|
|
|
16
16
|
install_dirs: List[Path] = field(default_factory=list)
|
|
17
17
|
# Data registry to exchange data of any type
|
|
18
18
|
data_registry: DataRegistry = field(default_factory=DataRegistry)
|
|
19
|
+
# Inputs provided for the pipeline run
|
|
20
|
+
inputs: Dict[str, Any] = field(default_factory=dict)
|
|
19
21
|
|
|
20
22
|
def add_install_dirs(self, install_dirs: List[Path]) -> None:
|
|
21
23
|
self.install_dirs.extend(install_dirs)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import distutils.util
|
|
3
|
+
from typing import Any, Dict, List
|
|
4
|
+
|
|
5
|
+
from py_app_dev.core.exceptions import UserNotificationException
|
|
6
|
+
|
|
7
|
+
from .domain.config import InputType, ProjectInput
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _map_type_for_argparse(input_type: InputType) -> Any:
|
|
11
|
+
if input_type == "string":
|
|
12
|
+
return str
|
|
13
|
+
elif input_type == "integer":
|
|
14
|
+
return int
|
|
15
|
+
elif input_type == "boolean":
|
|
16
|
+
return lambda x: bool(distutils.util.strtobool(x))
|
|
17
|
+
else:
|
|
18
|
+
raise ValueError(f"Unsupported input type specified: {input_type}")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_argument_parser_from_definitions(
|
|
22
|
+
input_definitions: Dict[str, ProjectInput],
|
|
23
|
+
description: str = "Pypeline inputs parser.",
|
|
24
|
+
) -> argparse.ArgumentParser:
|
|
25
|
+
"""Creates and configures an ArgumentParser based on input definitions."""
|
|
26
|
+
parser = argparse.ArgumentParser(
|
|
27
|
+
description=description,
|
|
28
|
+
exit_on_error=False,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
for name, definition in input_definitions.items():
|
|
32
|
+
arg_type = _map_type_for_argparse(definition.type)
|
|
33
|
+
|
|
34
|
+
help_text = definition.description or f"Input parameter '{name}'."
|
|
35
|
+
help_text += f" (Type: {definition.type}"
|
|
36
|
+
if definition.default is not None:
|
|
37
|
+
help_text += f", Default: {definition.default}"
|
|
38
|
+
help_text += ")"
|
|
39
|
+
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
f"--{name}",
|
|
42
|
+
dest=name, # Attribute name in the parsed namespace
|
|
43
|
+
help=help_text,
|
|
44
|
+
type=arg_type, # Type conversion function
|
|
45
|
+
required=definition.required,
|
|
46
|
+
default=definition.default,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return parser
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class InputsParser:
|
|
53
|
+
"""Parses input arguments based on definitions using argparse."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, parser: argparse.ArgumentParser) -> None:
|
|
56
|
+
self.parser = parser
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_inputs_definitions(
|
|
60
|
+
cls,
|
|
61
|
+
input_definitions: Dict[str, ProjectInput],
|
|
62
|
+
description: str = "Pypeline inputs parser.",
|
|
63
|
+
) -> "InputsParser":
|
|
64
|
+
"""Factory method to create an InputsParser instance from input definitions."""
|
|
65
|
+
return cls(create_argument_parser_from_definitions(input_definitions, description))
|
|
66
|
+
|
|
67
|
+
def parse_inputs(self, inputs: List[str]) -> Dict[str, Any]:
|
|
68
|
+
"""Parses and validates the provided input strings against the configured parser. Inputs are expected as a list of 'name=value' elements."""
|
|
69
|
+
try:
|
|
70
|
+
args = []
|
|
71
|
+
for item in inputs:
|
|
72
|
+
if "=" not in item:
|
|
73
|
+
raise UserNotificationException(f"Invalid input format: '{item}', expected 'name=value'")
|
|
74
|
+
name, value = item.split("=", 1)
|
|
75
|
+
args.append(f"--{name}")
|
|
76
|
+
args.append(value)
|
|
77
|
+
|
|
78
|
+
parsed_namespace = self.parser.parse_args(args)
|
|
79
|
+
return vars(parsed_namespace)
|
|
80
|
+
except argparse.ArgumentError as e:
|
|
81
|
+
error_message = f"Input validation error: {e}"
|
|
82
|
+
raise UserNotificationException(error_message) from e
|
|
83
|
+
except SystemExit as e:
|
|
84
|
+
error_message = f"Input parsing failed: {e}"
|
|
85
|
+
raise UserNotificationException(error_message) from e
|
|
86
|
+
except Exception as e:
|
|
87
|
+
error_message = f"An unexpected error occurred during input parsing: {e}"
|
|
88
|
+
raise UserNotificationException(error_message) from e
|
pypeline/main.py
CHANGED
|
@@ -10,6 +10,7 @@ from pypeline import __version__
|
|
|
10
10
|
from pypeline.domain.execution_context import ExecutionContext
|
|
11
11
|
from pypeline.domain.pipeline import PipelineConfigIterator
|
|
12
12
|
from pypeline.domain.project_slurper import ProjectSlurper
|
|
13
|
+
from pypeline.inputs_parser import InputsParser
|
|
13
14
|
from pypeline.kickstart.create import KickstartProject
|
|
14
15
|
from pypeline.pypeline import PipelineScheduler, PipelineStepsExecutor
|
|
15
16
|
|
|
@@ -48,6 +49,12 @@ def run(
|
|
|
48
49
|
print: bool = typer.Option(False, help="Print the pipeline steps."),
|
|
49
50
|
force_run: bool = typer.Option(False, help="Force the execution of a step even if it is not dirty."),
|
|
50
51
|
dry_run: bool = typer.Option(False, help="Do not run any step, just print the steps that would be executed."),
|
|
52
|
+
inputs: Optional[List[str]] = typer.Option( # noqa: B008
|
|
53
|
+
None,
|
|
54
|
+
"--input",
|
|
55
|
+
"-i",
|
|
56
|
+
help="Provide input parameters as key=value pairs (e.g., -i name=value -i flag=true).",
|
|
57
|
+
),
|
|
51
58
|
) -> None:
|
|
52
59
|
project_slurper = ProjectSlurper(project_dir, config_file)
|
|
53
60
|
if print:
|
|
@@ -65,8 +72,15 @@ def run(
|
|
|
65
72
|
if not steps_references:
|
|
66
73
|
logger.info("No steps to run.")
|
|
67
74
|
return
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
# Parse the inputs
|
|
76
|
+
input_definitions = project_slurper.project_config.inputs
|
|
77
|
+
if input_definitions is None and inputs:
|
|
78
|
+
raise UserNotificationException(f"Inputs are not accepted because there are no inputs defined in the '{project_slurper.project_config.file}' configuration.")
|
|
79
|
+
if input_definitions and inputs:
|
|
80
|
+
inputs_dict = InputsParser.from_inputs_definitions(input_definitions).parse_inputs(inputs)
|
|
81
|
+
else:
|
|
82
|
+
inputs_dict = {}
|
|
83
|
+
PipelineStepsExecutor[ExecutionContext](ExecutionContext(project_dir, inputs=inputs_dict), steps_references, force_run, dry_run).run()
|
|
70
84
|
|
|
71
85
|
|
|
72
86
|
def main() -> None:
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
pypeline/__init__.py,sha256=
|
|
1
|
+
pypeline/__init__.py,sha256=ojUczZNOmgkgAYXRdDJ_bo8gdfmTPU5zTYbNMYMWPLg,23
|
|
2
2
|
pypeline/__run.py,sha256=TCdaX05Qm3g8T4QYryKB25Xxf0L5Km7hFOHe1mK9vI0,350
|
|
3
3
|
pypeline/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
pypeline/domain/artifacts.py,sha256=5k7cVfHhLmvWXNuHKxXb9ca4Lxu0JytGQqazENCeKEU,1404
|
|
5
|
-
pypeline/domain/config.py,sha256=
|
|
6
|
-
pypeline/domain/execution_context.py,sha256
|
|
5
|
+
pypeline/domain/config.py,sha256=6vWdHi7B6MA7NGi9wWXQE-YhSg1COSRmc3b1ji6AdAk,2053
|
|
6
|
+
pypeline/domain/execution_context.py,sha256=-YErMvP5jdXHqJnPCD5ePoU2XMhdp-Zpr4eJaRPKXYY,1537
|
|
7
7
|
pypeline/domain/pipeline.py,sha256=2BsN2lw2znUxLH--Novyqe6SubVKs6XeHQSQf9yxirw,7788
|
|
8
|
-
pypeline/domain/project_slurper.py,sha256=
|
|
8
|
+
pypeline/domain/project_slurper.py,sha256=64aqgVsrLgAK-c5QOM2N0wGCkOM1uNMio8yKjO2zDLU,1069
|
|
9
|
+
pypeline/inputs_parser.py,sha256=8WJnWbACNmw_iYLhbAwcgGohCZWult8HtiJC9KiG3R0,3389
|
|
9
10
|
pypeline/kickstart/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
11
|
pypeline/kickstart/create.py,sha256=iaB8MMC7PinpPBwRmz3rWZuE-DRbsLh2NtvczYaVgi0,2133
|
|
11
12
|
pypeline/kickstart/templates/project/.gitignore,sha256=y8GJoVvRPez1LBokf1NaDOt2X1XtGwKFMF5yjA8AVS0,24
|
|
@@ -13,12 +14,12 @@ pypeline/kickstart/templates/project/bootstrap.ps1,sha256=eR8cyIJwDVt-bA2H3GWmxU
|
|
|
13
14
|
pypeline/kickstart/templates/project/bootstrap.py,sha256=9cJp_sbU0SKvDjJluvyQfh0_xIsf6E1ct7sa7rRecNU,17244
|
|
14
15
|
pypeline/kickstart/templates/project/poetry.toml,sha256=qgVxBdPcJZOHdHCTOBoZYna3cke4VGgRkNZ0bKgN6rs,32
|
|
15
16
|
pypeline/kickstart/templates/project/pypeline.ps1,sha256=PjCJULG8XA3AHKbNt3oHrIgD04huvvpIue_gjSo3PMA,104
|
|
16
|
-
pypeline/kickstart/templates/project/pypeline.yaml,sha256=
|
|
17
|
+
pypeline/kickstart/templates/project/pypeline.yaml,sha256=dyzn8vmHugpINVyB9l0QujK0J-oi--JQXunjUj9n3_k,385
|
|
17
18
|
pypeline/kickstart/templates/project/pyproject.toml,sha256=A60HZ6Aqf8KTFLoC35SexuJ2Ze1I-khuYmYUKhphfNY,247
|
|
18
19
|
pypeline/kickstart/templates/project/scoopfile.json,sha256=DcfZ8jYf9hmPHM-AWwnPKQJCzRG3fCuYtMeoY01nkag,219
|
|
19
20
|
pypeline/kickstart/templates/project/steps/my_step.py,sha256=iZYTzWtL-qxEW_t7q079d-xpnRST_tumSzxqiQDW7sM,707
|
|
20
21
|
pypeline/kickstart/templates/project/west.yaml,sha256=ZfVym7M4yzzC-Nm0vESdhqNYs6EaJuMQWGJBht_i0b4,188
|
|
21
|
-
pypeline/main.py,sha256=
|
|
22
|
+
pypeline/main.py,sha256=zkDqmYuTjMuYLsKdAHO-8RiEXUAgNLWoS3pbFbIp0Eg,4070
|
|
22
23
|
pypeline/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
24
|
pypeline/pypeline.py,sha256=-mquLfFlEvESk-HORhvjRMESIzdlVAgBLPjwUDOPLqg,7452
|
|
24
25
|
pypeline/steps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -26,8 +27,8 @@ pypeline/steps/create_venv.py,sha256=sajEl43Yqcd1f0BN9lZOhm93UIRXj4Qx4TdAOSxLkTU
|
|
|
26
27
|
pypeline/steps/env_setup_script.py,sha256=j6wrrnZSXXu1fq01zUdgiFBTTlpR4XXl_uMWwVCcQSI,2221
|
|
27
28
|
pypeline/steps/scoop_install.py,sha256=TIrU2hPCS3SzHc2_L2A2EgGFkTQIGdrconDUeNkMx64,3484
|
|
28
29
|
pypeline/steps/west_install.py,sha256=hPyr28ksdKsQ0tv0gMNytzupgk1IgjN9CpmaBdX5zps,1947
|
|
29
|
-
pypeline_runner-1.
|
|
30
|
-
pypeline_runner-1.
|
|
31
|
-
pypeline_runner-1.
|
|
32
|
-
pypeline_runner-1.
|
|
33
|
-
pypeline_runner-1.
|
|
30
|
+
pypeline_runner-1.12.0.dist-info/LICENSE,sha256=sKxdoqSmW9ezvPvt0ZGJbneyA0SBcm0GiqzTv2jN230,1066
|
|
31
|
+
pypeline_runner-1.12.0.dist-info/METADATA,sha256=Z4LtZX3fUUXeeyZDd85HNjUru7uPFQ6diDSz8XKyySk,7553
|
|
32
|
+
pypeline_runner-1.12.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
33
|
+
pypeline_runner-1.12.0.dist-info/entry_points.txt,sha256=pe1u0uuhPI_yeQ0KjEw6jK-EvQfPcZwBSajgbAdKz1o,47
|
|
34
|
+
pypeline_runner-1.12.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|