FlowerPower 0.30.0__py3-none-any.whl → 0.31.1__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.
Files changed (38) hide show
  1. flowerpower/cfg/__init__.py +143 -25
  2. flowerpower/cfg/base.py +132 -11
  3. flowerpower/cfg/exceptions.py +53 -0
  4. flowerpower/cfg/pipeline/__init__.py +151 -35
  5. flowerpower/cfg/pipeline/adapter.py +1 -0
  6. flowerpower/cfg/pipeline/builder.py +24 -25
  7. flowerpower/cfg/pipeline/builder_adapter.py +142 -0
  8. flowerpower/cfg/pipeline/builder_executor.py +101 -0
  9. flowerpower/cfg/pipeline/run.py +99 -40
  10. flowerpower/cfg/project/__init__.py +59 -14
  11. flowerpower/cfg/project/adapter.py +6 -0
  12. flowerpower/cli/__init__.py +8 -2
  13. flowerpower/cli/cfg.py +0 -38
  14. flowerpower/cli/pipeline.py +121 -83
  15. flowerpower/cli/utils.py +120 -71
  16. flowerpower/flowerpower.py +94 -120
  17. flowerpower/pipeline/config_manager.py +180 -0
  18. flowerpower/pipeline/executor.py +126 -0
  19. flowerpower/pipeline/lifecycle_manager.py +231 -0
  20. flowerpower/pipeline/manager.py +121 -274
  21. flowerpower/pipeline/pipeline.py +66 -278
  22. flowerpower/pipeline/registry.py +45 -4
  23. flowerpower/utils/__init__.py +19 -0
  24. flowerpower/utils/adapter.py +286 -0
  25. flowerpower/utils/callback.py +73 -67
  26. flowerpower/utils/config.py +306 -0
  27. flowerpower/utils/executor.py +178 -0
  28. flowerpower/utils/filesystem.py +194 -0
  29. flowerpower/utils/misc.py +312 -138
  30. flowerpower/utils/security.py +221 -0
  31. {flowerpower-0.30.0.dist-info → flowerpower-0.31.1.dist-info}/METADATA +2 -2
  32. flowerpower-0.31.1.dist-info/RECORD +53 -0
  33. flowerpower/cfg/pipeline/_schedule.py +0 -32
  34. flowerpower-0.30.0.dist-info/RECORD +0 -42
  35. {flowerpower-0.30.0.dist-info → flowerpower-0.31.1.dist-info}/WHEEL +0 -0
  36. {flowerpower-0.30.0.dist-info → flowerpower-0.31.1.dist-info}/entry_points.txt +0 -0
  37. {flowerpower-0.30.0.dist-info → flowerpower-0.31.1.dist-info}/licenses/LICENSE +0 -0
  38. {flowerpower-0.30.0.dist-info → flowerpower-0.31.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,221 @@
1
+ """Security utilities for input validation and sanitization."""
2
+
3
+ import os
4
+ import re
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Callable
10
+
11
+
12
+ class SecurityError(Exception):
13
+ """Raised when security validation fails."""
14
+ pass
15
+
16
+
17
+ def validate_file_path(path: Union[str, Path],
18
+ allowed_extensions: Optional[List[str]] = None,
19
+ allow_absolute: bool = True,
20
+ allow_relative: bool = True) -> Path:
21
+ """Validate and sanitize file paths to prevent directory traversal attacks.
22
+
23
+ Args:
24
+ path: File path to validate
25
+ allowed_extensions: List of allowed file extensions (e.g., ['.yaml', '.yml'])
26
+ allow_absolute: Whether to allow absolute paths
27
+ allow_relative: Whether to allow relative paths
28
+
29
+ Returns:
30
+ Validated Path object
31
+
32
+ Raises:
33
+ SecurityError: If path is invalid or potentially dangerous
34
+ ValueError: If path is empty or None
35
+ """
36
+ if not path:
37
+ raise ValueError("Path cannot be empty or None")
38
+
39
+ # Convert to Path object
40
+ path_obj = Path(path)
41
+
42
+ # Check for directory traversal attempts
43
+ path_str = str(path_obj)
44
+ if '..' in path_obj.parts or path_str.startswith('..'):
45
+ raise SecurityError(f"Directory traversal detected in path: {path}")
46
+
47
+ # Check absolute vs relative path restrictions
48
+ if path_obj.is_absolute() and not allow_absolute:
49
+ raise SecurityError(f"Absolute paths not allowed: {path}")
50
+
51
+ if not path_obj.is_absolute() and not allow_relative:
52
+ raise SecurityError(f"Relative paths not allowed: {path}")
53
+
54
+ # Validate file extension if specified
55
+ if allowed_extensions:
56
+ if not path_obj.suffix.lower() in [ext.lower() for ext in allowed_extensions]:
57
+ raise SecurityError(
58
+ f"File extension '{path_obj.suffix}' not allowed. "
59
+ f"Allowed: {allowed_extensions}"
60
+ )
61
+
62
+ # Check for potentially dangerous characters
63
+ dangerous_chars = ['|', '&', ';', '`', '$', '<', '>', '"', "'"]
64
+ if any(char in path_str for char in dangerous_chars):
65
+ raise SecurityError(f"Dangerous characters detected in path: {path}")
66
+
67
+ return path_obj
68
+
69
+
70
+ def validate_pipeline_name(name: str) -> str:
71
+ """Validate pipeline name to prevent injection attacks.
72
+
73
+ Args:
74
+ name: Pipeline name to validate
75
+
76
+ Returns:
77
+ Validated name
78
+
79
+ Raises:
80
+ ValueError: If name is invalid
81
+ SecurityError: If name contains dangerous characters
82
+ """
83
+ if not name or not isinstance(name, str):
84
+ raise ValueError("Pipeline name must be a non-empty string")
85
+
86
+ name = name.strip()
87
+ if not name:
88
+ raise ValueError("Pipeline name cannot be empty or only whitespace")
89
+
90
+ # Check for dangerous characters
91
+ if not re.match(r'^[a-zA-Z0-9_-]+$', name):
92
+ raise SecurityError(
93
+ f"Pipeline name '{name}' contains invalid characters. "
94
+ "Only alphanumeric, underscore, and hyphen are allowed."
95
+ )
96
+
97
+ # Check length constraints
98
+ if len(name) > 100:
99
+ raise SecurityError(f"Pipeline name too long: {len(name)} > 100 characters")
100
+
101
+ return name
102
+
103
+
104
+ def validate_config_dict(config: Dict[str, Any],
105
+ allowed_keys: Optional[List[str]] = None,
106
+ max_depth: int = 10) -> Dict[str, Any]:
107
+ """Validate configuration dictionary to prevent malicious content.
108
+
109
+ Args:
110
+ config: Configuration dictionary to validate
111
+ allowed_keys: List of allowed top-level keys
112
+ max_depth: Maximum nesting depth to prevent DoS attacks
113
+
114
+ Returns:
115
+ Validated configuration dictionary
116
+
117
+ Raises:
118
+ SecurityError: If configuration contains dangerous content
119
+ ValueError: If configuration is invalid
120
+ """
121
+ if not isinstance(config, dict):
122
+ raise ValueError("Configuration must be a dictionary")
123
+
124
+ # Check for allowed keys
125
+ if allowed_keys:
126
+ invalid_keys = set(config.keys()) - set(allowed_keys)
127
+ if invalid_keys:
128
+ raise SecurityError(f"Invalid configuration keys: {invalid_keys}")
129
+
130
+ # Check nesting depth
131
+ def check_depth(obj, depth=0):
132
+ if depth > max_depth:
133
+ raise SecurityError(f"Configuration nesting too deep: {depth} > {max_depth}")
134
+
135
+ if isinstance(obj, dict):
136
+ for value in obj.values():
137
+ check_depth(value, depth + 1)
138
+ elif isinstance(obj, (list, tuple)):
139
+ for item in obj:
140
+ check_depth(item, depth + 1)
141
+
142
+ check_depth(config)
143
+
144
+ return config
145
+
146
+
147
+ def sanitize_log_data(data: Any) -> Any:
148
+ """Sanitize data for safe logging to prevent log injection.
149
+
150
+ Args:
151
+ data: Data to sanitize for logging
152
+
153
+ Returns:
154
+ Sanitized data safe for logging
155
+ """
156
+ if isinstance(data, str):
157
+ # Remove potential log injection characters
158
+ sanitized = re.sub(r'[\r\n\t]', ' ', data)
159
+ # Limit length to prevent log flooding
160
+ if len(sanitized) > 1000:
161
+ sanitized = sanitized[:997] + "..."
162
+ return sanitized
163
+ elif isinstance(data, (dict, list)):
164
+ # For complex objects, convert to string and sanitize
165
+ return sanitize_log_data(str(data))
166
+ else:
167
+ return data
168
+
169
+
170
+ def validate_executor_type(executor_type: str) -> str:
171
+ """Validate executor type to prevent arbitrary code execution.
172
+
173
+ Args:
174
+ executor_type: Executor type string to validate
175
+
176
+ Returns:
177
+ Validated executor type
178
+
179
+ Raises:
180
+ SecurityError: If executor type is invalid or dangerous
181
+ """
182
+ if not executor_type or not isinstance(executor_type, str):
183
+ raise ValueError("Executor type must be a non-empty string")
184
+
185
+ allowed_executors = {
186
+ 'synchronous', 'threadpool', 'processpool', 'ray', 'dask'
187
+ }
188
+
189
+ if executor_type not in allowed_executors:
190
+ raise SecurityError(
191
+ f"Invalid executor type: {executor_type}. "
192
+ f"Allowed types: {allowed_executors}"
193
+ )
194
+
195
+ return executor_type
196
+
197
+
198
+ def validate_callback_function(callback: Any) -> bool:
199
+ """Validate callback function to ensure it's safe to execute.
200
+
201
+ Args:
202
+ callback: Callback function or callable to validate
203
+
204
+ Returns:
205
+ True if callback is valid
206
+
207
+ Raises:
208
+ SecurityError: If callback is dangerous or invalid
209
+ """
210
+ if callback is None:
211
+ return True
212
+
213
+ if not callable(callback):
214
+ raise SecurityError("Callback must be callable")
215
+
216
+ # Check if it's a built-in function that could be dangerous
217
+ dangerous_functions = {'eval', 'exec', 'compile', '__import__'}
218
+ if hasattr(callback, '__name__') and callback.__name__ in dangerous_functions:
219
+ raise SecurityError(f"Dangerous callback function: {callback.__name__}")
220
+
221
+ return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FlowerPower
3
- Version: 0.30.0
3
+ Version: 0.31.1
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
@@ -11,7 +11,7 @@ Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: duration-parser>=1.0.1
13
13
  Requires-Dist: fsspec>=2024.10.0
14
- Requires-Dist: fsspec-utils[full]>=0.1.0
14
+ Requires-Dist: fsspec-utils>=0.1.0
15
15
  Requires-Dist: humanize>=4.12.2
16
16
  Requires-Dist: msgspec>=0.19.0
17
17
  Requires-Dist: munch>=4.0.0
@@ -0,0 +1,53 @@
1
+ flowerpower/__init__.py,sha256=nHFrD6twJyR-Ti6Yn0lLcyXXcKOi0tFfOD96oAu17Js,431
2
+ flowerpower/flowerpower.py,sha256=RCQFRvzZhuxmaIw8Q28lCb2XwArFmTNcUbHqxMcmRZk,21422
3
+ flowerpower/cfg/__init__.py,sha256=bEvxYCjYGO5djgjiYmLMIMrrSB2uaEdA7LbcVOT_6Ow,13345
4
+ flowerpower/cfg/base.py,sha256=-hYLxx7aoSvh_w_GRefPRicT3M5L3fTsUtCnhYDiQF8,9907
5
+ flowerpower/cfg/exceptions.py,sha256=zUJabfUekr_BFLehv_v7SewPTUkC-nZ3vcbA7iB8ujc,1628
6
+ flowerpower/cfg/pipeline/__init__.py,sha256=e9IMjpFBo61SiEg7aVH7AYyqwfZeSYn7YhoNdKvpUKE,13820
7
+ flowerpower/cfg/pipeline/adapter.py,sha256=PGRo-4-Dorevh9HttVDmVNvyX1BWfNj_SQ5kcqYOXuA,2275
8
+ flowerpower/cfg/pipeline/builder.py,sha256=drQv5j6fAygfVk3ShawuYQPZbfuNYYqbLoGdA8o6NCQ,13215
9
+ flowerpower/cfg/pipeline/builder_adapter.py,sha256=UJRdZKkzAzklA-JtxpZ6Atp2oxWsoYY5mWejXykOvDQ,4526
10
+ flowerpower/cfg/pipeline/builder_executor.py,sha256=GArx-PKTt3AujqrJE2ShvfZ5rIQu9oGPKhfDaddqsVY,3185
11
+ flowerpower/cfg/pipeline/run.py,sha256=6a8tYuxjvTGiE6E7M7kd0fTAZtJyHUnWoRb_dIAXVMA,8488
12
+ flowerpower/cfg/project/__init__.py,sha256=WJpjSV8N3e8xpBDAQawOZa--fflnQ0Efz_hl4hmV_p4,6311
13
+ flowerpower/cfg/project/adapter.py,sha256=oI_LIs-DObJWNcb3W39h4OqrfgBajwA_QQtPA7Gi_Eo,1686
14
+ flowerpower/cli/__init__.py,sha256=z0VqloACt6C1hHti1r-_ZP9XW4S4hxpJp2sR8wKErVw,5236
15
+ flowerpower/cli/cfg.py,sha256=Ic8nXHm_myOx6uZgZAjs3OU8vWoZghdNG6pC1mdeM_M,67
16
+ flowerpower/cli/pipeline.py,sha256=VjAHgtvhX9wFSGHmQ2hYpz3QCBoJwvUkJwJvza6nbvA,25585
17
+ flowerpower/cli/utils.py,sha256=wBaPE_6VC8Bgoz_uxfeXBiVFpYY_1iQh-_GKqBuIQbI,6714
18
+ flowerpower/pipeline/__init__.py,sha256=ltr4LQnM5Boa9w7t-lNVmmcSglIkTPuIoeYnEKWU4Og,122
19
+ flowerpower/pipeline/base.py,sha256=oQSDfEAyY2_kDRlHNnHr6ihZvfGeOjyMJRPKob1i7U8,3560
20
+ flowerpower/pipeline/config_manager.py,sha256=0u_Nn_XGafjW3LmhGyp39CP9KXfLo02Z9ivDAsHvKmA,6166
21
+ flowerpower/pipeline/executor.py,sha256=AAiMbNKIQKNrumu1lavG_yVK9rzhPYjZAIt0BxAbJjc,4567
22
+ flowerpower/pipeline/io.py,sha256=phYJhN4LZ0c6d8_udEQ4C9cGzeV3Ku0hsj0gyE1n2UY,16246
23
+ flowerpower/pipeline/lifecycle_manager.py,sha256=JKJXdgdwhH8yUYdwlAJp5-dSbDFFb-EqmgiY-O5KIoA,7670
24
+ flowerpower/pipeline/manager.py,sha256=XLrCY5x9WmQithe0QmeYonR4LyqzqSQHR-y7cL6DKr4,41143
25
+ flowerpower/pipeline/pipeline.py,sha256=x4nQeKC7irJoD6x2RzU52dN4VGWJFSGTDYKNuDhG7fk,15742
26
+ flowerpower/pipeline/registry.py,sha256=gIc1N7yRm95dEhc5WnZapTK827VeydGxZSYcWFqB10U,30122
27
+ flowerpower/pipeline/visualizer.py,sha256=EVpjv-TUe1zGvdEAWyShJcVXurm02W0jkLbj7z1uAv4,4953
28
+ flowerpower/plugins/io/__init__.py,sha256=ZmSdKoh3TavJagOz0vUItnEqAh3mAM1QpAWj0KufF_k,222
29
+ flowerpower/settings/__init__.py,sha256=XKQa8AI9VrX8ievs-suq3Cm6PBt4cJ78ZHVIjUbXCyA,130
30
+ flowerpower/settings/_backend.py,sha256=Up1RBqAs3jtDUOV-9wEpL68Qmom-dRWMOeHXIh0F3lw,4273
31
+ flowerpower/settings/executor.py,sha256=vNF383g1gMGkq_CXUzateGMNcJZig-vHkVVb0Hi_b74,249
32
+ flowerpower/settings/general.py,sha256=RxY6PGF_L8ApFlLPHulZ2I8_-aHYqOj63fUu9kSQTjI,227
33
+ flowerpower/settings/hamilton.py,sha256=GVzWKz3B-wy07etY1mNUstEa4DFrQ_lM2cjE0qG_6qw,623
34
+ flowerpower/settings/logging.py,sha256=BHahxfuiofByiTU8TYQ2AObPDRz9SYH-MalSEu1DRro,71
35
+ flowerpower/settings/retry.py,sha256=W3AAVSxmTUAeeSbzRGA37RxqtKKyUdi2MkwDzCsXP94,181
36
+ flowerpower/utils/__init__.py,sha256=SGFNcfks88cBRoxQnLIfHX3BTXcKf-SbWo-zxklXDJY,533
37
+ flowerpower/utils/adapter.py,sha256=3axZ1QIf72x_2AiS7F7X2ySrHkZQnhMnpGel6-9psVU,9645
38
+ flowerpower/utils/callback.py,sha256=ZIDMGwR30m35Fmj-mChkgAhZ0Zf8HeNtsGdsZ727hIQ,6916
39
+ flowerpower/utils/config.py,sha256=JC_kKq9-VJrW7ndWiehm5s-C0JG4_4oBffuwhVbaibQ,11507
40
+ flowerpower/utils/executor.py,sha256=AqO1RLxI9uKWX9OPu9uK6yOX2CNQMptRKpvcG2o-tak,6258
41
+ flowerpower/utils/filesystem.py,sha256=W5kpJ0IERSsI3hQd7XFtbuk9bcIXidl7Qgw2bQVOkd0,6346
42
+ flowerpower/utils/logging.py,sha256=WUnpoEmr9H5MGFF68jlshhEJcuMtW49M7VVRstmqIAg,1192
43
+ flowerpower/utils/misc.py,sha256=_BbAXmcbhDm5Y-RpW-HhJ5txu8KoaDQjdmRJwzNs7aM,14649
44
+ flowerpower/utils/monkey.py,sha256=vJMYANjZI13PNbEQThdX0EFP1_6bGNHgnpF7HwReNyM,58
45
+ flowerpower/utils/open_telemetry.py,sha256=fQWJWbIQFtKIxMBjAWeF12NGnqT0isO3A3j-DSOv_vE,949
46
+ flowerpower/utils/security.py,sha256=7TPwD4nqjVm2rFBjJ0qM86wEQql-9j3Jo8udV3mSBoA,7113
47
+ flowerpower/utils/templates.py,sha256=ouyEeSDqa9PjW8c32fGpcINlpC0WToawRFZkMPtwsLE,1591
48
+ flowerpower-0.31.1.dist-info/licenses/LICENSE,sha256=9AkLexxrmr0aBgSHiqxpJk9wgazpP1CTJyiDyr56J9k,1063
49
+ flowerpower-0.31.1.dist-info/METADATA,sha256=TgvnINYIvNKmijCZg0U11cHEFzC2eEA3LzO_8YuwdFc,17202
50
+ flowerpower-0.31.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
+ flowerpower-0.31.1.dist-info/entry_points.txt,sha256=61X11i5a2IwC9LBiP20XCDl5zMOigGCjMCx17B7bDbQ,52
52
+ flowerpower-0.31.1.dist-info/top_level.txt,sha256=VraH4WtEUfSxs5L-rXwDQhzQb9eLHTUtgvmFZ2dAYnA,12
53
+ flowerpower-0.31.1.dist-info/RECORD,,
@@ -1,32 +0,0 @@
1
- import datetime as dt
2
-
3
- import msgspec
4
- from munch import munchify
5
-
6
- from ..base import BaseConfig
7
-
8
-
9
- class ScheduleConfig(BaseConfig):
10
- cron: str | dict | None = msgspec.field(default=None)
11
- interval: str | int | dict | None = msgspec.field(default=None)
12
- date: str | None = msgspec.field(default=None)
13
-
14
- def __post_init__(self):
15
- if isinstance(self.date, str):
16
- try:
17
- self.date = dt.datetime.fromisoformat(self.date)
18
- except ValueError:
19
- raise ValueError(
20
- f"Invalid date format: {self.date}. Expected ISO format."
21
- )
22
- if isinstance(self.cron, dict):
23
- self.cron = munchify(self.cron)
24
- if isinstance(self.interval, dict):
25
- self.interval = munchify(self.interval)
26
-
27
-
28
- # class ScheduleConfig(BaseConfig):
29
- # run: ScheduleRunConfig = msgspec.field(default_factory=ScheduleRunConfig)
30
- # trigger: ScheduleTriggerConfig = msgspec.field(
31
- # default_factory=ScheduleTriggerConfig
32
- # )
@@ -1,42 +0,0 @@
1
- flowerpower/__init__.py,sha256=nHFrD6twJyR-Ti6Yn0lLcyXXcKOi0tFfOD96oAu17Js,431
2
- flowerpower/flowerpower.py,sha256=-taA0uemnoy1B5ugNxzwQ_Jir8jfmaGNXTk_uewaN88,23755
3
- flowerpower/cfg/__init__.py,sha256=aWeKlBNgJ4LCtWBzZns5RmQm9GN9SpTbozE-NsS2_9w,8481
4
- flowerpower/cfg/base.py,sha256=IpvCcUvGTjmjN69D34IFNn692hxTwZzYQJ1utgKANWo,4795
5
- flowerpower/cfg/pipeline/__init__.py,sha256=HSUlDt9smo-zPqOljEl329CQg8qsd0EJfCH1FOtuMdk,9276
6
- flowerpower/cfg/pipeline/_schedule.py,sha256=kaDyij3eUj0u6VdmJW4x_AQbSAZ-r7s2Fk7QFkXU5JQ,1029
7
- flowerpower/cfg/pipeline/adapter.py,sha256=uBKV6BZlsRRqSYNyC1oEWPchsaH7rFPCBobG5BrF3ss,2265
8
- flowerpower/cfg/pipeline/builder.py,sha256=rZ-cspbV-nwtckvOA49vt7DNJNb-gRLHHWiejmJPsFs,13192
9
- flowerpower/cfg/pipeline/run.py,sha256=cirZbiHsY8D87idBvbfNv1gEtKpgv3xLfiiFIXJFxrA,6733
10
- flowerpower/cfg/project/__init__.py,sha256=bMMZ5Zo3YgkgOGt82H1mW8I4WnoCUpa0bSLPpZiB7K4,4560
11
- flowerpower/cfg/project/adapter.py,sha256=2k2U25NziDEiUacLTjxaSxOVkaQBRt6ECWGRGX0v4J0,1481
12
- flowerpower/cli/__init__.py,sha256=RkJT3mPlSOi55dNNP8kpm_omF0zEfbmWVP16N-lkijE,4897
13
- flowerpower/cli/cfg.py,sha256=P7qEcjraitMxbzVWJMqWeitIdpUkW41QkUi7ol0ksW0,1455
14
- flowerpower/cli/pipeline.py,sha256=I58cWjK7J4dkXdJXIePcPiD7iILnYqoje0ztkR5mNwg,21958
15
- flowerpower/cli/utils.py,sha256=tsxvKIqUhl4m9IzuaSoc5a3_gb6Fu4LYyV8fVodqIdA,5127
16
- flowerpower/pipeline/__init__.py,sha256=ltr4LQnM5Boa9w7t-lNVmmcSglIkTPuIoeYnEKWU4Og,122
17
- flowerpower/pipeline/base.py,sha256=oQSDfEAyY2_kDRlHNnHr6ihZvfGeOjyMJRPKob1i7U8,3560
18
- flowerpower/pipeline/io.py,sha256=phYJhN4LZ0c6d8_udEQ4C9cGzeV3Ku0hsj0gyE1n2UY,16246
19
- flowerpower/pipeline/manager.py,sha256=gJMWe_T1WpxO2H9GDrNtf61S4oJuLugMHwX82jqAuEU,47579
20
- flowerpower/pipeline/pipeline.py,sha256=8Vhc3GGoAJI-5zv8qX4gZ5JwJrTdVoiXHWNpv5OeKGw,26207
21
- flowerpower/pipeline/registry.py,sha256=iHdEBPiRpZorAUe6sDkqONoP8t6cicdQYGdK8UyxuAQ,28791
22
- flowerpower/pipeline/visualizer.py,sha256=EVpjv-TUe1zGvdEAWyShJcVXurm02W0jkLbj7z1uAv4,4953
23
- flowerpower/plugins/io/__init__.py,sha256=ZmSdKoh3TavJagOz0vUItnEqAh3mAM1QpAWj0KufF_k,222
24
- flowerpower/settings/__init__.py,sha256=XKQa8AI9VrX8ievs-suq3Cm6PBt4cJ78ZHVIjUbXCyA,130
25
- flowerpower/settings/_backend.py,sha256=Up1RBqAs3jtDUOV-9wEpL68Qmom-dRWMOeHXIh0F3lw,4273
26
- flowerpower/settings/executor.py,sha256=vNF383g1gMGkq_CXUzateGMNcJZig-vHkVVb0Hi_b74,249
27
- flowerpower/settings/general.py,sha256=RxY6PGF_L8ApFlLPHulZ2I8_-aHYqOj63fUu9kSQTjI,227
28
- flowerpower/settings/hamilton.py,sha256=GVzWKz3B-wy07etY1mNUstEa4DFrQ_lM2cjE0qG_6qw,623
29
- flowerpower/settings/logging.py,sha256=BHahxfuiofByiTU8TYQ2AObPDRz9SYH-MalSEu1DRro,71
30
- flowerpower/settings/retry.py,sha256=W3AAVSxmTUAeeSbzRGA37RxqtKKyUdi2MkwDzCsXP94,181
31
- flowerpower/utils/callback.py,sha256=sGYSrEbnl0xfRa1X-mA-om3erpH7pmpsWdyPQ9HpU7E,6736
32
- flowerpower/utils/logging.py,sha256=WUnpoEmr9H5MGFF68jlshhEJcuMtW49M7VVRstmqIAg,1192
33
- flowerpower/utils/misc.py,sha256=gY1KVJp4lF4S0THUu1LUrjXaf87TzLO-TcwHxZZh2Zk,8414
34
- flowerpower/utils/monkey.py,sha256=vJMYANjZI13PNbEQThdX0EFP1_6bGNHgnpF7HwReNyM,58
35
- flowerpower/utils/open_telemetry.py,sha256=fQWJWbIQFtKIxMBjAWeF12NGnqT0isO3A3j-DSOv_vE,949
36
- flowerpower/utils/templates.py,sha256=ouyEeSDqa9PjW8c32fGpcINlpC0WToawRFZkMPtwsLE,1591
37
- flowerpower-0.30.0.dist-info/licenses/LICENSE,sha256=9AkLexxrmr0aBgSHiqxpJk9wgazpP1CTJyiDyr56J9k,1063
38
- flowerpower-0.30.0.dist-info/METADATA,sha256=3nPehMWbDcryhbkgbieU-_13I_8S3-1aNL54bMdjdDs,17208
39
- flowerpower-0.30.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
- flowerpower-0.30.0.dist-info/entry_points.txt,sha256=61X11i5a2IwC9LBiP20XCDl5zMOigGCjMCx17B7bDbQ,52
41
- flowerpower-0.30.0.dist-info/top_level.txt,sha256=VraH4WtEUfSxs5L-rXwDQhzQb9eLHTUtgvmFZ2dAYnA,12
42
- flowerpower-0.30.0.dist-info/RECORD,,