FlowerPower 0.31.0__py3-none-any.whl → 0.31.2__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.
- flowerpower/pipeline/manager.py +34 -5
- flowerpower/pipeline/visualizer.py +25 -11
- flowerpower/utils/misc.py +204 -203
- {flowerpower-0.31.0.dist-info → flowerpower-0.31.2.dist-info}/METADATA +1 -1
- {flowerpower-0.31.0.dist-info → flowerpower-0.31.2.dist-info}/RECORD +9 -9
- {flowerpower-0.31.0.dist-info → flowerpower-0.31.2.dist-info}/WHEEL +0 -0
- {flowerpower-0.31.0.dist-info → flowerpower-0.31.2.dist-info}/entry_points.txt +0 -0
- {flowerpower-0.31.0.dist-info → flowerpower-0.31.2.dist-info}/licenses/LICENSE +0 -0
- {flowerpower-0.31.0.dist-info → flowerpower-0.31.2.dist-info}/top_level.txt +0 -0
flowerpower/pipeline/manager.py
CHANGED
@@ -580,11 +580,32 @@ class PipelineManager:
|
|
580
580
|
"""
|
581
581
|
return self._lifecycle_manager.list_pipelines()
|
582
582
|
|
583
|
-
def show_pipelines(self) -> None:
|
584
|
-
"""Display all available pipelines in a
|
583
|
+
def show_pipelines(self, format: str = "table") -> None:
|
584
|
+
"""Display all available pipelines in a selected format.
|
585
585
|
|
586
|
-
|
586
|
+
Args:
|
587
|
+
format: One of "table", "json", or "yaml". Defaults to "table".
|
587
588
|
"""
|
589
|
+
fmt = (format or "table").lower()
|
590
|
+
if fmt == "table":
|
591
|
+
return self.registry.show_pipelines()
|
592
|
+
names = self._lifecycle_manager.list_pipelines()
|
593
|
+
try:
|
594
|
+
if fmt == "json":
|
595
|
+
import json
|
596
|
+
print(json.dumps(names))
|
597
|
+
return None
|
598
|
+
if fmt == "yaml":
|
599
|
+
import yaml # type: ignore
|
600
|
+
print(yaml.safe_dump(names, sort_keys=False))
|
601
|
+
return None
|
602
|
+
except Exception as e:
|
603
|
+
warnings.warn(
|
604
|
+
f"Failed to format pipelines as {fmt}: {e}. Falling back to table.",
|
605
|
+
RuntimeWarning,
|
606
|
+
)
|
607
|
+
return self.registry.show_pipelines()
|
608
|
+
warnings.warn(f"Unknown format '{format}', using table.", RuntimeWarning)
|
588
609
|
return self.registry.show_pipelines()
|
589
610
|
|
590
611
|
@property
|
@@ -985,7 +1006,13 @@ class PipelineManager:
|
|
985
1006
|
)
|
986
1007
|
|
987
1008
|
# Visualizer Delegations
|
988
|
-
def save_dag(
|
1009
|
+
def save_dag(
|
1010
|
+
self,
|
1011
|
+
name: str,
|
1012
|
+
format: str = "png",
|
1013
|
+
reload: bool = False,
|
1014
|
+
output_path: str | None = None,
|
1015
|
+
) -> str:
|
989
1016
|
"""Save pipeline DAG visualization to a file.
|
990
1017
|
|
991
1018
|
Creates a visual representation of the pipeline's directed acyclic graph (DAG)
|
@@ -1020,7 +1047,9 @@ class PipelineManager:
|
|
1020
1047
|
... reload=True
|
1021
1048
|
... )
|
1022
1049
|
"""
|
1023
|
-
self.visualizer.save_dag(
|
1050
|
+
return self.visualizer.save_dag(
|
1051
|
+
name=name, format=format, reload=reload, output_path=output_path
|
1052
|
+
)
|
1024
1053
|
|
1025
1054
|
def show_dag(
|
1026
1055
|
self, name: str, format: str = "png", reload: bool = False, raw: bool = False
|
@@ -67,7 +67,8 @@ class PipelineVisualizer:
|
|
67
67
|
name: str,
|
68
68
|
format: str = "png",
|
69
69
|
reload: bool = False,
|
70
|
-
|
70
|
+
output_path: str | None = None,
|
71
|
+
) -> str:
|
71
72
|
"""
|
72
73
|
Save an image of the graph of functions for a given pipeline name.
|
73
74
|
|
@@ -86,25 +87,38 @@ class PipelineVisualizer:
|
|
86
87
|
"""
|
87
88
|
dag = self._get_dag_object(name=name, reload=reload)
|
88
89
|
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
90
|
+
# Determine final output path
|
91
|
+
if output_path is None:
|
92
|
+
graph_dir = posixpath.join(self.project_cfg.base_dir, "graphs")
|
93
|
+
self._fs.makedirs(graph_dir, exist_ok=True)
|
94
|
+
base = posixpath.join(graph_dir, name)
|
95
|
+
final_path = f"{base}.{format}"
|
96
|
+
render_path = base
|
97
|
+
else:
|
98
|
+
# If output_path already has an extension, use as-is; otherwise append format
|
99
|
+
if "." in posixpath.basename(output_path):
|
100
|
+
final_path = output_path
|
101
|
+
# Remove extension for graphviz render base path
|
102
|
+
render_path = final_path.rsplit(".", 1)[0]
|
103
|
+
fmt = final_path.rsplit(".", 1)[1]
|
104
|
+
if fmt != format:
|
105
|
+
# Honor explicit extension if it differs from format argument
|
106
|
+
format = fmt
|
107
|
+
else:
|
108
|
+
final_path = f"{output_path}.{format}"
|
109
|
+
render_path = output_path
|
97
110
|
|
98
111
|
# Render the DAG using the graphviz object returned by display_all_functions
|
99
112
|
dag.render(
|
100
|
-
|
113
|
+
render_path, # graphviz appends the format automatically
|
101
114
|
format=format,
|
102
115
|
cleanup=True,
|
103
116
|
view=False,
|
104
117
|
)
|
105
118
|
print(
|
106
|
-
f"📊 Saved graph for [bold blue]{self.project_cfg.name}.{name}[/bold blue] to [green]{
|
119
|
+
f"📊 Saved graph for [bold blue]{self.project_cfg.name}.{name}[/bold blue] to [green]{final_path}[/green]"
|
107
120
|
)
|
121
|
+
return final_path
|
108
122
|
|
109
123
|
def show_dag(
|
110
124
|
self,
|
flowerpower/utils/misc.py
CHANGED
@@ -9,218 +9,219 @@ from typing import Any
|
|
9
9
|
import msgspec
|
10
10
|
from fsspec_utils import AbstractFileSystem, filesystem
|
11
11
|
from .security import validate_file_path
|
12
|
+
from fsspec_utils.utils import run_parallel
|
12
13
|
|
13
|
-
if importlib.util.find_spec("joblib"):
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
# if importlib.util.find_spec("joblib"):
|
15
|
+
# from joblib import Parallel, delayed
|
16
|
+
# from rich.progress import (BarColumn, Progress, TextColumn,
|
17
|
+
# TimeElapsedColumn)
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
# def _prepare_parallel_args(
|
20
|
+
# args: tuple, kwargs: dict
|
21
|
+
# ) -> tuple[list, list, dict, dict, int]:
|
22
|
+
# """Prepare and validate arguments for parallel execution.
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
# Args:
|
25
|
+
# args: Positional arguments
|
26
|
+
# kwargs: Keyword arguments
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
# Returns:
|
29
|
+
# tuple: (iterables, fixed_args, iterable_kwargs, fixed_kwargs, first_iterable_len)
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
31
|
+
# Raises:
|
32
|
+
# ValueError: If no iterable arguments or length mismatch
|
33
|
+
# """
|
34
|
+
# iterables = []
|
35
|
+
# fixed_args = []
|
36
|
+
# iterable_kwargs = {}
|
37
|
+
# fixed_kwargs = {}
|
38
|
+
# first_iterable_len = None
|
39
|
+
|
40
|
+
# # Process positional arguments
|
41
|
+
# for arg in args:
|
42
|
+
# if isinstance(arg, (list, tuple)) and not isinstance(arg[0], (list, tuple)):
|
43
|
+
# iterables.append(arg)
|
44
|
+
# if first_iterable_len is None:
|
45
|
+
# first_iterable_len = len(arg)
|
46
|
+
# elif len(arg) != first_iterable_len:
|
47
|
+
# raise ValueError(
|
48
|
+
# f"Iterable length mismatch: argument has length {len(arg)}, expected {first_iterable_len}"
|
49
|
+
# )
|
50
|
+
# else:
|
51
|
+
# fixed_args.append(arg)
|
52
|
+
|
53
|
+
# # Process keyword arguments
|
54
|
+
# for key, value in kwargs.items():
|
55
|
+
# if isinstance(value, (list, tuple)) and not isinstance(
|
56
|
+
# value[0], (list, tuple)
|
57
|
+
# ):
|
58
|
+
# if first_iterable_len is None:
|
59
|
+
# first_iterable_len = len(value)
|
60
|
+
# elif len(value) != first_iterable_len:
|
61
|
+
# raise ValueError(
|
62
|
+
# f"Iterable length mismatch: {key} has length {len(value)}, expected {first_iterable_len}"
|
63
|
+
# )
|
64
|
+
# iterable_kwargs[key] = value
|
65
|
+
# else:
|
66
|
+
# fixed_kwargs[key] = value
|
67
|
+
|
68
|
+
# if first_iterable_len is None:
|
69
|
+
# raise ValueError("At least one iterable argument is required")
|
70
|
+
|
71
|
+
# return iterables, fixed_args, iterable_kwargs, fixed_kwargs, first_iterable_len
|
72
|
+
|
73
|
+
# def _execute_parallel_with_progress(
|
74
|
+
# func: callable,
|
75
|
+
# iterables: list,
|
76
|
+
# fixed_args: list,
|
77
|
+
# iterable_kwargs: dict,
|
78
|
+
# fixed_kwargs: dict,
|
79
|
+
# param_combinations: list,
|
80
|
+
# parallel_kwargs: dict,
|
81
|
+
# ) -> list:
|
82
|
+
# """Execute parallel tasks with progress tracking.
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
84
|
+
# Args:
|
85
|
+
# func: Function to execute
|
86
|
+
# iterables: List of iterable arguments
|
87
|
+
# fixed_args: List of fixed arguments
|
88
|
+
# iterable_kwargs: Dictionary of iterable keyword arguments
|
89
|
+
# fixed_kwargs: Dictionary of fixed keyword arguments
|
90
|
+
# param_combinations: List of parameter combinations
|
91
|
+
# parallel_kwargs: Parallel execution configuration
|
91
92
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
93
|
+
# Returns:
|
94
|
+
# list: Results from parallel execution
|
95
|
+
# """
|
96
|
+
# results = [None] * len(param_combinations)
|
97
|
+
# with Progress(
|
98
|
+
# TextColumn("[progress.description]{task.description}"),
|
99
|
+
# BarColumn(),
|
100
|
+
# "[progress.percentage]{task.percentage:>3.0f}%",
|
101
|
+
# TimeElapsedColumn(),
|
102
|
+
# transient=True,
|
103
|
+
# ) as progress:
|
104
|
+
# task = progress.add_task(
|
105
|
+
# "Running in parallel...", total=len(param_combinations)
|
106
|
+
# )
|
107
|
+
|
108
|
+
# def wrapper(idx, param_tuple):
|
109
|
+
# res = func(
|
110
|
+
# *(list(param_tuple[: len(iterables)]) + fixed_args),
|
111
|
+
# **{
|
112
|
+
# k: v
|
113
|
+
# for k, v in zip(
|
114
|
+
# iterable_kwargs.keys(), param_tuple[len(iterables) :]
|
115
|
+
# )
|
116
|
+
# },
|
117
|
+
# **fixed_kwargs,
|
118
|
+
# )
|
119
|
+
# progress.update(task, advance=1)
|
120
|
+
# return idx, res
|
121
|
+
#
|
122
|
+
# for idx, result in Parallel(**parallel_kwargs)(
|
123
|
+
# delayed(wrapper)(i, param_tuple)
|
124
|
+
# for i, param_tuple in enumerate(param_combinations)
|
125
|
+
# ):
|
126
|
+
# results[idx] = result
|
127
|
+
# return results
|
128
|
+
|
129
|
+
# def _execute_parallel_without_progress(
|
130
|
+
# func: callable,
|
131
|
+
# iterables: list,
|
132
|
+
# fixed_args: list,
|
133
|
+
# iterable_kwargs: dict,
|
134
|
+
# fixed_kwargs: dict,
|
135
|
+
# param_combinations: list,
|
136
|
+
# parallel_kwargs: dict,
|
137
|
+
# ) -> list:
|
138
|
+
# """Execute parallel tasks without progress tracking.
|
138
139
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
140
|
+
# Args:
|
141
|
+
# func: Function to execute
|
142
|
+
# iterables: List of iterable arguments
|
143
|
+
# fixed_args: List of fixed arguments
|
144
|
+
# iterable_kwargs: Dictionary of iterable keyword arguments
|
145
|
+
# fixed_kwargs: Dictionary of fixed keyword arguments
|
146
|
+
# param_combinations: List of parameter combinations
|
147
|
+
# parallel_kwargs: Parallel execution configuration
|
147
148
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
else:
|
221
|
-
|
222
|
-
|
223
|
-
|
149
|
+
# Returns:
|
150
|
+
# list: Results from parallel execution
|
151
|
+
# """
|
152
|
+
# return Parallel(**parallel_kwargs)(
|
153
|
+
# delayed(func)(
|
154
|
+
# *(list(param_tuple[: len(iterables)]) + fixed_args),
|
155
|
+
# **{
|
156
|
+
# k: v
|
157
|
+
# for k, v in zip(
|
158
|
+
# iterable_kwargs.keys(), param_tuple[len(iterables) :]
|
159
|
+
# )
|
160
|
+
# },
|
161
|
+
# **fixed_kwargs,
|
162
|
+
# )
|
163
|
+
# for param_tuple in param_combinations
|
164
|
+
# )
|
165
|
+
|
166
|
+
# def run_parallel(
|
167
|
+
# func: callable,
|
168
|
+
# *args,
|
169
|
+
# n_jobs: int = -1,
|
170
|
+
# backend: str = "threading",
|
171
|
+
# verbose: bool = True,
|
172
|
+
# **kwargs,
|
173
|
+
# ) -> list[any]:
|
174
|
+
# """Runs a function for a list of parameters in parallel.
|
175
|
+
|
176
|
+
# Args:
|
177
|
+
# func (Callable): function to run in parallel
|
178
|
+
# *args: Positional arguments. Can be single values or iterables
|
179
|
+
# n_jobs (int, optional): Number of joblib workers. Defaults to -1
|
180
|
+
# backend (str, optional): joblib backend. Valid options are
|
181
|
+
# `loky`,`threading`, `mutliprocessing` or `sequential`. Defaults to "threading"
|
182
|
+
# verbose (bool, optional): Show progress bar. Defaults to True
|
183
|
+
# **kwargs: Keyword arguments. Can be single values or iterables
|
184
|
+
|
185
|
+
# Returns:
|
186
|
+
# list[any]: Function output
|
187
|
+
|
188
|
+
# Examples:
|
189
|
+
# >>> # Single iterable argument
|
190
|
+
# >>> run_parallel(func, [1,2,3], fixed_arg=42)
|
191
|
+
|
192
|
+
# >>> # Multiple iterables in args and kwargs
|
193
|
+
# >>> run_parallel(func, [1,2,3], val=[7,8,9], fixed=42)
|
194
|
+
|
195
|
+
# >>> # Only kwargs iterables
|
196
|
+
# >>> run_parallel(func, x=[1,2,3], y=[4,5,6], fixed=42)
|
197
|
+
# """
|
198
|
+
# parallel_kwargs = {"n_jobs": n_jobs, "backend": backend, "verbose": 0}
|
199
|
+
|
200
|
+
# # Prepare and validate arguments
|
201
|
+
# iterables, fixed_args, iterable_kwargs, fixed_kwargs, first_iterable_len = _prepare_parallel_args(
|
202
|
+
# args, kwargs
|
203
|
+
# )
|
204
|
+
|
205
|
+
# # Create parameter combinations
|
206
|
+
# all_iterables = iterables + list(iterable_kwargs.values())
|
207
|
+
# param_combinations = list(zip(*all_iterables))
|
208
|
+
|
209
|
+
# # Execute with or without progress tracking
|
210
|
+
# if not verbose:
|
211
|
+
# return _execute_parallel_without_progress(
|
212
|
+
# func, iterables, fixed_args, iterable_kwargs, fixed_kwargs,
|
213
|
+
# param_combinations, parallel_kwargs
|
214
|
+
# )
|
215
|
+
# else:
|
216
|
+
# return _execute_parallel_with_progress(
|
217
|
+
# func, iterables, fixed_args, iterable_kwargs, fixed_kwargs,
|
218
|
+
# param_combinations, parallel_kwargs
|
219
|
+
# )
|
220
|
+
|
221
|
+
# else:
|
222
|
+
|
223
|
+
# def run_parallel(*args, **kwargs):
|
224
|
+
# raise ImportError("joblib not installed")
|
224
225
|
|
225
226
|
|
226
227
|
def get_partitions_from_path(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: FlowerPower
|
3
|
-
Version: 0.31.
|
3
|
+
Version: 0.31.2
|
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
|
@@ -21,10 +21,10 @@ flowerpower/pipeline/config_manager.py,sha256=0u_Nn_XGafjW3LmhGyp39CP9KXfLo02Z9i
|
|
21
21
|
flowerpower/pipeline/executor.py,sha256=AAiMbNKIQKNrumu1lavG_yVK9rzhPYjZAIt0BxAbJjc,4567
|
22
22
|
flowerpower/pipeline/io.py,sha256=phYJhN4LZ0c6d8_udEQ4C9cGzeV3Ku0hsj0gyE1n2UY,16246
|
23
23
|
flowerpower/pipeline/lifecycle_manager.py,sha256=JKJXdgdwhH8yUYdwlAJp5-dSbDFFb-EqmgiY-O5KIoA,7670
|
24
|
-
flowerpower/pipeline/manager.py,sha256=
|
24
|
+
flowerpower/pipeline/manager.py,sha256=NTjdWu_SzOmeLvnD5ruJzI72UOx_UFLxhNrUipPK43g,42138
|
25
25
|
flowerpower/pipeline/pipeline.py,sha256=x4nQeKC7irJoD6x2RzU52dN4VGWJFSGTDYKNuDhG7fk,15742
|
26
26
|
flowerpower/pipeline/registry.py,sha256=gIc1N7yRm95dEhc5WnZapTK827VeydGxZSYcWFqB10U,30122
|
27
|
-
flowerpower/pipeline/visualizer.py,sha256=
|
27
|
+
flowerpower/pipeline/visualizer.py,sha256=5DI9A7JhiwreRjZPIu5FbQCoI65W0cDC8c2iPkpkOGY,5612
|
28
28
|
flowerpower/plugins/io/__init__.py,sha256=ZmSdKoh3TavJagOz0vUItnEqAh3mAM1QpAWj0KufF_k,222
|
29
29
|
flowerpower/settings/__init__.py,sha256=XKQa8AI9VrX8ievs-suq3Cm6PBt4cJ78ZHVIjUbXCyA,130
|
30
30
|
flowerpower/settings/_backend.py,sha256=Up1RBqAs3jtDUOV-9wEpL68Qmom-dRWMOeHXIh0F3lw,4273
|
@@ -40,14 +40,14 @@ flowerpower/utils/config.py,sha256=JC_kKq9-VJrW7ndWiehm5s-C0JG4_4oBffuwhVbaibQ,1
|
|
40
40
|
flowerpower/utils/executor.py,sha256=AqO1RLxI9uKWX9OPu9uK6yOX2CNQMptRKpvcG2o-tak,6258
|
41
41
|
flowerpower/utils/filesystem.py,sha256=W5kpJ0IERSsI3hQd7XFtbuk9bcIXidl7Qgw2bQVOkd0,6346
|
42
42
|
flowerpower/utils/logging.py,sha256=WUnpoEmr9H5MGFF68jlshhEJcuMtW49M7VVRstmqIAg,1192
|
43
|
-
flowerpower/utils/misc.py,sha256=
|
43
|
+
flowerpower/utils/misc.py,sha256=_BbAXmcbhDm5Y-RpW-HhJ5txu8KoaDQjdmRJwzNs7aM,14649
|
44
44
|
flowerpower/utils/monkey.py,sha256=vJMYANjZI13PNbEQThdX0EFP1_6bGNHgnpF7HwReNyM,58
|
45
45
|
flowerpower/utils/open_telemetry.py,sha256=fQWJWbIQFtKIxMBjAWeF12NGnqT0isO3A3j-DSOv_vE,949
|
46
46
|
flowerpower/utils/security.py,sha256=7TPwD4nqjVm2rFBjJ0qM86wEQql-9j3Jo8udV3mSBoA,7113
|
47
47
|
flowerpower/utils/templates.py,sha256=ouyEeSDqa9PjW8c32fGpcINlpC0WToawRFZkMPtwsLE,1591
|
48
|
-
flowerpower-0.31.
|
49
|
-
flowerpower-0.31.
|
50
|
-
flowerpower-0.31.
|
51
|
-
flowerpower-0.31.
|
52
|
-
flowerpower-0.31.
|
53
|
-
flowerpower-0.31.
|
48
|
+
flowerpower-0.31.2.dist-info/licenses/LICENSE,sha256=9AkLexxrmr0aBgSHiqxpJk9wgazpP1CTJyiDyr56J9k,1063
|
49
|
+
flowerpower-0.31.2.dist-info/METADATA,sha256=Gv1RElnrVxJg_h-INW7QnoKkjy5G0OYjrzu55AsDwnw,17202
|
50
|
+
flowerpower-0.31.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
51
|
+
flowerpower-0.31.2.dist-info/entry_points.txt,sha256=61X11i5a2IwC9LBiP20XCDl5zMOigGCjMCx17B7bDbQ,52
|
52
|
+
flowerpower-0.31.2.dist-info/top_level.txt,sha256=VraH4WtEUfSxs5L-rXwDQhzQb9eLHTUtgvmFZ2dAYnA,12
|
53
|
+
flowerpower-0.31.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|