pypeline-runner 0.2.1__py3-none-any.whl → 0.3.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 +2 -1
- pypeline/domain/pipeline.py +38 -1
- pypeline/main.py +5 -1
- pypeline/pypeline.py +99 -7
- {pypeline_runner-0.2.1.dist-info → pypeline_runner-0.3.0.dist-info}/METADATA +4 -4
- {pypeline_runner-0.2.1.dist-info → pypeline_runner-0.3.0.dist-info}/RECORD +10 -10
- {pypeline_runner-0.2.1.dist-info → pypeline_runner-0.3.0.dist-info}/LICENSE +0 -0
- {pypeline_runner-0.2.1.dist-info → pypeline_runner-0.3.0.dist-info}/WHEEL +0 -0
- {pypeline_runner-0.2.1.dist-info → pypeline_runner-0.3.0.dist-info}/entry_points.txt +0 -0
pypeline/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.3.0"
|
pypeline/domain/config.py
CHANGED
|
@@ -5,10 +5,11 @@ from typing import Any, Dict, Optional
|
|
|
5
5
|
import yaml
|
|
6
6
|
from mashumaro import DataClassDictMixin
|
|
7
7
|
from py_app_dev.core.exceptions import UserNotificationException
|
|
8
|
-
from py_app_dev.core.pipeline import PipelineConfig
|
|
9
8
|
from yaml.parser import ParserError
|
|
10
9
|
from yaml.scanner import ScannerError
|
|
11
10
|
|
|
11
|
+
from .pipeline import PipelineConfig
|
|
12
|
+
|
|
12
13
|
|
|
13
14
|
@dataclass
|
|
14
15
|
class ProjectConfig(DataClassDictMixin):
|
pypeline/domain/pipeline.py
CHANGED
|
@@ -1,14 +1,48 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
+
from dataclasses import dataclass
|
|
2
3
|
from pathlib import Path
|
|
3
|
-
from typing import
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Dict,
|
|
7
|
+
List,
|
|
8
|
+
Optional,
|
|
9
|
+
OrderedDict,
|
|
10
|
+
Type,
|
|
11
|
+
TypeAlias,
|
|
12
|
+
)
|
|
4
13
|
|
|
14
|
+
from mashumaro import DataClassDictMixin
|
|
5
15
|
from py_app_dev.core.runnable import Runnable
|
|
6
16
|
|
|
7
17
|
from .execution_context import ExecutionContext
|
|
8
18
|
|
|
9
19
|
|
|
20
|
+
@dataclass
|
|
21
|
+
class PipelineStepConfig(DataClassDictMixin):
|
|
22
|
+
#: Step name or class name if file is not specified
|
|
23
|
+
step: str
|
|
24
|
+
#: Path to file with step class
|
|
25
|
+
file: Optional[str] = None
|
|
26
|
+
#: Python module with step class
|
|
27
|
+
module: Optional[str] = None
|
|
28
|
+
#: Step class name
|
|
29
|
+
class_name: Optional[str] = None
|
|
30
|
+
#: Command to run. For simple steps that don't need a class
|
|
31
|
+
run: Optional[str] = None
|
|
32
|
+
#: Step description
|
|
33
|
+
description: Optional[str] = None
|
|
34
|
+
#: Step timeout in seconds
|
|
35
|
+
timeout_sec: Optional[int] = None
|
|
36
|
+
#: Custom step configuration
|
|
37
|
+
config: Optional[Dict[str, Any]] = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
PipelineConfig: TypeAlias = OrderedDict[str, List[PipelineStepConfig]]
|
|
41
|
+
|
|
42
|
+
|
|
10
43
|
class PipelineStep(Runnable):
|
|
11
44
|
def __init__(self, execution_context: ExecutionContext, output_dir: Path, config: Optional[Dict[str, Any]] = None) -> None:
|
|
45
|
+
super().__init__(self.get_needs_dependency_management())
|
|
12
46
|
self.execution_context = execution_context
|
|
13
47
|
self.output_dir = output_dir
|
|
14
48
|
self.config = config
|
|
@@ -18,6 +52,9 @@ class PipelineStep(Runnable):
|
|
|
18
52
|
def update_execution_context(self) -> None:
|
|
19
53
|
pass
|
|
20
54
|
|
|
55
|
+
def get_needs_dependency_management(self) -> bool:
|
|
56
|
+
return True
|
|
57
|
+
|
|
21
58
|
|
|
22
59
|
class PipelineStepReference:
|
|
23
60
|
def __init__(self, group_name: str, _class: Type[PipelineStep], config: Optional[Dict[str, Any]] = None) -> None:
|
pypeline/main.py
CHANGED
|
@@ -86,10 +86,14 @@ def run(
|
|
|
86
86
|
PipelineStepsExecutor(project_slurper.artifacts_locator, steps_references, force_run, dry_run).run()
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
def main() -> None:
|
|
90
90
|
try:
|
|
91
91
|
setup_logger()
|
|
92
92
|
app()
|
|
93
93
|
except UserNotificationException as e:
|
|
94
94
|
logger.error(f"{e}")
|
|
95
95
|
sys.exit(1)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|
pypeline/pypeline.py
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from importlib.util import module_from_spec, spec_from_file_location
|
|
1
3
|
from pathlib import Path
|
|
2
|
-
from typing import
|
|
3
|
-
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Dict,
|
|
7
|
+
List,
|
|
8
|
+
Optional,
|
|
9
|
+
Type,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from py_app_dev.core.exceptions import UserNotificationException
|
|
4
13
|
from py_app_dev.core.logging import logger
|
|
5
|
-
from py_app_dev.core.pipeline import PipelineConfig
|
|
6
|
-
from py_app_dev.core.pipeline import PipelineLoader as GenericPipelineLoader
|
|
7
14
|
from py_app_dev.core.runnable import Executor
|
|
8
15
|
|
|
9
16
|
from .domain.artifacts import ProjectArtifactsLocator
|
|
10
17
|
from .domain.execution_context import ExecutionContext
|
|
11
|
-
from .domain.pipeline import PipelineStep, PipelineStepReference
|
|
18
|
+
from .domain.pipeline import PipelineConfig, PipelineStep, PipelineStepConfig, PipelineStepReference
|
|
12
19
|
|
|
13
20
|
|
|
14
21
|
class PipelineLoader:
|
|
@@ -23,10 +30,95 @@ class PipelineLoader:
|
|
|
23
30
|
def __init__(self, pipeline_config: PipelineConfig, project_root_dir: Path) -> None:
|
|
24
31
|
self.pipeline_config = pipeline_config
|
|
25
32
|
self.project_root_dir = project_root_dir
|
|
26
|
-
self._loader = GenericPipelineLoader[PipelineStep](self.pipeline_config, self.project_root_dir)
|
|
27
33
|
|
|
28
34
|
def load_steps_references(self) -> List[PipelineStepReference]:
|
|
29
|
-
|
|
35
|
+
result = []
|
|
36
|
+
for group_name, steps_config in self.pipeline_config.items():
|
|
37
|
+
result.extend(self._load_steps(group_name, steps_config, self.project_root_dir))
|
|
38
|
+
return result
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def _load_steps(
|
|
42
|
+
group_name: str,
|
|
43
|
+
steps_config: List[PipelineStepConfig],
|
|
44
|
+
project_root_dir: Path,
|
|
45
|
+
) -> List[PipelineStepReference]:
|
|
46
|
+
result = []
|
|
47
|
+
for step_config in steps_config:
|
|
48
|
+
step_class_name = step_config.class_name or step_config.step
|
|
49
|
+
if step_config.module:
|
|
50
|
+
step_class = PipelineLoader._load_module_step(step_config.module, step_class_name)
|
|
51
|
+
elif step_config.file:
|
|
52
|
+
step_class = PipelineLoader._load_user_step(project_root_dir.joinpath(step_config.file), step_class_name)
|
|
53
|
+
elif step_config.run:
|
|
54
|
+
step_class = PipelineLoader._create_run_command_step_class(step_config.run, step_class_name)
|
|
55
|
+
else:
|
|
56
|
+
raise UserNotificationException(f"Step '{step_class_name}' has no 'module' nor 'file' nor `run` defined." " Please check your pipeline configuration.")
|
|
57
|
+
result.append(PipelineStepReference(group_name, step_class, step_config.config))
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _load_user_step(python_file: Path, step_class_name: str) -> Type[PipelineStep]:
|
|
62
|
+
# Create a module specification from the file path
|
|
63
|
+
spec = spec_from_file_location(f"user__{step_class_name}", python_file)
|
|
64
|
+
if spec and spec.loader:
|
|
65
|
+
step_module = module_from_spec(spec)
|
|
66
|
+
# Import the module
|
|
67
|
+
spec.loader.exec_module(step_module)
|
|
68
|
+
try:
|
|
69
|
+
step_class = getattr(step_module, step_class_name)
|
|
70
|
+
except AttributeError:
|
|
71
|
+
raise UserNotificationException(f"Could not load class '{step_class_name}' from file '{python_file}'." " Please check your pipeline configuration.") from None
|
|
72
|
+
return step_class
|
|
73
|
+
raise UserNotificationException(f"Could not load file '{python_file}'." " Please check the file for any errors.")
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def _load_module_step(module_name: str, step_class_name: str) -> Type[PipelineStep]:
|
|
77
|
+
try:
|
|
78
|
+
module = importlib.import_module(module_name)
|
|
79
|
+
step_class = getattr(module, step_class_name)
|
|
80
|
+
except ImportError:
|
|
81
|
+
raise UserNotificationException(f"Could not load module '{module_name}'. Please check your pipeline configuration.") from None
|
|
82
|
+
except AttributeError:
|
|
83
|
+
raise UserNotificationException(f"Could not load class '{step_class_name}' from module '{module_name}'." " Please check your pipeline configuration.") from None
|
|
84
|
+
return step_class
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def _create_run_command_step_class(command: str, name: str) -> Type[PipelineStep]:
|
|
88
|
+
"""Dynamically creates a step class for a given command."""
|
|
89
|
+
|
|
90
|
+
class DynamicRunCommandStep(PipelineStep):
|
|
91
|
+
"""A simple step that runs a command."""
|
|
92
|
+
|
|
93
|
+
def __init__(self, execution_context: ExecutionContext, output_dir: Path, config: Optional[Dict[str, Any]] = None) -> None:
|
|
94
|
+
super().__init__(execution_context, output_dir, config)
|
|
95
|
+
self.command = command
|
|
96
|
+
self.name = name
|
|
97
|
+
|
|
98
|
+
def get_needs_dependency_management(self) -> bool:
|
|
99
|
+
"""A command step does not need dependency management."""
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
def run(self) -> int:
|
|
103
|
+
self.execution_context.create_process_executor(
|
|
104
|
+
[self.command],
|
|
105
|
+
cwd=self.project_root_dir,
|
|
106
|
+
).execute()
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
def get_name(self) -> str:
|
|
110
|
+
return self.name
|
|
111
|
+
|
|
112
|
+
def get_inputs(self) -> List[Path]:
|
|
113
|
+
return []
|
|
114
|
+
|
|
115
|
+
def get_outputs(self) -> List[Path]:
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
def update_execution_context(self) -> None:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
return DynamicRunCommandStep
|
|
30
122
|
|
|
31
123
|
|
|
32
124
|
class PipelineStepsExecutor:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pypeline-runner
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Configure and execute pipelines with Python (similar to GitHub workflows or Jenkins pipelines).
|
|
5
5
|
Home-page: https://github.com/cuinixam/pypeline
|
|
6
6
|
License: MIT
|
|
@@ -17,9 +17,9 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
-
Requires-Dist: py-app-dev (>=2.
|
|
21
|
-
Requires-Dist: pyyaml (>=6.0
|
|
22
|
-
Requires-Dist: typer[all] (>=0.12
|
|
20
|
+
Requires-Dist: py-app-dev (>=2.2,<3.0)
|
|
21
|
+
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
22
|
+
Requires-Dist: typer[all] (>=0.12,<0.13)
|
|
23
23
|
Project-URL: Bug Tracker, https://github.com/cuinixam/pypeline/issues
|
|
24
24
|
Project-URL: Changelog, https://github.com/cuinixam/pypeline/blob/main/CHANGELOG.md
|
|
25
25
|
Project-URL: Documentation, https://pypeline-runner.readthedocs.io
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
pypeline/__init__.py,sha256=
|
|
1
|
+
pypeline/__init__.py,sha256=VrXpHDu3erkzwl_WXrqINBm9xWkcyUy53IQOj042dOs,22
|
|
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=qXshnk9umi0AVGV4m5iEiy_MQ5Ad2LDZwI8OULU-qMk,1355
|
|
5
|
-
pypeline/domain/config.py,sha256=
|
|
5
|
+
pypeline/domain/config.py,sha256=AlavAaz5hSxa6yaKYnj-x71ClhOtA41yv5Qf2JIE47k,1650
|
|
6
6
|
pypeline/domain/execution_context.py,sha256=0zc3OgXeIMDpgWWYMaDGub7fY5urLLR79yuCaXVxoTQ,1101
|
|
7
|
-
pypeline/domain/pipeline.py,sha256=
|
|
7
|
+
pypeline/domain/pipeline.py,sha256=TbnM1JI1AFEdDfMSfdDVi2DZjFAAWBRMzIycOsui4Vs,1997
|
|
8
8
|
pypeline/domain/project_slurper.py,sha256=YCho7V1BHjFmC_foxHFaWX8c_VbMJ16XEB4CQBlMrhc,894
|
|
9
9
|
pypeline/kickstart/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
pypeline/kickstart/create.py,sha256=Gz2rVfhoQCKgYWlJoefh5Azcgamc9JJtiNsP1DTKdQo,2235
|
|
@@ -17,15 +17,15 @@ pypeline/kickstart/templates/project/pypeline.yaml,sha256=i9YSqpS2o-_Bzl69fvdOFV
|
|
|
17
17
|
pypeline/kickstart/templates/project/pyproject.toml,sha256=yc6RCo-bUo1PXF91XfM-dButgfxU16Uud34NidgJ0zQ,225
|
|
18
18
|
pypeline/kickstart/templates/project/scoopfile.json,sha256=DcfZ8jYf9hmPHM-AWwnPKQJCzRG3fCuYtMeoY01nkag,219
|
|
19
19
|
pypeline/kickstart/templates/project/steps/my_step.py,sha256=YNRdb7ofqo3PxMQe8Vhtf7K9eZclv-6J0IwMrd3E70w,651
|
|
20
|
-
pypeline/main.py,sha256=
|
|
20
|
+
pypeline/main.py,sha256=gwzbTUu-9_LXHpMDUDJU4gUInA4wGRmngO0VaTAqptk,3260
|
|
21
21
|
pypeline/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
-
pypeline/pypeline.py,sha256=
|
|
22
|
+
pypeline/pypeline.py,sha256=YiKZ8IH1t1FsTqJ45dVBkr2I2XRN2Su0ChlJFbUIqBc,8335
|
|
23
23
|
pypeline/steps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
24
|
pypeline/steps/create_venv.py,sha256=od1zLVPhNdN-sWqEqYMkX19c5IEt4lIIOcASBeoqUUw,1954
|
|
25
25
|
pypeline/steps/scoop_install.py,sha256=MmoC4Yy1lOhHrfKBrgJKHMcRUNPbRztzUbSgIlazer8,3356
|
|
26
26
|
pypeline/steps/west_install.py,sha256=J1p-62owTDOY6bVTGCr5d9YFOL8DeRUPTnq0TbiaWlA,1930
|
|
27
|
-
pypeline_runner-0.
|
|
28
|
-
pypeline_runner-0.
|
|
29
|
-
pypeline_runner-0.
|
|
30
|
-
pypeline_runner-0.
|
|
31
|
-
pypeline_runner-0.
|
|
27
|
+
pypeline_runner-0.3.0.dist-info/LICENSE,sha256=sKxdoqSmW9ezvPvt0ZGJbneyA0SBcm0GiqzTv2jN230,1066
|
|
28
|
+
pypeline_runner-0.3.0.dist-info/METADATA,sha256=f0qMzL-SHEe-n5BJw1DHJ-9WovmEmIaBh1X4bUMaYiY,7156
|
|
29
|
+
pypeline_runner-0.3.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
30
|
+
pypeline_runner-0.3.0.dist-info/entry_points.txt,sha256=pe1u0uuhPI_yeQ0KjEw6jK-EvQfPcZwBSajgbAdKz1o,47
|
|
31
|
+
pypeline_runner-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|