wpipe 2.2.0__tar.gz → 2.3.1__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.
Files changed (137) hide show
  1. {wpipe-2.2.0 → wpipe-2.3.1}/PKG-INFO +38 -31
  2. {wpipe-2.2.0 → wpipe-2.3.1}/README.md +1 -1
  3. {wpipe-2.2.0 → wpipe-2.3.1}/pyproject.toml +1 -1
  4. wpipe-2.3.1/setup.cfg +4 -0
  5. wpipe-2.3.1/setup.py +63 -0
  6. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/__init__.py +51 -15
  7. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/dashboard/main.py +4 -4
  8. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/components/metrics.py +5 -0
  9. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/resource_monitor/monitor.py +5 -0
  10. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/sqlite/tables_dto/tracker_models.py +11 -11
  11. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/tracking/tracker.py +5 -1
  12. wpipe-2.3.1/wpipe.egg-info/PKG-INFO +635 -0
  13. wpipe-2.3.1/wpipe.egg-info/SOURCES.txt +80 -0
  14. wpipe-2.3.1/wpipe.egg-info/dependency_links.txt +1 -0
  15. wpipe-2.3.1/wpipe.egg-info/requires.txt +25 -0
  16. wpipe-2.3.1/wpipe.egg-info/top_level.txt +2 -0
  17. wpipe-2.2.0/.gitignore +0 -178
  18. wpipe-2.2.0/examples/01_basic_pipeline/01_simple_function/README.md +0 -76
  19. wpipe-2.2.0/examples/01_basic_pipeline/01_simple_function/example.py +0 -84
  20. wpipe-2.2.0/examples/01_basic_pipeline/02_class_steps/README.md +0 -75
  21. wpipe-2.2.0/examples/01_basic_pipeline/02_class_steps/example.py +0 -93
  22. wpipe-2.2.0/examples/01_basic_pipeline/03_mixed_steps/README.md +0 -75
  23. wpipe-2.2.0/examples/01_basic_pipeline/03_mixed_steps/example.py +0 -87
  24. wpipe-2.2.0/examples/01_basic_pipeline/04_default_values/README.md +0 -70
  25. wpipe-2.2.0/examples/01_basic_pipeline/04_default_values/example.py +0 -72
  26. wpipe-2.2.0/examples/01_basic_pipeline/05_args_kwargs/README.md +0 -70
  27. wpipe-2.2.0/examples/01_basic_pipeline/05_args_kwargs/example.py +0 -78
  28. wpipe-2.2.0/examples/01_basic_pipeline/06_dict_processing/README.md +0 -75
  29. wpipe-2.2.0/examples/01_basic_pipeline/06_dict_processing/example.py +0 -100
  30. wpipe-2.2.0/examples/01_basic_pipeline/07_multiple_runs/README.md +0 -71
  31. wpipe-2.2.0/examples/01_basic_pipeline/07_multiple_runs/example.py +0 -95
  32. wpipe-2.2.0/examples/01_basic_pipeline/08_data_aggregation/README.md +0 -81
  33. wpipe-2.2.0/examples/01_basic_pipeline/08_data_aggregation/example.py +0 -106
  34. wpipe-2.2.0/examples/01_basic_pipeline/09_empty_data/README.md +0 -71
  35. wpipe-2.2.0/examples/01_basic_pipeline/09_empty_data/example.py +0 -76
  36. wpipe-2.2.0/examples/01_basic_pipeline/10_lambda_steps/README.md +0 -77
  37. wpipe-2.2.0/examples/01_basic_pipeline/10_lambda_steps/example.py +0 -34
  38. wpipe-2.2.0/examples/01_basic_pipeline/11_decorator_steps/README.md +0 -74
  39. wpipe-2.2.0/examples/01_basic_pipeline/11_decorator_steps/example.py +0 -91
  40. wpipe-2.2.0/examples/01_basic_pipeline/12_context_manager/README.md +0 -72
  41. wpipe-2.2.0/examples/01_basic_pipeline/12_context_manager/example.py +0 -73
  42. wpipe-2.2.0/examples/01_basic_pipeline/13_async_pipeline/README.md +0 -76
  43. wpipe-2.2.0/examples/01_basic_pipeline/13_async_pipeline/example.py +0 -80
  44. wpipe-2.2.0/examples/01_basic_pipeline/14_pipeline_chaining/README.md +0 -70
  45. wpipe-2.2.0/examples/01_basic_pipeline/14_pipeline_chaining/example.py +0 -53
  46. wpipe-2.2.0/examples/01_basic_pipeline/15_pipeline_clone/README.md +0 -70
  47. wpipe-2.2.0/examples/01_basic_pipeline/15_pipeline_clone/example.py +0 -53
  48. wpipe-2.2.0/examples/01_basic_pipeline/16_LogGestor/example.py +0 -19
  49. wpipe-2.2.0/examples/01_basic_pipeline/README.md +0 -166
  50. wpipe-2.2.0/examples/15_export/01_json/export_json.py +0 -77
  51. wpipe-2.2.0/examples/15_export/README.md +0 -110
  52. wpipe-2.2.0/wpipe/README.md +0 -241
  53. wpipe-2.2.0/wpipe/checkpoint/README.md +0 -67
  54. wpipe-2.2.0/wpipe/composition/README.md +0 -163
  55. wpipe-2.2.0/wpipe/dashboard/static/dashboard.js +0 -1384
  56. wpipe-2.2.0/wpipe/dashboard/static/styles.css +0 -1969
  57. wpipe-2.2.0/wpipe/dashboard/templates/base.html +0 -268
  58. wpipe-2.2.0/wpipe/dashboard/templates/tabs/alerts.html +0 -20
  59. wpipe-2.2.0/wpipe/dashboard/templates/tabs/analytics.html +0 -36
  60. wpipe-2.2.0/wpipe/dashboard/templates/tabs/data.html +0 -42
  61. wpipe-2.2.0/wpipe/dashboard/templates/tabs/events.html +0 -15
  62. wpipe-2.2.0/wpipe/dashboard/templates/tabs/graph.html +0 -110
  63. wpipe-2.2.0/wpipe/dashboard/templates/tabs/pipelines.html +0 -29
  64. wpipe-2.2.0/wpipe/dashboard/templates/tabs/states.html +0 -29
  65. wpipe-2.2.0/wpipe/dashboard/templates/tabs/timeline.html +0 -22
  66. wpipe-2.2.0/wpipe/decorators/README.md +0 -245
  67. wpipe-2.2.0/wpipe/export/README.md +0 -102
  68. wpipe-2.2.0/wpipe/parallel/README.md +0 -106
  69. wpipe-2.2.0/wpipe/resource_monitor/README.md +0 -104
  70. wpipe-2.2.0/wpipe/timeout/README.md +0 -81
  71. wpipe-2.2.0/wpipe/type_hinting/README.md +0 -94
  72. {wpipe-2.2.0 → wpipe-2.3.1}/LICENSE +0 -0
  73. {wpipe-2.2.0 → wpipe-2.3.1}/test/__init__.py +0 -0
  74. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_api_pipeline.py +0 -0
  75. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_async.py +0 -0
  76. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_background.py +0 -0
  77. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_basic_pipeline.py +0 -0
  78. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_checkpoint.py +0 -0
  79. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_core.py +0 -0
  80. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_dashboard_and_monitor.py +0 -0
  81. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_export.py +0 -0
  82. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_misc.py +0 -0
  83. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_nested_pipelines.py +0 -0
  84. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_pipe.py +0 -0
  85. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_pipe_async.py +0 -0
  86. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_pipeline_config.py +0 -0
  87. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_sqlite.py +0 -0
  88. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_tracking.py +0 -0
  89. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_util.py +0 -0
  90. {wpipe-2.2.0 → wpipe-2.3.1}/test/test_yaml_config.py +0 -0
  91. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/api_client/__init__.py +0 -0
  92. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/api_client/api_client.py +0 -0
  93. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/checkpoint/__init__.py +0 -0
  94. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/checkpoint/checkpoint.py +0 -0
  95. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/composition/__init__.py +0 -0
  96. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/composition/pipeline_step.py +0 -0
  97. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/dashboard/__init__.py +0 -0
  98. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/dashboard/__main__.py +0 -0
  99. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/decorators/__init__.py +0 -0
  100. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/decorators/step.py +0 -0
  101. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/exception/__init__.py +0 -0
  102. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/exception/api_error.py +0 -0
  103. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/export/__init__.py +0 -0
  104. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/export/exporter.py +0 -0
  105. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/log/__init__.py +0 -0
  106. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/log/log.py +0 -0
  107. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/parallel/__init__.py +0 -0
  108. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/parallel/executor.py +0 -0
  109. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/__init__.py +0 -0
  110. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/components/__init__.py +0 -0
  111. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/components/constants.py +0 -0
  112. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/components/logic_blocks.py +0 -0
  113. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/components/logic_blocks_async.py +0 -0
  114. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/components/progress.py +0 -0
  115. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/components/reporting.py +0 -0
  116. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/pipe.py +0 -0
  117. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/pipe_async.py +0 -0
  118. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/pipe/pipe_async_minimal.py +0 -0
  119. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/ram/__init__.py +0 -0
  120. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/ram/ram.py +0 -0
  121. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/resource_monitor/__init__.py +0 -0
  122. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/sqlite/Sqlite.py +0 -0
  123. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/sqlite/__init__.py +0 -0
  124. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/sqlite/tables_dto/__init__.py +0 -0
  125. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/sqlite/tables_dto/log_gestor_model.py +0 -0
  126. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/sqlite/tables_dto/records.py +0 -0
  127. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/timeout/__init__.py +0 -0
  128. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/timeout/timeout.py +0 -0
  129. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/tracking/__init__.py +0 -0
  130. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/tracking/alerts.py +0 -0
  131. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/tracking/analysis.py +0 -0
  132. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/tracking/queries.py +0 -0
  133. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/type_hinting/__init__.py +0 -0
  134. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/type_hinting/validators.py +0 -0
  135. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/util/__init__.py +0 -0
  136. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/util/transform.py +0 -0
  137. {wpipe-2.2.0 → wpipe-2.3.1}/wpipe/util/utils.py +0 -0
@@ -1,12 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wpipe
3
- Version: 2.2.0
3
+ Version: 2.3.1
4
4
  Summary: Pipeline library with API integration for task orchestration and execution tracking
5
- Project-URL: Homepage, https://github.com/wisrovi/wpipe
6
- Project-URL: Documentation, https://wpipe.readthedocs.io/en/latest/
7
- Project-URL: Repository, https://github.com/wisrovi/wpipe
8
- Project-URL: Issues, https://github.com/wisrovi/wpipe/issues
9
- Project-URL: Changelog, https://github.com/wisrovi/wpipe/blob/main/CHANGELOG.md
5
+ Home-page: https://github.com/wisrovi/wpipe
6
+ Author: William Steve Rodriguez Villamizar
10
7
  Author-email: William Steve Rodriguez Villamizar <wisrovi.rodriguez@gmail.com>
11
8
  License: MIT License
12
9
 
@@ -42,46 +39,56 @@ License: MIT License
42
39
  🌐 Website: https://wisrovi.github.io/wpipe/
43
40
 
44
41
  ¡Gracias por usar WPipe!
45
- License-File: LICENSE
46
- Keywords: api,orchestration,pipeline,tasks,worker,workflow
47
- Classifier: Development Status :: 5 - Production/Stable
48
- Classifier: Intended Audience :: Developers
49
- Classifier: License :: OSI Approved :: MIT License
50
- Classifier: Operating System :: OS Independent
42
+
43
+ Project-URL: Homepage, https://github.com/wisrovi/wpipe
44
+ Project-URL: Documentation, https://wpipe.readthedocs.io/en/latest/
45
+ Project-URL: Repository, https://github.com/wisrovi/wpipe
46
+ Project-URL: Issues, https://github.com/wisrovi/wpipe/issues
47
+ Project-URL: Changelog, https://github.com/wisrovi/wpipe/blob/main/CHANGELOG.md
48
+ Keywords: pipeline,workflow,tasks,orchestration,api,worker
51
49
  Classifier: Programming Language :: Python :: 3
52
50
  Classifier: Programming Language :: Python :: 3.9
53
51
  Classifier: Programming Language :: Python :: 3.10
54
52
  Classifier: Programming Language :: Python :: 3.11
55
53
  Classifier: Programming Language :: Python :: 3.12
56
54
  Classifier: Programming Language :: Python :: 3.13
55
+ Classifier: License :: OSI Approved :: MIT License
56
+ Classifier: Operating System :: OS Independent
57
57
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
58
- Requires-Python: >=3.9
59
- Requires-Dist: fastapi>=0.100.0
58
+ Classifier: Intended Audience :: Developers
59
+ Classifier: Development Status :: 5 - Production/Stable
60
+ Requires-Python: >=3.6
61
+ Description-Content-Type: text/markdown
62
+ License-File: LICENSE
63
+ Requires-Dist: requests>=2.31.0
60
64
  Requires-Dist: loguru>=0.7.0
61
65
  Requires-Dist: pandas>=2.0.0
62
- Requires-Dist: pydantic>=2.0.0
63
66
  Requires-Dist: pyyaml>=6.0.1
64
- Requires-Dist: requests>=2.31.0
65
- Requires-Dist: rich>=13.7.0
66
67
  Requires-Dist: tqdm>=4.66.0
68
+ Requires-Dist: rich>=13.7.0
69
+ Requires-Dist: pydantic>=2.0.0
70
+ Requires-Dist: fastapi>=0.100.0
67
71
  Requires-Dist: uvicorn>=0.23.0
68
72
  Requires-Dist: wsqlite>=0.1.0
69
73
  Provides-Extra: dev
70
- Requires-Dist: black>=23.0.0; extra == 'dev'
71
- Requires-Dist: mypy>=1.7.0; extra == 'dev'
72
- Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
73
- Requires-Dist: pytest>=7.4.0; extra == 'dev'
74
- Requires-Dist: ruff>=0.1.0; extra == 'dev'
74
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
75
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
76
+ Requires-Dist: black>=23.0.0; extra == "dev"
77
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
78
+ Requires-Dist: mypy>=1.7.0; extra == "dev"
75
79
  Provides-Extra: docs
76
- Requires-Dist: myst-parser>=2.0.0; extra == 'docs'
77
- Requires-Dist: sphinx-copybutton>=0.5.0; extra == 'docs'
78
- Requires-Dist: sphinx-design>=0.5.0; extra == 'docs'
79
- Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == 'docs'
80
- Requires-Dist: sphinx-sitemap>=2.5.0; extra == 'docs'
81
- Requires-Dist: sphinx>=7.2.0; extra == 'docs'
82
- Description-Content-Type: text/markdown
83
-
84
- # 🚀 WPipe v2.2.0
80
+ Requires-Dist: sphinx>=7.2.0; extra == "docs"
81
+ Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == "docs"
82
+ Requires-Dist: sphinx-copybutton>=0.5.0; extra == "docs"
83
+ Requires-Dist: myst-parser>=2.0.0; extra == "docs"
84
+ Requires-Dist: sphinx-sitemap>=2.5.0; extra == "docs"
85
+ Requires-Dist: sphinx-design>=0.5.0; extra == "docs"
86
+ Dynamic: author
87
+ Dynamic: home-page
88
+ Dynamic: license-file
89
+ Dynamic: requires-python
90
+
91
+ # 🚀 WPipe v2.3.0
85
92
 
86
93
  **El motor de orquestación de pipelines más rápido, resiliente y puro para Python.**
87
94
 
@@ -1,4 +1,4 @@
1
- # 🚀 WPipe v2.2.0
1
+ # 🚀 WPipe v2.3.0
2
2
 
3
3
  **El motor de orquestación de pipelines más rápido, resiliente y puro para Python.**
4
4
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "wpipe"
7
- version = "2.2.0"
7
+ version = "2.3.1"
8
8
  authors = [
9
9
  { name = "William Steve Rodriguez Villamizar", email = "wisrovi.rodriguez@gmail.com" }
10
10
  ]
wpipe-2.3.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
wpipe-2.3.1/setup.py ADDED
@@ -0,0 +1,63 @@
1
+ """
2
+ Setup configuration for the wpipe package.
3
+
4
+ This module handles the packaging and distribution of the wpipe library,
5
+ a tool for creating pipelines connected to an API.
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import List
10
+ from setuptools import setup, find_packages
11
+
12
+
13
+ def get_long_description() -> str:
14
+ """
15
+ Retrieve the long description from the README.md file.
16
+
17
+ Returns:
18
+ str: The content of the README.md file.
19
+ """
20
+ this_directory: Path = Path(__file__).parent
21
+ readme_path: Path = this_directory / "README.md"
22
+ if readme_path.exists():
23
+ return readme_path.read_text(encoding="utf-8")
24
+ return "Libreria para crear pipelines conectados a una API"
25
+
26
+
27
+ def run_setup() -> None:
28
+ """
29
+ Execute the setup process for the package.
30
+ """
31
+ install_requires: List[str] = [
32
+ "requests",
33
+ "loguru",
34
+ "pandas",
35
+ "pyyaml",
36
+ "rich",
37
+ ]
38
+
39
+ setup(
40
+ name="wpipe",
41
+ version="2.3.1",
42
+ description="Library for creating pipelines connected to an API",
43
+ author="William Steve Rodriguez Villamizar",
44
+ author_email="wisrovi.rodriguez@gmail.com",
45
+ packages=find_packages(),
46
+ install_requires=install_requires,
47
+ classifiers=[
48
+ "Programming Language :: Python :: 3",
49
+ "License :: OSI Approved :: MIT License",
50
+ "Operating System :: OS Independent",
51
+ "Topic :: Software Development :: Build Tools",
52
+ "Intended Audience :: Developers",
53
+ ],
54
+ python_requires=">=3.6",
55
+ long_description_content_type="text/markdown",
56
+ long_description=get_long_description(),
57
+ license="MIT",
58
+ url="https://github.com/wisrovi/wpipe",
59
+ )
60
+
61
+
62
+ if __name__ == "__main__":
63
+ run_setup()
@@ -12,16 +12,14 @@ from typing import Any, Dict
12
12
 
13
13
  from wsqlite import WSQLite as Wsqlite_original
14
14
 
15
+ import wsqlite.core.connection as wconn
16
+
15
17
  # Connection pooling for performance optimization
16
18
  _db_connections: Dict[str, sqlite3.Connection] = {}
17
19
  _db_lock = threading.RLock()
18
20
 
19
- def patched_get_connection(self) -> sqlite3.Connection:
21
+ def patched_get_shared_connection(db_path: str) -> sqlite3.Connection:
20
22
  """Obtain a shared database connection to improve performance."""
21
- db_path = getattr(self, 'db_path', None) or self.__dict__.get('db_path')
22
- if db_path is None:
23
- raise AttributeError(f"WSQLite object has no attribute 'db_path'.")
24
-
25
23
  with _db_lock:
26
24
  if db_path in _db_connections:
27
25
  try:
@@ -38,41 +36,79 @@ def patched_get_connection(self) -> sqlite3.Connection:
38
36
  _db_connections[db_path] = conn
39
37
  return _db_connections[db_path]
40
38
 
41
- Wsqlite_original._get_connection = patched_get_connection
39
+ # Redirect wsqlite's internal connection getter to our shared pool
40
+ wconn._get_connection = patched_get_shared_connection
41
+
42
+ # Also patch ConnectionPool to use our shared connection
43
+ from wsqlite.core.pool import ConnectionPool
44
+ ConnectionPool._create_connection = lambda self: patched_get_shared_connection(self.db_path)
45
+
46
+ def _patched_return(self, conn):
47
+ """Patched return_connection that releases semaphore but avoids rollback."""
48
+ try:
49
+ if self._is_healthy(conn):
50
+ try:
51
+ self._pool.put_nowait(conn)
52
+ except:
53
+ pass
54
+ finally:
55
+ self._semaphore.release()
56
+
57
+ ConnectionPool.return_connection = _patched_return
42
58
 
43
- def patched_insert(self, data: Any) -> int:
44
- """Insert a new record and return the generated ID."""
45
- table_name = self.table_name
46
- data_dict = data.model_dump() if hasattr(data, "model_dump") else data
59
+ def patched_insert(self, data: Any) -> Any:
60
+ """Insert a new record and return the generated ID without committing immediately."""
61
+ self._call_hook(data, "pre_save")
47
62
 
63
+ data_dict = self._dump(data)
64
+ # Filter out None values to allow SQLite autoincrement
48
65
  columns = [k for k, v in data_dict.items() if v is not None]
49
66
  placeholders = ["?" for _ in columns]
50
67
  values = [data_dict[k] for k in columns]
51
68
 
52
- query = f"INSERT INTO {table_name} ({', '.join(columns)}) VALUES ({', '.join(placeholders)})"
69
+ query = f"INSERT INTO {self.table_name} ({', '.join(columns)}) VALUES ({', '.join(placeholders)})"
53
70
 
54
71
  with _db_lock:
55
- conn = self._get_connection()
72
+ conn = patched_get_shared_connection(self.db_path)
56
73
  cursor = conn.cursor()
57
74
  try:
58
75
  cursor.execute(query, values)
59
- # Commit removed for performance. Final commit will be handled by Pipeline.
60
- return cursor.lastrowid
76
+ result = cursor.lastrowid
77
+ self._call_hook(data, "post_save")
78
+ return result
61
79
  except Exception as e:
62
- conn.rollback()
80
+ # Only rollback if we are in a transaction
81
+ try:
82
+ conn.rollback()
83
+ except:
84
+ pass
63
85
  raise e
64
86
 
65
87
  Wsqlite_original.insert = patched_insert
66
88
 
67
89
  @atexit.register
68
90
  def _close_connections():
91
+ """Cleanup connections and threads on exit."""
69
92
  with _db_lock:
70
93
  for path, conn in list(_db_connections.items()):
71
94
  try:
95
+ # Force commit before closing if possible
96
+ conn.commit()
72
97
  conn.close()
73
98
  except:
74
99
  pass
75
100
  _db_connections.clear()
101
+
102
+ # Final attempt to silence lingering daemon threads in environments like Binder/Jupyter
103
+ import threading
104
+ for thread in threading.enumerate():
105
+ if thread.daemon and thread is not threading.current_thread():
106
+ if "_RefreshThread" in str(thread):
107
+ try:
108
+ # Give it a very short window to finish or just ignore it
109
+ thread.join(timeout=0.01)
110
+ except:
111
+ pass
76
112
 
77
113
  # Lazy loading map
78
114
  _LAZY_MAP = {
@@ -143,8 +143,8 @@ def create_app(
143
143
  """Get the YAML configuration for a pipeline execution."""
144
144
  tracker = PipelineTracker(db_path=db_path, config_dir=config_dir)
145
145
  pipeline = tracker.get_pipeline(pipeline_id)
146
- if pipeline and pipeline.get("config_yaml"):
147
- yaml_path = Path(pipeline["config_yaml"])
146
+ if pipeline and pipeline.get("yaml_path"):
147
+ yaml_path = Path(pipeline["yaml_path"])
148
148
  if not yaml_path.exists() and config_dir:
149
149
  yaml_path = Path(config_dir) / f"{pipeline_id}.yaml"
150
150
  if not yaml_path.exists() and config_dir:
@@ -190,13 +190,13 @@ def create_app(
190
190
  return tracker.get_top_slow_steps(limit=limit)
191
191
 
192
192
  @app.get("/api/analysis/states")
193
- async def get_states_analysis() -> List[Dict[str, Any]]:
193
+ async def get_states_analysis() -> Dict[str, Any]:
194
194
  """Get analysis of pipeline states."""
195
195
  tracker = PipelineTracker(db_path=db_path, config_dir=config_dir)
196
196
  return tracker.get_states_analysis()
197
197
 
198
198
  @app.get("/api/analysis/pipelines")
199
- async def get_pipelines_analysis() -> List[Dict[str, Any]]:
199
+ async def get_pipelines_analysis() -> Dict[str, Any]:
200
200
  """Get analysis of pipeline performance."""
201
201
  tracker = PipelineTracker(db_path=db_path, config_dir=config_dir)
202
202
  return tracker.get_pipelines_analysis()
@@ -77,6 +77,11 @@ class SystemMetricsCollector:
77
77
  def stop(self) -> None:
78
78
  """Stop the background collection thread."""
79
79
  self._stop_event.set()
80
+ if self._thread and self._thread.is_alive():
81
+ try:
82
+ self._thread.join(timeout=1.0)
83
+ except:
84
+ pass
80
85
 
81
86
  def _collect_loop(self) -> None:
82
87
  """Continuous collection loop executed in the background thread."""
@@ -133,6 +133,11 @@ class ResourceMonitor:
133
133
  return
134
134
 
135
135
  self._monitoring = False
136
+ if self._monitor_thread and self._monitor_thread.is_alive():
137
+ try:
138
+ self._monitor_thread.join(timeout=1.0)
139
+ except:
140
+ pass
136
141
 
137
142
  self.end_time = time.time()
138
143
  try:
@@ -70,7 +70,7 @@ class StepModel(BaseModel):
70
70
  error_traceback (Optional[str]): Full traceback if the step failed.
71
71
  """
72
72
 
73
- id: Optional[int] = Field(None, description="Primary Key")
73
+ id: Optional[int] = Field(None, description="primary key autoincrement")
74
74
  pipeline_id: str
75
75
  step_order: int
76
76
  step_name: str
@@ -103,7 +103,7 @@ class StepHistoryModel(BaseModel):
103
103
  recorded_at (Optional[str]): ISO timestamp of the record.
104
104
  """
105
105
 
106
- id: Optional[int] = Field(None, description="Primary Key")
106
+ id: Optional[int] = Field(None, description="primary key autoincrement")
107
107
  pipeline_id: str
108
108
  step_name: str
109
109
  duration_ms: float
@@ -135,7 +135,7 @@ class PerformanceStatsModel(BaseModel):
135
135
  created_at (Optional[str]): ISO timestamp of the record creation.
136
136
  """
137
137
 
138
- id: Optional[int] = Field(None, description="Primary Key")
138
+ id: Optional[int] = Field(None, description="primary key autoincrement")
139
139
  entity_type: str
140
140
  entity_name: str
141
141
  period_start: str
@@ -169,7 +169,7 @@ class AlertConfigModel(BaseModel):
169
169
  enabled (int): Whether the alert is enabled (1) or disabled (0).
170
170
  """
171
171
 
172
- id: Optional[int] = Field(None, description="Primary Key")
172
+ id: Optional[int] = Field(None, description="primary key autoincrement")
173
173
  name: str
174
174
  metric: str
175
175
  condition: str
@@ -196,7 +196,7 @@ class AlertFiredModel(BaseModel):
196
196
  fired_at (Optional[str]): ISO timestamp of when the alert fired.
197
197
  """
198
198
 
199
- id: Optional[int] = Field(None, description="Primary Key")
199
+ id: Optional[int] = Field(None, description="primary key autoincrement")
200
200
  alert_config_id: int
201
201
  pipeline_id: str
202
202
  step_id: Optional[int] = None
@@ -224,7 +224,7 @@ class EventModel(BaseModel):
224
224
  created_at (Optional[str]): ISO timestamp of event creation.
225
225
  """
226
226
 
227
- id: Optional[int] = Field(None, description="Primary Key")
227
+ id: Optional[int] = Field(None, description="primary key autoincrement")
228
228
  pipeline_id: str
229
229
  step_id: Optional[int] = None
230
230
  event_type: str
@@ -250,7 +250,7 @@ class PipelineRelationModel(BaseModel):
250
250
  created_at (Optional[str]): ISO timestamp of relation creation.
251
251
  """
252
252
 
253
- id: Optional[int] = Field(None, description="Primary Key")
253
+ id: Optional[int] = Field(None, description="primary key autoincrement")
254
254
  parent_pipeline_id: str
255
255
  child_pipeline_id: str
256
256
  relation_type: str = "triggered"
@@ -276,7 +276,7 @@ class SystemMetricsModel(BaseModel):
276
276
  recorded_at (Optional[str]): ISO timestamp of the measurement.
277
277
  """
278
278
 
279
- id: Optional[int] = Field(None, description="Primary Key")
279
+ id: Optional[int] = Field(None, description="primary key autoincrement")
280
280
  pipeline_id: str
281
281
  cpu_percent: Optional[float] = None
282
282
  memory_percent: Optional[float] = None
@@ -304,7 +304,7 @@ class ResourceMetricsModel(BaseModel):
304
304
  created_at (Optional[str]): ISO timestamp of record creation.
305
305
  """
306
306
 
307
- id: Optional[int] = Field(None, description="Primary Key")
307
+ id: Optional[int] = Field(None, description="primary key autoincrement")
308
308
  task_name: str
309
309
  start_ram_mb: Optional[float] = None
310
310
  peak_ram_mb: Optional[float] = None
@@ -330,7 +330,7 @@ class CheckpointModel(BaseModel):
330
330
  created_at (Optional[str]): ISO timestamp of checkpoint creation.
331
331
  """
332
332
 
333
- id: Optional[int] = Field(None, description="Primary Key")
333
+ id: Optional[int] = Field(None, description="primary key autoincrement")
334
334
  pipeline_id: str
335
335
  step_order: int
336
336
  step_name: str
@@ -356,7 +356,7 @@ class ComparisonModel(BaseModel):
356
356
  created_at (Optional[str]): ISO timestamp of comparison creation.
357
357
  """
358
358
 
359
- id: Optional[int] = Field(None, description="Primary Key")
359
+ id: Optional[int] = Field(None, description="primary key autoincrement")
360
360
  comparison_uuid: str
361
361
  pipeline_a_id: str
362
362
  pipeline_b_id: str
@@ -334,7 +334,11 @@ class PipelineTracker:
334
334
  if not pipeline_records:
335
335
  return []
336
336
  model = pipeline_records[0]
337
- started = datetime.fromisoformat(model.started_at)
337
+ started_at = model.started_at
338
+ if started_at is None:
339
+ started = datetime.now()
340
+ else:
341
+ started = datetime.fromisoformat(str(started_at))
338
342
  duration_ms = (datetime.now() - started).total_seconds() * 1000
339
343
  model.status = "error" if error_message else "completed"
340
344
  model.completed_at = datetime.now().isoformat()