pypeline-runner 1.10.0__tar.gz → 1.12.0__tar.gz

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.
Files changed (34) hide show
  1. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/PKG-INFO +2 -2
  2. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/pyproject.toml +2 -2
  3. pypeline_runner-1.12.0/src/pypeline/__init__.py +1 -0
  4. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/domain/config.py +14 -1
  5. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/domain/execution_context.py +3 -1
  6. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/domain/project_slurper.py +4 -0
  7. pypeline_runner-1.12.0/src/pypeline/inputs_parser.py +88 -0
  8. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/pypeline.yaml +6 -0
  9. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/main.py +16 -2
  10. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/steps/create_venv.py +3 -0
  11. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/steps/scoop_install.py +2 -0
  12. pypeline_runner-1.10.0/src/pypeline/__init__.py +0 -1
  13. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/LICENSE +0 -0
  14. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/README.md +0 -0
  15. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/__run.py +0 -0
  16. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/domain/__init__.py +0 -0
  17. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/domain/artifacts.py +0 -0
  18. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/domain/pipeline.py +0 -0
  19. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/__init__.py +0 -0
  20. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/create.py +0 -0
  21. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/.gitignore +0 -0
  22. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/bootstrap.ps1 +0 -0
  23. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/bootstrap.py +0 -0
  24. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/poetry.toml +0 -0
  25. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/pypeline.ps1 +0 -0
  26. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/pyproject.toml +0 -0
  27. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/scoopfile.json +0 -0
  28. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/steps/my_step.py +0 -0
  29. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/kickstart/templates/project/west.yaml +0 -0
  30. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/py.typed +0 -0
  31. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/pypeline.py +0 -0
  32. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/steps/__init__.py +0 -0
  33. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/steps/env_setup_script.py +0 -0
  34. {pypeline_runner-1.10.0 → pypeline_runner-1.12.0}/src/pypeline/steps/west_install.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pypeline-runner
3
- Version: 1.10.0
3
+ Version: 1.12.0
4
4
  Summary: Configure and execute pipelines with Python (similar to GitHub workflows or Jenkins pipelines).
5
5
  License: MIT
6
6
  Author: cuinixam
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Topic :: Software Development :: Libraries
20
- Requires-Dist: py-app-dev (>=2.9,<3.0)
20
+ Requires-Dist: py-app-dev (>=2.10,<3.0)
21
21
  Requires-Dist: pyyaml (>=6.0,<7.0)
22
22
  Requires-Dist: typer (>=0,<1)
23
23
  Project-URL: Bug Tracker, https://github.com/cuinixam/pypeline/issues
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pypeline-runner"
3
- version = "1.10.0"
3
+ version = "1.12.0"
4
4
  description = "Configure and execute pipelines with Python (similar to GitHub workflows or Jenkins pipelines)."
5
5
  authors = ["cuinixam <me@cuinixam.com>"]
6
6
  license = "MIT"
@@ -27,7 +27,7 @@ pypeline = "pypeline.main:main"
27
27
 
28
28
  [tool.poetry.dependencies]
29
29
  python = "^3.10"
30
- py-app-dev = "^2.9"
30
+ py-app-dev = "^2.10"
31
31
  typer = "^0"
32
32
  pyyaml = "^6.0"
33
33
 
@@ -0,0 +1 @@
1
+ __version__ = "1.12.0"
@@ -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)
@@ -21,3 +21,7 @@ class ProjectSlurper:
21
21
  @property
22
22
  def project_dir(self) -> Path:
23
23
  return self.artifacts_locator.project_root_dir
24
+
25
+ @property
26
+ def project_config(self) -> ProjectConfig:
27
+ return self.user_config
@@ -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
@@ -1,3 +1,9 @@
1
+ inputs:
2
+ my_input:
3
+ type: string
4
+ description: My input description
5
+ default: my_input_default
6
+
1
7
  pipeline:
2
8
  - step: CreateVEnv
3
9
  module: pypeline.steps.create_venv
@@ -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
- PipelineStepsExecutor[ExecutionContext](ExecutionContext(project_dir), steps_references, force_run, dry_run).run()
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:
@@ -55,3 +55,6 @@ class CreateVEnv(PipelineStep[ExecutionContext]):
55
55
 
56
56
  def update_execution_context(self) -> None:
57
57
  pass
58
+
59
+ def get_needs_dependency_management(self) -> bool:
60
+ return False
@@ -68,7 +68,9 @@ class ScoopInstall(PipelineStep[ExecutionContext]):
68
68
  def run(self) -> int:
69
69
  self.logger.debug(f"Run {self.get_name()} step. Output dir: {self.output_dir}")
70
70
  installed_apps = create_scoop_wrapper().install(self.scoop_file)
71
+ self.logger.debug("Installed apps:")
71
72
  for app in installed_apps:
73
+ self.logger.debug(f" - {app.name} ({app.version})")
72
74
  self.install_dirs.extend(app.get_all_required_paths())
73
75
  self.execution_info.to_json_file(self.execution_info_file)
74
76
  return 0
@@ -1 +0,0 @@
1
- __version__ = "1.10.0"