FlowerPower 0.21.0__tar.gz → 0.31.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.
- {flowerpower-0.21.0/src/FlowerPower.egg-info → flowerpower-0.31.0}/PKG-INFO +1 -13
- flowerpower-0.31.0/pyproject.toml +142 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0/src/FlowerPower.egg-info}/PKG-INFO +1 -13
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/FlowerPower.egg-info/SOURCES.txt +12 -3
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/FlowerPower.egg-info/requires.txt +0 -16
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/cfg/__init__.py +143 -25
- flowerpower-0.31.0/src/flowerpower/cfg/base.py +262 -0
- flowerpower-0.31.0/src/flowerpower/cfg/exceptions.py +53 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/cfg/pipeline/__init__.py +151 -35
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/cfg/pipeline/adapter.py +1 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/cfg/pipeline/builder.py +24 -25
- flowerpower-0.31.0/src/flowerpower/cfg/pipeline/builder_adapter.py +142 -0
- flowerpower-0.31.0/src/flowerpower/cfg/pipeline/builder_executor.py +101 -0
- flowerpower-0.31.0/src/flowerpower/cfg/pipeline/run.py +195 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/cfg/project/__init__.py +59 -14
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/cfg/project/adapter.py +6 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/cli/__init__.py +8 -9
- flowerpower-0.31.0/src/flowerpower/cli/cfg.py +3 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/cli/pipeline.py +121 -83
- flowerpower-0.31.0/src/flowerpower/cli/utils.py +197 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/flowerpower.py +94 -120
- flowerpower-0.31.0/src/flowerpower/pipeline/config_manager.py +180 -0
- flowerpower-0.31.0/src/flowerpower/pipeline/executor.py +126 -0
- flowerpower-0.31.0/src/flowerpower/pipeline/lifecycle_manager.py +231 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/pipeline/manager.py +121 -276
- flowerpower-0.31.0/src/flowerpower/pipeline/pipeline.py +431 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/pipeline/registry.py +45 -4
- flowerpower-0.31.0/src/flowerpower/utils/__init__.py +19 -0
- flowerpower-0.31.0/src/flowerpower/utils/adapter.py +286 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/utils/callback.py +73 -67
- flowerpower-0.31.0/src/flowerpower/utils/config.py +306 -0
- flowerpower-0.31.0/src/flowerpower/utils/executor.py +178 -0
- flowerpower-0.31.0/src/flowerpower/utils/filesystem.py +194 -0
- flowerpower-0.31.0/src/flowerpower/utils/misc.py +420 -0
- flowerpower-0.31.0/src/flowerpower/utils/security.py +221 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/tests/test_flowerpower_project.py +1 -1
- flowerpower-0.21.0/pyproject.toml +0 -81
- flowerpower-0.21.0/src/flowerpower/cfg/base.py +0 -141
- flowerpower-0.21.0/src/flowerpower/cfg/pipeline/_schedule.py +0 -32
- flowerpower-0.21.0/src/flowerpower/cfg/pipeline/run.py +0 -83
- flowerpower-0.21.0/src/flowerpower/cli/cfg.py +0 -41
- flowerpower-0.21.0/src/flowerpower/cli/mqtt.py +0 -168
- flowerpower-0.21.0/src/flowerpower/cli/utils.py +0 -148
- flowerpower-0.21.0/src/flowerpower/pipeline/pipeline.py +0 -643
- flowerpower-0.21.0/src/flowerpower/plugins/mqtt/__init__.py +0 -8
- flowerpower-0.21.0/src/flowerpower/utils/misc.py +0 -247
- {flowerpower-0.21.0 → flowerpower-0.31.0}/LICENSE +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/README.md +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/setup.cfg +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/FlowerPower.egg-info/dependency_links.txt +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/FlowerPower.egg-info/entry_points.txt +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/FlowerPower.egg-info/top_level.txt +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/__init__.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/pipeline/__init__.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/pipeline/base.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/pipeline/io.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/pipeline/visualizer.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/plugins/io/__init__.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/settings/__init__.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/settings/_backend.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/settings/executor.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/settings/general.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/settings/hamilton.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/settings/logging.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/settings/retry.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/utils/logging.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/utils/monkey.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/utils/open_telemetry.py +0 -0
- {flowerpower-0.21.0 → flowerpower-0.31.0}/src/flowerpower/utils/templates.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: FlowerPower
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.31.0
|
4
4
|
Summary: A simple workflow framework for building and managing data processing pipelines
|
5
5
|
Author-email: "Volker L." <ligno.blades@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/legout/flowerpower
|
@@ -25,26 +25,14 @@ Provides-Extra: io
|
|
25
25
|
Requires-Dist: flowerpower-io>=0.1.1; extra == "io"
|
26
26
|
Provides-Extra: io-legacy
|
27
27
|
Requires-Dist: flowerpower-io[legacy]>=0.1.1; extra == "io-legacy"
|
28
|
-
Provides-Extra: mongodb
|
29
|
-
Requires-Dist: pymongo>=4.7.2; extra == "mongodb"
|
30
|
-
Provides-Extra: mqtt
|
31
|
-
Requires-Dist: paho-mqtt>=2.1.0; extra == "mqtt"
|
32
|
-
Requires-Dist: orjson>=3.10.11; extra == "mqtt"
|
33
|
-
Requires-Dist: mmh3>=5.1.0; extra == "mqtt"
|
34
28
|
Provides-Extra: opentelemetry
|
35
29
|
Requires-Dist: opentelemetry-api>=1.5.0; extra == "opentelemetry"
|
36
30
|
Requires-Dist: opentelemetry-sdk>=1.5.0; extra == "opentelemetry"
|
37
31
|
Requires-Dist: opentelemetry-exporter-jaeger>=1.21.0; extra == "opentelemetry"
|
38
32
|
Provides-Extra: ray
|
39
33
|
Requires-Dist: ray>=2.34.0; extra == "ray"
|
40
|
-
Provides-Extra: tui
|
41
|
-
Requires-Dist: textual>=0.85.2; extra == "tui"
|
42
34
|
Provides-Extra: ui
|
43
35
|
Requires-Dist: sf-hamilton-ui>=0.0.11; extra == "ui"
|
44
|
-
Provides-Extra: webserver
|
45
|
-
Requires-Dist: sanic>=24.6.0; extra == "webserver"
|
46
|
-
Requires-Dist: sanic-ext>=23.12.0; extra == "webserver"
|
47
|
-
Requires-Dist: orjson>=3.10.11; extra == "webserver"
|
48
36
|
Provides-Extra: openlineage
|
49
37
|
Requires-Dist: openlineage-python>=1.32.0; extra == "openlineage"
|
50
38
|
Dynamic: license-file
|
@@ -0,0 +1,142 @@
|
|
1
|
+
[project]
|
2
|
+
name = "FlowerPower"
|
3
|
+
description = "A simple workflow framework for building and managing data processing pipelines"
|
4
|
+
authors = [{ name = "Volker L.", email = "ligno.blades@gmail.com" }]
|
5
|
+
readme = "README.md"
|
6
|
+
requires-python = ">= 3.11"
|
7
|
+
version = "0.31.0"
|
8
|
+
keywords = ["hamilton", "workflow", "pipeline", "scheduler", "dask", "ray"]
|
9
|
+
|
10
|
+
dependencies = [
|
11
|
+
#'dill>=0.3.8',
|
12
|
+
'duration-parser>=1.0.1',
|
13
|
+
'fsspec>=2024.10.0',
|
14
|
+
'fsspec-utils>=0.1.0',
|
15
|
+
'humanize>=4.12.2',
|
16
|
+
'msgspec>=0.19.0',
|
17
|
+
'munch>=4.0.0',
|
18
|
+
#"openai>=1.100.2",
|
19
|
+
#'orjson>=3.10.15',
|
20
|
+
#'python-dotenv>=1.0.1',
|
21
|
+
'pyyaml>=6.0.1',
|
22
|
+
'rich>=13.9.3',
|
23
|
+
's3fs>=2024.10.0',
|
24
|
+
'sf-hamilton-sdk>=0.5.2',
|
25
|
+
'sf-hamilton[visualization,rich,tqdm]>=1.69.0',
|
26
|
+
'typer>=0.12.3',
|
27
|
+
]
|
28
|
+
|
29
|
+
|
30
|
+
[project.urls]
|
31
|
+
"Homepage" = "https://github.com/legout/flowerpower"
|
32
|
+
"Bug Tracker" = "https://github.com/legout/flowerpower/issues"
|
33
|
+
|
34
|
+
[project.scripts]
|
35
|
+
flowerpower = "flowerpower.cli:app"
|
36
|
+
|
37
|
+
[project.optional-dependencies]
|
38
|
+
io = ["flowerpower-io>=0.1.1"]
|
39
|
+
io-legacy = ["flowerpower-io[legacy]>=0.1.1"]
|
40
|
+
opentelemetry = [
|
41
|
+
"opentelemetry-api>=1.5.0",
|
42
|
+
"opentelemetry-sdk>=1.5.0",
|
43
|
+
"opentelemetry-exporter-jaeger>=1.21.0", #"sf-hamilton[opentelemetry]>=1.83.3"
|
44
|
+
]
|
45
|
+
ray = ["ray>=2.34.0"]
|
46
|
+
ui = ["sf-hamilton-ui>=0.0.11"]
|
47
|
+
|
48
|
+
openlineage = ["openlineage-python>=1.32.0"]
|
49
|
+
|
50
|
+
|
51
|
+
[tool.uv]
|
52
|
+
dev-dependencies = [
|
53
|
+
"ipython>=8.24.0",
|
54
|
+
"isort>=5.13.2",
|
55
|
+
"ruff>=0.7.1",
|
56
|
+
"jupyterlab>=4.3.0",
|
57
|
+
"pytest>=8.3.4",
|
58
|
+
"pytest-mock>=3.12.0",
|
59
|
+
"pytest-cov>=4.1.0",
|
60
|
+
"marimo>=0.10.19",
|
61
|
+
"pre-commit>=4.2.0",
|
62
|
+
"mkdocs>=1.6.1",
|
63
|
+
"mkdocs-material>=9.6.17",
|
64
|
+
"quarto>=0.1.0",
|
65
|
+
"mkdocs-glightbox>=0.4.0",
|
66
|
+
"mkdocs-mermaid2-plugin>=1.2.1",
|
67
|
+
"pymdown-extensions>=10.16.1",
|
68
|
+
"mkdocstrings>=0.30.0",
|
69
|
+
"mkdocstrings-python>=1.17.0",
|
70
|
+
"repomix>=0.3.4",
|
71
|
+
# Security audit tools
|
72
|
+
"bandit[toml]>=1.7.7",
|
73
|
+
"safety>=3.2.0",
|
74
|
+
"mypy>=1.13.0",
|
75
|
+
"pandas>=2.3.2",
|
76
|
+
"numpy>=2.3.3",
|
77
|
+
"matplotlib>=3.10.6",
|
78
|
+
"seaborn>=0.13.2",
|
79
|
+
]
|
80
|
+
package = true
|
81
|
+
|
82
|
+
# Security configuration
|
83
|
+
[tool.bandit]
|
84
|
+
exclude_dirs = ["tests", "examples"]
|
85
|
+
skips = ["B101"] # Skip assert_used test for test files
|
86
|
+
|
87
|
+
[tool.bandit.assert_used]
|
88
|
+
skips = ["*/test_*.py", "*/tests.py"]
|
89
|
+
|
90
|
+
# MyPy configuration
|
91
|
+
[tool.mypy]
|
92
|
+
python_version = "3.11"
|
93
|
+
warn_return_any = true
|
94
|
+
warn_unused_configs = true
|
95
|
+
disallow_untyped_defs = true
|
96
|
+
disallow_incomplete_defs = true
|
97
|
+
check_untyped_defs = true
|
98
|
+
disallow_untyped_decorators = true
|
99
|
+
no_implicit_optional = true
|
100
|
+
warn_redundant_casts = true
|
101
|
+
warn_unused_ignores = true
|
102
|
+
warn_no_return = true
|
103
|
+
warn_unreachable = true
|
104
|
+
strict_equality = true
|
105
|
+
show_error_codes = true
|
106
|
+
|
107
|
+
[[tool.mypy.overrides]]
|
108
|
+
module = [
|
109
|
+
"hamilton.*",
|
110
|
+
"sf_hamilton.*",
|
111
|
+
"fsspec_utils.*",
|
112
|
+
"loguru.*",
|
113
|
+
"munch.*",
|
114
|
+
"rich.*",
|
115
|
+
"typer.*",
|
116
|
+
]
|
117
|
+
ignore_missing_imports = true
|
118
|
+
|
119
|
+
# Ruff configuration (extended for security)
|
120
|
+
[tool.ruff]
|
121
|
+
line-length = 88
|
122
|
+
target-version = "py311"
|
123
|
+
|
124
|
+
[tool.ruff.lint]
|
125
|
+
select = [
|
126
|
+
"E", # pycodestyle errors
|
127
|
+
"W", # pycodestyle warnings
|
128
|
+
"F", # pyflakes
|
129
|
+
"I", # isort
|
130
|
+
"B", # flake8-bugbear
|
131
|
+
"C4", # flake8-comprehensions
|
132
|
+
"UP", # pyupgrade
|
133
|
+
"S", # flake8-bandit (security)
|
134
|
+
]
|
135
|
+
ignore = [
|
136
|
+
"E501", # line too long, handled by black
|
137
|
+
"B008", # do not perform function calls in argument defaults
|
138
|
+
]
|
139
|
+
|
140
|
+
[tool.ruff.lint.per-file-ignores]
|
141
|
+
"tests/*" = ["S101"] # Allow assert in tests
|
142
|
+
"examples/*" = ["S101"] # Allow assert in examples
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: FlowerPower
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.31.0
|
4
4
|
Summary: A simple workflow framework for building and managing data processing pipelines
|
5
5
|
Author-email: "Volker L." <ligno.blades@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/legout/flowerpower
|
@@ -25,26 +25,14 @@ Provides-Extra: io
|
|
25
25
|
Requires-Dist: flowerpower-io>=0.1.1; extra == "io"
|
26
26
|
Provides-Extra: io-legacy
|
27
27
|
Requires-Dist: flowerpower-io[legacy]>=0.1.1; extra == "io-legacy"
|
28
|
-
Provides-Extra: mongodb
|
29
|
-
Requires-Dist: pymongo>=4.7.2; extra == "mongodb"
|
30
|
-
Provides-Extra: mqtt
|
31
|
-
Requires-Dist: paho-mqtt>=2.1.0; extra == "mqtt"
|
32
|
-
Requires-Dist: orjson>=3.10.11; extra == "mqtt"
|
33
|
-
Requires-Dist: mmh3>=5.1.0; extra == "mqtt"
|
34
28
|
Provides-Extra: opentelemetry
|
35
29
|
Requires-Dist: opentelemetry-api>=1.5.0; extra == "opentelemetry"
|
36
30
|
Requires-Dist: opentelemetry-sdk>=1.5.0; extra == "opentelemetry"
|
37
31
|
Requires-Dist: opentelemetry-exporter-jaeger>=1.21.0; extra == "opentelemetry"
|
38
32
|
Provides-Extra: ray
|
39
33
|
Requires-Dist: ray>=2.34.0; extra == "ray"
|
40
|
-
Provides-Extra: tui
|
41
|
-
Requires-Dist: textual>=0.85.2; extra == "tui"
|
42
34
|
Provides-Extra: ui
|
43
35
|
Requires-Dist: sf-hamilton-ui>=0.0.11; extra == "ui"
|
44
|
-
Provides-Extra: webserver
|
45
|
-
Requires-Dist: sanic>=24.6.0; extra == "webserver"
|
46
|
-
Requires-Dist: sanic-ext>=23.12.0; extra == "webserver"
|
47
|
-
Requires-Dist: orjson>=3.10.11; extra == "webserver"
|
48
36
|
Provides-Extra: openlineage
|
49
37
|
Requires-Dist: openlineage-python>=1.32.0; extra == "openlineage"
|
50
38
|
Dynamic: license-file
|
@@ -11,27 +11,30 @@ src/flowerpower/__init__.py
|
|
11
11
|
src/flowerpower/flowerpower.py
|
12
12
|
src/flowerpower/cfg/__init__.py
|
13
13
|
src/flowerpower/cfg/base.py
|
14
|
+
src/flowerpower/cfg/exceptions.py
|
14
15
|
src/flowerpower/cfg/pipeline/__init__.py
|
15
|
-
src/flowerpower/cfg/pipeline/_schedule.py
|
16
16
|
src/flowerpower/cfg/pipeline/adapter.py
|
17
17
|
src/flowerpower/cfg/pipeline/builder.py
|
18
|
+
src/flowerpower/cfg/pipeline/builder_adapter.py
|
19
|
+
src/flowerpower/cfg/pipeline/builder_executor.py
|
18
20
|
src/flowerpower/cfg/pipeline/run.py
|
19
21
|
src/flowerpower/cfg/project/__init__.py
|
20
22
|
src/flowerpower/cfg/project/adapter.py
|
21
23
|
src/flowerpower/cli/__init__.py
|
22
24
|
src/flowerpower/cli/cfg.py
|
23
|
-
src/flowerpower/cli/mqtt.py
|
24
25
|
src/flowerpower/cli/pipeline.py
|
25
26
|
src/flowerpower/cli/utils.py
|
26
27
|
src/flowerpower/pipeline/__init__.py
|
27
28
|
src/flowerpower/pipeline/base.py
|
29
|
+
src/flowerpower/pipeline/config_manager.py
|
30
|
+
src/flowerpower/pipeline/executor.py
|
28
31
|
src/flowerpower/pipeline/io.py
|
32
|
+
src/flowerpower/pipeline/lifecycle_manager.py
|
29
33
|
src/flowerpower/pipeline/manager.py
|
30
34
|
src/flowerpower/pipeline/pipeline.py
|
31
35
|
src/flowerpower/pipeline/registry.py
|
32
36
|
src/flowerpower/pipeline/visualizer.py
|
33
37
|
src/flowerpower/plugins/io/__init__.py
|
34
|
-
src/flowerpower/plugins/mqtt/__init__.py
|
35
38
|
src/flowerpower/settings/__init__.py
|
36
39
|
src/flowerpower/settings/_backend.py
|
37
40
|
src/flowerpower/settings/executor.py
|
@@ -39,10 +42,16 @@ src/flowerpower/settings/general.py
|
|
39
42
|
src/flowerpower/settings/hamilton.py
|
40
43
|
src/flowerpower/settings/logging.py
|
41
44
|
src/flowerpower/settings/retry.py
|
45
|
+
src/flowerpower/utils/__init__.py
|
46
|
+
src/flowerpower/utils/adapter.py
|
42
47
|
src/flowerpower/utils/callback.py
|
48
|
+
src/flowerpower/utils/config.py
|
49
|
+
src/flowerpower/utils/executor.py
|
50
|
+
src/flowerpower/utils/filesystem.py
|
43
51
|
src/flowerpower/utils/logging.py
|
44
52
|
src/flowerpower/utils/misc.py
|
45
53
|
src/flowerpower/utils/monkey.py
|
46
54
|
src/flowerpower/utils/open_telemetry.py
|
55
|
+
src/flowerpower/utils/security.py
|
47
56
|
src/flowerpower/utils/templates.py
|
48
57
|
tests/test_flowerpower_project.py
|
@@ -17,14 +17,6 @@ flowerpower-io>=0.1.1
|
|
17
17
|
[io-legacy]
|
18
18
|
flowerpower-io[legacy]>=0.1.1
|
19
19
|
|
20
|
-
[mongodb]
|
21
|
-
pymongo>=4.7.2
|
22
|
-
|
23
|
-
[mqtt]
|
24
|
-
paho-mqtt>=2.1.0
|
25
|
-
orjson>=3.10.11
|
26
|
-
mmh3>=5.1.0
|
27
|
-
|
28
20
|
[openlineage]
|
29
21
|
openlineage-python>=1.32.0
|
30
22
|
|
@@ -36,13 +28,5 @@ opentelemetry-exporter-jaeger>=1.21.0
|
|
36
28
|
[ray]
|
37
29
|
ray>=2.34.0
|
38
30
|
|
39
|
-
[tui]
|
40
|
-
textual>=0.85.2
|
41
|
-
|
42
31
|
[ui]
|
43
32
|
sf-hamilton-ui>=0.0.11
|
44
|
-
|
45
|
-
[webserver]
|
46
|
-
sanic>=24.6.0
|
47
|
-
sanic-ext>=23.12.0
|
48
|
-
orjson>=3.10.11
|
@@ -6,6 +6,7 @@ from munch import Munch
|
|
6
6
|
|
7
7
|
from ..settings import CONFIG_DIR, PIPELINES_DIR
|
8
8
|
from .base import BaseConfig
|
9
|
+
from .exceptions import ConfigLoadError, ConfigSaveError, ConfigPathError
|
9
10
|
from .pipeline import PipelineConfig, init_pipeline_config
|
10
11
|
from .project import ProjectConfig, init_project_config
|
11
12
|
|
@@ -21,8 +22,9 @@ class Config(BaseConfig):
|
|
21
22
|
pipeline (PipelineConfig): Configuration for the pipeline.
|
22
23
|
project (ProjectConfig): Configuration for the project.
|
23
24
|
fs (AbstractFileSystem | None): Filesystem abstraction for I/O operations.
|
24
|
-
base_dir (str |
|
25
|
-
|
25
|
+
base_dir (str | None): Base directory for the configuration.
|
26
|
+
base_dir_path (pathlib.Path | None): Base directory as a Path object (property).
|
27
|
+
storage_options (Munch): Options for filesystem operations.
|
26
28
|
|
27
29
|
Example:
|
28
30
|
```python
|
@@ -41,8 +43,61 @@ class Config(BaseConfig):
|
|
41
43
|
pipeline: PipelineConfig = msgspec.field(default_factory=PipelineConfig)
|
42
44
|
project: ProjectConfig = msgspec.field(default_factory=ProjectConfig)
|
43
45
|
fs: AbstractFileSystem | None = None
|
44
|
-
base_dir: str |
|
45
|
-
storage_options:
|
46
|
+
base_dir: str | None = None
|
47
|
+
storage_options: Munch = msgspec.field(default_factory=Munch)
|
48
|
+
|
49
|
+
def __post_init__(self):
|
50
|
+
"""Handle conversion of storage_options from dict to Munch if needed."""
|
51
|
+
if isinstance(self.storage_options, dict):
|
52
|
+
self.storage_options = Munch(self.storage_options)
|
53
|
+
|
54
|
+
# Validate storage_options
|
55
|
+
self._validate_storage_options()
|
56
|
+
|
57
|
+
# Validate base_dir if provided
|
58
|
+
if self.base_dir is not None:
|
59
|
+
self._validate_base_dir()
|
60
|
+
|
61
|
+
def _validate_storage_options(self) -> None:
|
62
|
+
"""Validate storage_options parameter.
|
63
|
+
|
64
|
+
Raises:
|
65
|
+
ValueError: If storage_options contains invalid values.
|
66
|
+
"""
|
67
|
+
if self.storage_options is None:
|
68
|
+
self.storage_options = Munch()
|
69
|
+
|
70
|
+
if not isinstance(self.storage_options, (dict, Munch)):
|
71
|
+
raise ValueError(f"storage_options must be a dict or Munch, got {type(self.storage_options)}")
|
72
|
+
|
73
|
+
def _validate_base_dir(self) -> None:
|
74
|
+
"""Validate base_dir parameter.
|
75
|
+
|
76
|
+
Raises:
|
77
|
+
ValueError: If base_dir contains invalid characters or is empty.
|
78
|
+
"""
|
79
|
+
# Convert Path to string if needed
|
80
|
+
base_dir_str = str(self.base_dir) if hasattr(self.base_dir, '__str__') else self.base_dir
|
81
|
+
|
82
|
+
if not isinstance(base_dir_str, str):
|
83
|
+
raise ValueError(f"base_dir must be a string or Path, got {type(self.base_dir)}")
|
84
|
+
|
85
|
+
# Check for directory traversal attempts (but allow absolute paths)
|
86
|
+
if '..' in base_dir_str:
|
87
|
+
raise ValueError(f"Invalid base_dir: {base_dir_str}. Contains path traversal characters.")
|
88
|
+
|
89
|
+
# Check for empty string
|
90
|
+
if not base_dir_str.strip():
|
91
|
+
raise ValueError("base_dir cannot be empty or whitespace only.")
|
92
|
+
|
93
|
+
@property
|
94
|
+
def base_dir_path(self) -> Path | None:
|
95
|
+
"""Get base_dir as a pathlib.Path object.
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
pathlib.Path | None: The base directory as a Path object, or None if base_dir is None.
|
99
|
+
"""
|
100
|
+
return Path(self.base_dir) if self.base_dir is not None else None
|
46
101
|
|
47
102
|
@classmethod
|
48
103
|
def load(
|
@@ -75,21 +130,29 @@ class Config(BaseConfig):
|
|
75
130
|
```
|
76
131
|
"""
|
77
132
|
if fs is None:
|
78
|
-
|
79
|
-
|
133
|
+
# Use cached filesystem for better performance
|
134
|
+
storage_options_hash = cls._hash_storage_options(storage_options)
|
135
|
+
fs = cls._get_cached_filesystem(base_dir, storage_options_hash)
|
136
|
+
|
137
|
+
try:
|
138
|
+
project = ProjectConfig.load(
|
139
|
+
base_dir=base_dir,
|
140
|
+
name=name,
|
141
|
+
fs=fs,
|
142
|
+
storage_options=storage_options,
|
80
143
|
)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
144
|
+
except ConfigLoadError as e:
|
145
|
+
raise ConfigLoadError(f"Failed to load project configuration: {e}", path=base_dir, original_error=e)
|
146
|
+
|
147
|
+
try:
|
148
|
+
pipeline = PipelineConfig.load(
|
149
|
+
base_dir=base_dir,
|
150
|
+
name=pipeline_name,
|
151
|
+
fs=fs,
|
152
|
+
storage_options=storage_options,
|
153
|
+
)
|
154
|
+
except ConfigLoadError as e:
|
155
|
+
raise ConfigLoadError(f"Failed to load pipeline configuration: {e}", path=base_dir, original_error=e)
|
93
156
|
|
94
157
|
return cls(
|
95
158
|
base_dir=base_dir,
|
@@ -120,9 +183,9 @@ class Config(BaseConfig):
|
|
120
183
|
```
|
121
184
|
"""
|
122
185
|
if fs is None and self.fs is None:
|
123
|
-
|
124
|
-
|
125
|
-
)
|
186
|
+
# Use cached filesystem for better performance
|
187
|
+
storage_options_hash = self._hash_storage_options(storage_options)
|
188
|
+
self.fs = self._get_cached_filesystem(self.base_dir, storage_options_hash)
|
126
189
|
|
127
190
|
if not self.fs.exists(CONFIG_DIR):
|
128
191
|
self.fs.makedirs(CONFIG_DIR)
|
@@ -130,13 +193,22 @@ class Config(BaseConfig):
|
|
130
193
|
if pipeline:
|
131
194
|
self.fs.makedirs(PIPELINES_DIR, exist_ok=True)
|
132
195
|
h_params = self.pipeline.pop("h_params") if self.pipeline.h_params else None
|
133
|
-
|
134
|
-
|
135
|
-
|
196
|
+
# Validate pipeline name to prevent directory traversal
|
197
|
+
if self.pipeline.name and ('..' in self.pipeline.name or '/' in self.pipeline.name or '\\' in self.pipeline.name):
|
198
|
+
raise ConfigPathError(f"Invalid pipeline name: {self.pipeline.name}. Contains path traversal characters.", path=self.pipeline.name)
|
199
|
+
try:
|
200
|
+
self.pipeline.to_yaml(
|
201
|
+
path=f"conf/pipelines/{self.pipeline.name}.yml", fs=self.fs
|
202
|
+
)
|
203
|
+
except ConfigSaveError as e:
|
204
|
+
raise ConfigSaveError(f"Failed to save pipeline configuration: {e}", path=f"conf/pipelines/{self.pipeline.name}.yml", original_error=e)
|
136
205
|
if h_params:
|
137
206
|
self.pipeline.h_params = h_params
|
138
207
|
if project:
|
139
|
-
|
208
|
+
try:
|
209
|
+
self.project.to_yaml("conf/project.yml", self.fs)
|
210
|
+
except ConfigSaveError as e:
|
211
|
+
raise ConfigSaveError(f"Failed to save project configuration: {e}", path="conf/project.yml", original_error=e)
|
140
212
|
|
141
213
|
|
142
214
|
def load(
|
@@ -247,3 +319,49 @@ def init_config(
|
|
247
319
|
storage_options=storage_options,
|
248
320
|
)
|
249
321
|
return Config(pipeline=pipeline_cfg, project=project_cfg, fs=fs, base_dir=base_dir)
|
322
|
+
|
323
|
+
|
324
|
+
# Helper methods for centralized load/save logic
|
325
|
+
@classmethod
|
326
|
+
def _load_config(
|
327
|
+
cls,
|
328
|
+
config_class: type[BaseConfig],
|
329
|
+
base_dir: str,
|
330
|
+
name: str | None,
|
331
|
+
fs: AbstractFileSystem,
|
332
|
+
storage_options: dict | BaseStorageOptions | None,
|
333
|
+
) -> BaseConfig:
|
334
|
+
"""Centralized configuration loading logic.
|
335
|
+
|
336
|
+
Args:
|
337
|
+
config_class: The configuration class to load.
|
338
|
+
base_dir: Base directory for configurations.
|
339
|
+
name: Configuration name.
|
340
|
+
fs: Filesystem instance.
|
341
|
+
storage_options: Options for filesystem.
|
342
|
+
|
343
|
+
Returns:
|
344
|
+
Loaded configuration instance.
|
345
|
+
"""
|
346
|
+
return config_class.load(
|
347
|
+
base_dir=base_dir,
|
348
|
+
name=name,
|
349
|
+
fs=fs,
|
350
|
+
storage_options=storage_options,
|
351
|
+
)
|
352
|
+
|
353
|
+
|
354
|
+
def _save_pipeline_config(self) -> None:
|
355
|
+
"""Save pipeline configuration with proper handling of h_params."""
|
356
|
+
self.fs.makedirs(PIPELINES_DIR, exist_ok=True)
|
357
|
+
h_params = self.pipeline.pop("h_params") if self.pipeline.h_params else None
|
358
|
+
self.pipeline.to_yaml(
|
359
|
+
path=f"conf/pipelines/{self.pipeline.name}.yml", fs=self.fs
|
360
|
+
)
|
361
|
+
if h_params:
|
362
|
+
self.pipeline.h_params = h_params
|
363
|
+
|
364
|
+
|
365
|
+
def _save_project_config(self) -> None:
|
366
|
+
"""Save project configuration."""
|
367
|
+
self.project.to_yaml("conf/project.yml", self.fs)
|