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.
@@ -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 formatted table.
583
+ def show_pipelines(self, format: str = "table") -> None:
584
+ """Display all available pipelines in a selected format.
585
585
 
586
- Uses rich formatting for terminal display.
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(self, name: str, format: str = "png", reload: bool = False) -> None:
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(name=name, format=format, reload=reload)
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
- # Use project_cfg attributes for path and filesystem access
90
- graph_dir = posixpath.join(self.project_cfg.base_dir, "graphs")
91
- self._fs.makedirs(graph_dir, exist_ok=True)
92
-
93
- output_path = posixpath.join(
94
- graph_dir, name
95
- ) # Output filename is just the pipeline name
96
- output_path_with_ext = f"{output_path}.{format}"
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
- output_path, # graphviz appends the format automatically
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]{output_path_with_ext}[/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
- from joblib import Parallel, delayed
15
- from rich.progress import (BarColumn, Progress, TextColumn,
16
- TimeElapsedColumn)
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
- def _prepare_parallel_args(
19
- args: tuple, kwargs: dict
20
- ) -> tuple[list, list, dict, dict, int]:
21
- """Prepare and validate arguments for parallel execution.
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
- Args:
24
- args: Positional arguments
25
- kwargs: Keyword arguments
24
+ # Args:
25
+ # args: Positional arguments
26
+ # kwargs: Keyword arguments
26
27
 
27
- Returns:
28
- tuple: (iterables, fixed_args, iterable_kwargs, fixed_kwargs, first_iterable_len)
28
+ # Returns:
29
+ # tuple: (iterables, fixed_args, iterable_kwargs, fixed_kwargs, first_iterable_len)
29
30
 
30
- Raises:
31
- ValueError: If no iterable arguments or length mismatch
32
- """
33
- iterables = []
34
- fixed_args = []
35
- iterable_kwargs = {}
36
- fixed_kwargs = {}
37
- first_iterable_len = None
38
-
39
- # Process positional arguments
40
- for arg in args:
41
- if isinstance(arg, (list, tuple)) and not isinstance(arg[0], (list, tuple)):
42
- iterables.append(arg)
43
- if first_iterable_len is None:
44
- first_iterable_len = len(arg)
45
- elif len(arg) != first_iterable_len:
46
- raise ValueError(
47
- f"Iterable length mismatch: argument has length {len(arg)}, expected {first_iterable_len}"
48
- )
49
- else:
50
- fixed_args.append(arg)
51
-
52
- # Process keyword arguments
53
- for key, value in kwargs.items():
54
- if isinstance(value, (list, tuple)) and not isinstance(
55
- value[0], (list, tuple)
56
- ):
57
- if first_iterable_len is None:
58
- first_iterable_len = len(value)
59
- elif len(value) != first_iterable_len:
60
- raise ValueError(
61
- f"Iterable length mismatch: {key} has length {len(value)}, expected {first_iterable_len}"
62
- )
63
- iterable_kwargs[key] = value
64
- else:
65
- fixed_kwargs[key] = value
66
-
67
- if first_iterable_len is None:
68
- raise ValueError("At least one iterable argument is required")
69
-
70
- return iterables, fixed_args, iterable_kwargs, fixed_kwargs, first_iterable_len
71
-
72
- def _execute_parallel_with_progress(
73
- func: callable,
74
- iterables: list,
75
- fixed_args: list,
76
- iterable_kwargs: dict,
77
- fixed_kwargs: dict,
78
- param_combinations: list,
79
- parallel_kwargs: dict,
80
- ) -> list:
81
- """Execute parallel tasks with progress tracking.
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
- Args:
84
- func: Function to execute
85
- iterables: List of iterable arguments
86
- fixed_args: List of fixed arguments
87
- iterable_kwargs: Dictionary of iterable keyword arguments
88
- fixed_kwargs: Dictionary of fixed keyword arguments
89
- param_combinations: List of parameter combinations
90
- parallel_kwargs: Parallel execution configuration
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
- Returns:
93
- list: Results from parallel execution
94
- """
95
- results = [None] * len(param_combinations)
96
- with Progress(
97
- TextColumn("[progress.description]{task.description}"),
98
- BarColumn(),
99
- "[progress.percentage]{task.percentage:>3.0f}%",
100
- TimeElapsedColumn(),
101
- transient=True,
102
- ) as progress:
103
- task = progress.add_task(
104
- "Running in parallel...", total=len(param_combinations)
105
- )
106
-
107
- def wrapper(idx, param_tuple):
108
- res = func(
109
- *(list(param_tuple[: len(iterables)]) + fixed_args),
110
- **{
111
- k: v
112
- for k, v in zip(
113
- iterable_kwargs.keys(), param_tuple[len(iterables) :]
114
- )
115
- },
116
- **fixed_kwargs,
117
- )
118
- progress.update(task, advance=1)
119
- return idx, res
120
-
121
- for idx, result in Parallel(**parallel_kwargs)(
122
- delayed(wrapper)(i, param_tuple)
123
- for i, param_tuple in enumerate(param_combinations)
124
- ):
125
- results[idx] = result
126
- return results
127
-
128
- def _execute_parallel_without_progress(
129
- func: callable,
130
- iterables: list,
131
- fixed_args: list,
132
- iterable_kwargs: dict,
133
- fixed_kwargs: dict,
134
- param_combinations: list,
135
- parallel_kwargs: dict,
136
- ) -> list:
137
- """Execute parallel tasks without progress tracking.
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
- Args:
140
- func: Function to execute
141
- iterables: List of iterable arguments
142
- fixed_args: List of fixed arguments
143
- iterable_kwargs: Dictionary of iterable keyword arguments
144
- fixed_kwargs: Dictionary of fixed keyword arguments
145
- param_combinations: List of parameter combinations
146
- parallel_kwargs: Parallel execution configuration
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
- Returns:
149
- list: Results from parallel execution
150
- """
151
- return Parallel(**parallel_kwargs)(
152
- delayed(func)(
153
- *(list(param_tuple[: len(iterables)]) + fixed_args),
154
- **{
155
- k: v
156
- for k, v in zip(
157
- iterable_kwargs.keys(), param_tuple[len(iterables) :]
158
- )
159
- },
160
- **fixed_kwargs,
161
- )
162
- for param_tuple in param_combinations
163
- )
164
-
165
- def run_parallel(
166
- func: callable,
167
- *args,
168
- n_jobs: int = -1,
169
- backend: str = "threading",
170
- verbose: bool = True,
171
- **kwargs,
172
- ) -> list[any]:
173
- """Runs a function for a list of parameters in parallel.
174
-
175
- Args:
176
- func (Callable): function to run in parallel
177
- *args: Positional arguments. Can be single values or iterables
178
- n_jobs (int, optional): Number of joblib workers. Defaults to -1
179
- backend (str, optional): joblib backend. Valid options are
180
- `loky`,`threading`, `mutliprocessing` or `sequential`. Defaults to "threading"
181
- verbose (bool, optional): Show progress bar. Defaults to True
182
- **kwargs: Keyword arguments. Can be single values or iterables
183
-
184
- Returns:
185
- list[any]: Function output
186
-
187
- Examples:
188
- >>> # Single iterable argument
189
- >>> run_parallel(func, [1,2,3], fixed_arg=42)
190
-
191
- >>> # Multiple iterables in args and kwargs
192
- >>> run_parallel(func, [1,2,3], val=[7,8,9], fixed=42)
193
-
194
- >>> # Only kwargs iterables
195
- >>> run_parallel(func, x=[1,2,3], y=[4,5,6], fixed=42)
196
- """
197
- parallel_kwargs = {"n_jobs": n_jobs, "backend": backend, "verbose": 0}
198
-
199
- # Prepare and validate arguments
200
- iterables, fixed_args, iterable_kwargs, fixed_kwargs, first_iterable_len = _prepare_parallel_args(
201
- args, kwargs
202
- )
203
-
204
- # Create parameter combinations
205
- all_iterables = iterables + list(iterable_kwargs.values())
206
- param_combinations = list(zip(*all_iterables))
207
-
208
- # Execute with or without progress tracking
209
- if not verbose:
210
- return _execute_parallel_without_progress(
211
- func, iterables, fixed_args, iterable_kwargs, fixed_kwargs,
212
- param_combinations, parallel_kwargs
213
- )
214
- else:
215
- return _execute_parallel_with_progress(
216
- func, iterables, fixed_args, iterable_kwargs, fixed_kwargs,
217
- param_combinations, parallel_kwargs
218
- )
219
-
220
- else:
221
-
222
- def run_parallel(*args, **kwargs):
223
- raise ImportError("joblib not installed")
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.0
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=XLrCY5x9WmQithe0QmeYonR4LyqzqSQHR-y7cL6DKr4,41143
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=EVpjv-TUe1zGvdEAWyShJcVXurm02W0jkLbj7z1uAv4,4953
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=iRfGHxVYCBqk5SsydR_jKBvsGXmI1mOqc_1UyNJKjr4,14236
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.0.dist-info/licenses/LICENSE,sha256=9AkLexxrmr0aBgSHiqxpJk9wgazpP1CTJyiDyr56J9k,1063
49
- flowerpower-0.31.0.dist-info/METADATA,sha256=z11b6gBDVqqNGH3y_SF0n_xgJbRV0qd69TmtTo2pJqg,17202
50
- flowerpower-0.31.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
- flowerpower-0.31.0.dist-info/entry_points.txt,sha256=61X11i5a2IwC9LBiP20XCDl5zMOigGCjMCx17B7bDbQ,52
52
- flowerpower-0.31.0.dist-info/top_level.txt,sha256=VraH4WtEUfSxs5L-rXwDQhzQb9eLHTUtgvmFZ2dAYnA,12
53
- flowerpower-0.31.0.dist-info/RECORD,,
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,,