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 CHANGED
@@ -1 +1 @@
1
- __version__ = "1.11.0"
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)
@@ -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
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
- 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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pypeline-runner
3
- Version: 1.11.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
@@ -1,11 +1,12 @@
1
- pypeline/__init__.py,sha256=uTVPjS5n_41_n-e8UW1W9n8Wt-3YLQI1WS0r4xGphOk,23
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=AlavAaz5hSxa6yaKYnj-x71ClhOtA41yv5Qf2JIE47k,1650
6
- pypeline/domain/execution_context.py,sha256=BwjKxkv5nBIc2htCzMgXwnBFCORHpWZYWSKQD5wpcMA,1424
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=e3BLV88GvfW3efh0agUWKqMk3oWnL602P5u9jER_o9U,971
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=7FeIu7OOqq7iToPVdP_a4MehIi9lUkBPbXnl8mYpxSo,279
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=Xk92H0z7z-WnrC6qdSrD5xHlTN65WDc64lSs6M4Jn_g,3324
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.11.0.dist-info/LICENSE,sha256=sKxdoqSmW9ezvPvt0ZGJbneyA0SBcm0GiqzTv2jN230,1066
30
- pypeline_runner-1.11.0.dist-info/METADATA,sha256=ggajX1hHht_OzArfOo-AskPyZN2aum8z6rExafdgYuw,7553
31
- pypeline_runner-1.11.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
32
- pypeline_runner-1.11.0.dist-info/entry_points.txt,sha256=pe1u0uuhPI_yeQ0KjEw6jK-EvQfPcZwBSajgbAdKz1o,47
33
- pypeline_runner-1.11.0.dist-info/RECORD,,
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,,