FlowerPower 0.21.0__py3-none-any.whl → 0.31.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.
Files changed (40) 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 +134 -22
  10. flowerpower/cfg/project/__init__.py +59 -14
  11. flowerpower/cfg/project/adapter.py +6 -0
  12. flowerpower/cli/__init__.py +8 -9
  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 -276
  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 +249 -76
  30. flowerpower/utils/security.py +221 -0
  31. {flowerpower-0.21.0.dist-info → flowerpower-0.31.0.dist-info}/METADATA +1 -13
  32. flowerpower-0.31.0.dist-info/RECORD +53 -0
  33. flowerpower/cfg/pipeline/_schedule.py +0 -32
  34. flowerpower/cli/mqtt.py +0 -168
  35. flowerpower/plugins/mqtt/__init__.py +0 -8
  36. flowerpower-0.21.0.dist-info/RECORD +0 -44
  37. {flowerpower-0.21.0.dist-info → flowerpower-0.31.0.dist-info}/WHEEL +0 -0
  38. {flowerpower-0.21.0.dist-info → flowerpower-0.31.0.dist-info}/entry_points.txt +0 -0
  39. {flowerpower-0.21.0.dist-info → flowerpower-0.31.0.dist-info}/licenses/LICENSE +0 -0
  40. {flowerpower-0.21.0.dist-info → flowerpower-0.31.0.dist-info}/top_level.txt +0 -0
@@ -20,12 +20,15 @@ from fsspec_utils import AbstractFileSystem, BaseStorageOptions, filesystem
20
20
 
21
21
  from ..settings import CONFIG_DIR, PIPELINES_DIR, CACHE_DIR
22
22
  from ..cfg import PipelineConfig, ProjectConfig
23
- from ..cfg.pipeline.adapter import AdapterConfig as PipelineAdapterConfig
24
- from ..cfg.pipeline.run import ExecutorConfig, RunConfig, WithAdapterConfig
25
- from ..cfg.project.adapter import AdapterConfig as ProjectAdapterConfig
23
+ from ..cfg.pipeline.run import RunConfig
26
24
  from ..utils.logging import setup_logging
25
+ from ..utils.config import merge_run_config_with_kwargs
26
+ from ..utils.filesystem import FilesystemHelper
27
+ from .config_manager import PipelineConfigManager
28
+ from .executor import PipelineExecutor
27
29
  from .io import PipelineIOManager
28
- from .registry import HookType, PipelineRegistry
30
+ from .lifecycle_manager import PipelineLifecycleManager
31
+ from .registry import PipelineRegistry, HookType
29
32
  from .visualizer import PipelineVisualizer
30
33
 
31
34
  setup_logging()
@@ -40,13 +43,11 @@ class PipelineManager:
40
43
  - Configuration management and loading
41
44
  - Pipeline creation, deletion, and discovery
42
45
  - Pipeline execution via PipelineRunner
43
- - Job scheduling via PipelineScheduler
44
46
  - Visualization via PipelineVisualizer
45
47
  - Import/export operations via PipelineIOManager
46
48
 
47
49
  Attributes:
48
50
  registry (PipelineRegistry): Handles pipeline registration and discovery
49
- scheduler (PipelineScheduler): Manages job scheduling and execution
50
51
  visualizer (PipelineVisualizer): Handles pipeline visualization
51
52
  io (PipelineIOManager): Manages pipeline import/export operations
52
53
  project_cfg (ProjectConfig): Current project configuration
@@ -121,8 +122,36 @@ class PipelineManager:
121
122
  if log_level:
122
123
  setup_logging(level=log_level)
123
124
 
125
+ self._setup_filesystem(base_dir, storage_options, fs, cfg_dir, pipelines_dir)
126
+ self._initialize_managers()
127
+ self._ensure_directories_exist()
128
+ self._add_modules_path()
129
+
130
+ def _setup_filesystem(
131
+ self,
132
+ base_dir: str | None,
133
+ storage_options: dict | Munch | BaseStorageOptions | None,
134
+ fs: AbstractFileSystem | None,
135
+ cfg_dir: str | None,
136
+ pipelines_dir: str | None
137
+ ) -> None:
138
+ """Setup filesystem and configuration directories.
139
+
140
+ Args:
141
+ base_dir: Root directory for the project
142
+ storage_options: Storage options for filesystem
143
+ fs: Pre-configured filesystem instance
144
+ cfg_dir: Configuration directory name
145
+ pipelines_dir: Pipelines directory name
146
+ """
124
147
  self._base_dir = base_dir or str(Path.cwd())
125
- # self._storage_options = storage_options
148
+ self._cfg_dir = cfg_dir
149
+ self._pipelines_dir = pipelines_dir
150
+
151
+ # Setup filesystem helper
152
+ self._fs_helper = FilesystemHelper(self._base_dir, storage_options)
153
+
154
+ # Configure caching if storage options provided
126
155
  if storage_options is not None:
127
156
  cached = True
128
157
  cache_storage = posixpath.join(
@@ -133,58 +162,81 @@ class PipelineManager:
133
162
  else:
134
163
  cached = False
135
164
  cache_storage = None
136
- if not fs:
137
- fs = filesystem(
138
- self._base_dir,
139
- storage_options=storage_options,
140
- cached=cached,
141
- cache_storage=cache_storage,
142
- )
143
- self._fs = fs
165
+
166
+ # Get filesystem instance
167
+ self._fs = fs or self._fs_helper.get_filesystem(cached=cached, cache_storage=cache_storage)
144
168
  self._storage_options = (
145
- storage_options or fs.storage_options
146
- if fs.protocol != "dir"
147
- else fs.fs.storage_options
169
+ storage_options or self._fs.storage_options
170
+ if self._fs.protocol != "dir"
171
+ else self._fs.fs.storage_options
148
172
  )
149
173
 
150
- # Store overrides for ProjectConfig loading
151
- self._cfg_dir = cfg_dir
152
- self._pipelines_dir = pipelines_dir
153
-
154
- self._load_project_cfg(
155
- reload=True
156
- ) # Load project config
157
-
174
+ def _initialize_managers(self) -> None:
175
+ """Initialize all manager components."""
176
+ # Initialize config manager
177
+ self._config_manager = PipelineConfigManager(
178
+ base_dir=self._base_dir,
179
+ fs=self._fs,
180
+ storage_options=self._storage_options,
181
+ cfg_dir=self._cfg_dir
182
+ )
158
183
 
159
- # Ensure essential directories exist (using paths from loaded project_cfg)
160
- try:
161
- self._fs.makedirs(self._cfg_dir, exist_ok=True)
162
- self._fs.makedirs(self._pipelines_dir, exist_ok=True)
163
- except (OSError, PermissionError) as e:
164
- logger.error(f"Error creating essential directories: {e}")
165
- raise RuntimeError(f"Failed to create essential directories: {e}") from e
166
- except Exception as e:
167
- logger.error(f"Unexpected error creating essential directories: {e}")
168
- raise RuntimeError(f"Unexpected filesystem error: {e}") from e
169
-
170
- # Ensure pipeline modules can be imported
171
- self._add_modules_path()
184
+ # Load project configuration
185
+ self._config_manager.load_project_config(reload=True)
172
186
 
173
- # Instantiate components using the loaded project config
187
+ # Initialize registry
174
188
  self.registry = PipelineRegistry(
175
- project_cfg=self.project_cfg,
189
+ project_cfg=self._config_manager.project_config,
176
190
  fs=self._fs,
177
191
  base_dir=self._base_dir,
178
192
  storage_options=self._storage_options,
179
193
  )
180
194
 
181
- # Initialize project context (will be injected by FlowerPowerProject)
195
+ # Initialize specialized managers
196
+ self._executor = PipelineExecutor(
197
+ config_manager=self._config_manager,
198
+ registry=self.registry
199
+ )
200
+ self._lifecycle_manager = PipelineLifecycleManager(registry=self.registry)
201
+
202
+ # Initialize other components
182
203
  self._project_context = None
183
- self.visualizer = PipelineVisualizer(project_cfg=self.project_cfg, fs=self._fs)
204
+ self.visualizer = PipelineVisualizer(
205
+ project_cfg=self._config_manager.project_config,
206
+ fs=self._fs
207
+ )
184
208
  self.io = PipelineIOManager(registry=self.registry)
185
209
 
186
- self._current_pipeline_name: str | None = None
187
- self._pipeline_cfg: PipelineConfig | None = None
210
+ def _ensure_directories_exist(self) -> None:
211
+ """Ensure essential directories exist."""
212
+ self._fs_helper.ensure_directories_exist(
213
+ self._fs,
214
+ self._cfg_dir,
215
+ self._pipelines_dir
216
+ )
217
+
218
+ def _add_modules_path(self) -> None:
219
+ """Add pipeline module paths to Python path.
220
+
221
+ This internal method ensures that pipeline modules can be imported by:
222
+ 1. Syncing filesystem cache if needed
223
+ 2. Adding project root to Python path
224
+ 3. Adding pipelines directory to Python path
225
+ """
226
+ if self._fs.is_cache_fs:
227
+ self._fs.sync_cache()
228
+ project_path = self._fs._mapper.directory
229
+ modules_path = posixpath.join(project_path, self._pipelines_dir)
230
+ else:
231
+ # Use the base directory directly if not using cache
232
+ project_path = self._fs.path
233
+ modules_path = posixpath.join(project_path, self._pipelines_dir)
234
+
235
+ if project_path not in sys.path:
236
+ sys.path.insert(0, project_path)
237
+
238
+ if modules_path not in sys.path:
239
+ sys.path.insert(0, modules_path)
188
240
 
189
241
  def __enter__(self) -> "PipelineManager":
190
242
  """Enter the context manager.
@@ -230,85 +282,10 @@ class PipelineManager:
230
282
  # Add cleanup code if needed
231
283
  pass
232
284
 
233
- def _add_modules_path(self) -> None:
234
- """Add pipeline module paths to Python path.
235
-
236
- This internal method ensures that pipeline modules can be imported by:
237
- 1. Syncing filesystem cache if needed
238
- 2. Adding project root to Python path
239
- 3. Adding pipelines directory to Python path
240
-
241
- Raises:
242
- RuntimeError: If filesystem sync fails or paths are invalid
243
-
244
- Example:
245
- >>> # Internal usage
246
- >>> manager = PipelineManager()
247
- >>> manager._add_modules_path()
248
- >>> import my_pipeline # Now importable
249
- """
250
- if self._fs.is_cache_fs:
251
- self._fs.sync_cache()
252
- project_path = self._fs._mapper.directory
253
- modules_path = posixpath.join(project_path, self._pipelines_dir)
254
-
255
- else:
256
- # Use the base directory directly if not using cache
257
- project_path = self._fs.path
258
- modules_path = posixpath.join(project_path, self._pipelines_dir)
259
-
260
- if project_path not in sys.path:
261
- sys.path.insert(0, project_path)
262
-
263
- if modules_path not in sys.path:
264
- sys.path.insert(0, modules_path)
265
-
266
- def _load_project_cfg(
267
- self, reload: bool = False
268
- ) -> ProjectConfig:
269
- """Load or reload the project configuration.
270
-
271
- This internal method handles loading project-wide settings from the config
272
- directory, applying overrides, and maintaining configuration state.
273
-
274
- Args:
275
- reload: Force reload configuration even if already loaded.
276
- Defaults to False for caching behavior.
277
-
278
- Returns:
279
- ProjectConfig: The loaded project configuration object with any
280
- specified overrides applied.
281
-
282
- Raises:
283
- FileNotFoundError: If project configuration file doesn't exist
284
- ValueError: If configuration format is invalid
285
- RuntimeError: If filesystem operations fail during loading
286
-
287
- Example:
288
- >>> # Internal usage
289
- >>> manager = PipelineManager()
290
- >>> project_cfg = manager._load_project_cfg(reload=True)
291
- >>> print(project_cfg.name)
292
- 'my_project'
293
- """
294
- if hasattr(self, "_project_cfg") and not reload:
295
- return self._project_cfg
296
-
297
- # Pass overrides to ProjectConfig.load
298
- self._project_cfg = ProjectConfig.load(
299
- base_dir=self._base_dir,
300
- fs=self._fs, # Pass pre-configured fs if provided
301
- storage_options=self._storage_options,
302
- )
303
- # Update internal fs reference in case ProjectConfig loaded/created one
304
- return self._project_cfg
305
285
 
306
286
  def load_pipeline(self, name: str, reload: bool = False) -> PipelineConfig:
307
287
  """Load or reload configuration for a specific pipeline.
308
288
 
309
- This internal method handles loading pipeline-specific settings from the config
310
- directory and maintaining the configuration cache state.
311
-
312
289
  Args:
313
290
  name: Name of the pipeline whose configuration to load
314
291
  reload: Force reload configuration even if already loaded.
@@ -316,45 +293,17 @@ class PipelineManager:
316
293
 
317
294
  Returns:
318
295
  PipelineConfig: The loaded pipeline configuration object
319
-
320
- Raises:
321
- FileNotFoundError: If pipeline configuration file doesn't exist
322
- ValueError: If configuration format is invalid
323
- RuntimeError: If filesystem operations fail during loading
324
-
325
- Example:
326
- >>> # Internal usage
327
- >>> manager = PipelineManager()
328
- >>> cfg = manager._load_pipeline_cfg("data_pipeline", reload=True)
329
- >>> print(cfg.run.executor.type)
330
- 'async'
331
296
  """
332
- if name == self._current_pipeline_name and not reload:
333
- return self._pipeline_cfg
334
-
335
- self._current_pipeline_name = name
336
- self._pipeline_cfg = PipelineConfig.load(
337
- base_dir=self._base_dir,
338
- name=name,
339
- fs=self._fs,
340
- storage_options=self._storage_options,
341
- )
342
- return self._pipeline_cfg
297
+ return self._config_manager.load_pipeline_config(name, reload)
343
298
 
344
299
  @property
345
300
  def current_pipeline_name(self) -> str:
346
301
  """Get the name of the currently loaded pipeline.
347
302
 
348
303
  Returns:
349
- str: Name of the currently loaded pipeline, or empty string if none loaded.
350
-
351
- Example:
352
- >>> manager = PipelineManager()
353
- >>> manager._load_pipeline_cfg("example_pipeline")
354
- >>> print(manager.current_pipeline_name)
355
- 'example_pipeline'
304
+ str: Name of the currently loaded pipeline, or None if none loaded.
356
305
  """
357
- return self._current_pipeline_name
306
+ return self._config_manager.current_pipeline_name
358
307
 
359
308
  @property
360
309
  def project_cfg(self) -> ProjectConfig:
@@ -374,9 +323,7 @@ class PipelineManager:
374
323
  >>> print(cfg.name)
375
324
  'my_project'
376
325
  """
377
- if not hasattr(self, "_project_cfg"):
378
- self._load_project_cfg()
379
- return self._project_cfg
326
+ return self._config_manager.project_config
380
327
 
381
328
  @property
382
329
  def pipeline_cfg(self) -> PipelineConfig:
@@ -395,83 +342,10 @@ class PipelineManager:
395
342
  >>> print(cfg.run.executor)
396
343
  'local'
397
344
  """
398
- if not hasattr(self, "_pipeline_cfg"):
399
- logger.warning("Pipeline config not loaded.")
400
- return
401
- return self._pipeline_cfg
345
+ return self._config_manager.pipeline_config
402
346
 
403
347
  # --- Core Execution Method ---
404
348
 
405
- def _merge_run_config_with_kwargs(self, run_config: RunConfig, kwargs: dict) -> RunConfig:
406
- """Merge kwargs into a RunConfig object.
407
-
408
- This helper method updates the RunConfig object with values from kwargs,
409
- handling different types of attributes appropriately.
410
-
411
- Args:
412
- run_config: The RunConfig object to update
413
- kwargs: Dictionary of additional parameters to merge
414
-
415
- Returns:
416
- RunConfig: Updated RunConfig object
417
- """
418
- # Handle dictionary-like attributes with update or deep merge
419
- if 'inputs' in kwargs and kwargs['inputs'] is not None:
420
- if run_config.inputs is None:
421
- run_config.inputs = kwargs['inputs']
422
- else:
423
- run_config.inputs.update(kwargs['inputs'])
424
-
425
- if 'config' in kwargs and kwargs['config'] is not None:
426
- if run_config.config is None:
427
- run_config.config = kwargs['config']
428
- else:
429
- run_config.config.update(kwargs['config'])
430
-
431
- if 'cache' in kwargs and kwargs['cache'] is not None:
432
- run_config.cache = kwargs['cache']
433
-
434
- if 'adapter' in kwargs and kwargs['adapter'] is not None:
435
- if run_config.adapter is None:
436
- run_config.adapter = kwargs['adapter']
437
- else:
438
- run_config.adapter.update(kwargs['adapter'])
439
-
440
- # Handle executor_cfg - convert string/dict to ExecutorConfig if needed
441
- if 'executor_cfg' in kwargs and kwargs['executor_cfg'] is not None:
442
- executor_cfg = kwargs['executor_cfg']
443
- if isinstance(executor_cfg, str):
444
- run_config.executor = ExecutorConfig(type=executor_cfg)
445
- elif isinstance(executor_cfg, dict):
446
- run_config.executor = ExecutorConfig.from_dict(executor_cfg)
447
- elif isinstance(executor_cfg, ExecutorConfig):
448
- run_config.executor = executor_cfg
449
-
450
- # Handle adapter configurations
451
- if 'with_adapter_cfg' in kwargs and kwargs['with_adapter_cfg'] is not None:
452
- with_adapter_cfg = kwargs['with_adapter_cfg']
453
- if isinstance(with_adapter_cfg, dict):
454
- run_config.with_adapter = WithAdapterConfig.from_dict(with_adapter_cfg)
455
- elif isinstance(with_adapter_cfg, WithAdapterConfig):
456
- run_config.with_adapter = with_adapter_cfg
457
-
458
- if 'pipeline_adapter_cfg' in kwargs and kwargs['pipeline_adapter_cfg'] is not None:
459
- run_config.pipeline_adapter_cfg = kwargs['pipeline_adapter_cfg']
460
-
461
- if 'project_adapter_cfg' in kwargs and kwargs['project_adapter_cfg'] is not None:
462
- run_config.project_adapter_cfg = kwargs['project_adapter_cfg']
463
-
464
- # Handle simple attributes
465
- simple_attrs = [
466
- 'final_vars', 'reload', 'log_level', 'max_retries', 'retry_delay',
467
- 'jitter_factor', 'retry_exceptions', 'on_success', 'on_failure'
468
- ]
469
-
470
- for attr in simple_attrs:
471
- if attr in kwargs and kwargs[attr] is not None:
472
- setattr(run_config, attr, kwargs[attr])
473
-
474
- return run_config
475
349
 
476
350
  def run(
477
351
  self,
@@ -549,33 +423,12 @@ class PipelineManager:
549
423
  ... reload=True
550
424
  ... )
551
425
  """
552
- # Initialize run_config - use provided config or load pipeline default
553
- if run_config is None:
554
- run_config = self.load_pipeline(name=name).run
426
+ # Set project context for executor
427
+ if hasattr(self, "_project_context") and self._project_context is not None:
428
+ self._executor._project_context = self._project_context
555
429
 
556
- # Merge kwargs into run_config
557
- if kwargs:
558
- run_config = self._merge_run_config_with_kwargs(run_config, kwargs)
559
-
560
- # Set up logging for this specific run if log_level is provided
561
- if run_config.log_level is not None:
562
- setup_logging(level=run_config.log_level)
563
- else:
564
- # Ensure logging is reset to default if no specific level is provided for this run
565
- setup_logging()
566
-
567
- # Use injected project context, fallback to self for backward compatibility
568
- project_context = getattr(self, "_project_context", self)
569
-
570
- # Get Pipeline instance from registry
571
- pipeline = self.registry.get_pipeline(
572
- name=name, project_context=project_context, reload=run_config.reload
573
- )
574
-
575
- # Execute pipeline using its own run method
576
- return pipeline.run(
577
- run_config=run_config,
578
- )
430
+ # Delegate to executor
431
+ return self._executor.run(name=name, run_config=run_config, **kwargs)
579
432
 
580
433
  # --- Delegated Methods ---
581
434
 
@@ -605,7 +458,7 @@ class PipelineManager:
605
458
  >>> # Overwrite existing pipeline
606
459
  >>> manager.new("data_transformation", overwrite=True)
607
460
  """
608
- self.registry.new(name=name, overwrite=overwrite)
461
+ self._lifecycle_manager.create_pipeline(name=name, overwrite=overwrite)
609
462
 
610
463
  def delete(self, name: str, cfg: bool = True, module: bool = False) -> None:
611
464
  """
@@ -632,7 +485,7 @@ class PipelineManager:
632
485
  >>> # Delete both config and module
633
486
  >>> manager.delete("test_pipeline", module=True)
634
487
  """
635
- self.registry.delete(name=name, cfg=cfg, module=module)
488
+ self._lifecycle_manager.delete_pipeline(name=name, cfg=cfg, module=module)
636
489
 
637
490
  def get_summary(
638
491
  self,
@@ -702,7 +555,7 @@ class PipelineManager:
702
555
  >>> pm = PipelineManager()
703
556
  >>> pm.show_summary()
704
557
  """
705
- return self.registry.show_summary(
558
+ return self._lifecycle_manager.show_summary(
706
559
  name=name,
707
560
  cfg=cfg,
708
561
  code=code,
@@ -711,21 +564,6 @@ class PipelineManager:
711
564
  to_svg=to_svg,
712
565
  )
713
566
 
714
- def show_pipelines(self) -> None:
715
- """Display all available pipelines in a formatted table.
716
-
717
- The table includes pipeline names, types, and enablement status.
718
- Uses rich formatting for terminal display.
719
-
720
- Example:
721
- >>> from flowerpower.pipeline import PipelineManager
722
- >>>
723
- >>> manager = PipelineManager()
724
- >>> manager.show_pipelines()
725
-
726
- """
727
- self.registry.show_pipelines()
728
-
729
567
  def list_pipelines(self) -> list[str]:
730
568
  """Get list of all available pipeline names.
731
569
 
@@ -740,7 +578,14 @@ class PipelineManager:
740
578
  >>> print(pipelines)
741
579
  ['data_ingestion', 'model_training', 'reporting']
742
580
  """
743
- return self.registry.list_pipelines()
581
+ return self._lifecycle_manager.list_pipelines()
582
+
583
+ def show_pipelines(self) -> None:
584
+ """Display all available pipelines in a formatted table.
585
+
586
+ Uses rich formatting for terminal display.
587
+ """
588
+ return self.registry.show_pipelines()
744
589
 
745
590
  @property
746
591
  def pipelines(self) -> list[str]:
@@ -758,7 +603,7 @@ class PipelineManager:
758
603
  >>> print(manager.pipelines)
759
604
  ['data_ingestion', 'model_training', 'reporting']
760
605
  """
761
- return self.registry.pipelines
606
+ return self._lifecycle_manager.pipelines
762
607
 
763
608
  @property
764
609
  def summary(self) -> dict[str, dict | str]:
@@ -778,7 +623,7 @@ class PipelineManager:
778
623
  data_pipeline: batch
779
624
  ml_pipeline: streaming
780
625
  """
781
- return self.registry.summary
626
+ return self._lifecycle_manager.summary
782
627
 
783
628
  def add_hook(
784
629
  self,
@@ -812,7 +657,7 @@ class PipelineManager:
812
657
  ... function_name="my_pre_execute_function"
813
658
  ... )
814
659
  """
815
- self.registry.add_hook(
660
+ self._lifecycle_manager.add_hook(
816
661
  name=name,
817
662
  type=type,
818
663
  to=to,