FlowerPower 0.11.6.20__py3-none-any.whl → 0.21.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 (101) hide show
  1. flowerpower/__init__.py +2 -6
  2. flowerpower/cfg/__init__.py +7 -14
  3. flowerpower/cfg/base.py +29 -25
  4. flowerpower/cfg/pipeline/__init__.py +8 -6
  5. flowerpower/cfg/pipeline/_schedule.py +32 -0
  6. flowerpower/cfg/pipeline/adapter.py +0 -5
  7. flowerpower/cfg/pipeline/builder.py +377 -0
  8. flowerpower/cfg/pipeline/run.py +36 -0
  9. flowerpower/cfg/project/__init__.py +11 -24
  10. flowerpower/cfg/project/adapter.py +0 -12
  11. flowerpower/cli/__init__.py +2 -21
  12. flowerpower/cli/cfg.py +0 -3
  13. flowerpower/cli/mqtt.py +0 -6
  14. flowerpower/cli/pipeline.py +22 -415
  15. flowerpower/cli/utils.py +0 -1
  16. flowerpower/flowerpower.py +345 -146
  17. flowerpower/pipeline/__init__.py +2 -0
  18. flowerpower/pipeline/base.py +21 -12
  19. flowerpower/pipeline/io.py +58 -54
  20. flowerpower/pipeline/manager.py +165 -726
  21. flowerpower/pipeline/pipeline.py +643 -0
  22. flowerpower/pipeline/registry.py +285 -18
  23. flowerpower/pipeline/visualizer.py +5 -6
  24. flowerpower/plugins/io/__init__.py +8 -0
  25. flowerpower/plugins/mqtt/__init__.py +7 -11
  26. flowerpower/settings/__init__.py +0 -2
  27. flowerpower/settings/{backend.py → _backend.py} +0 -21
  28. flowerpower/settings/logging.py +1 -1
  29. flowerpower/utils/logging.py +24 -12
  30. flowerpower/utils/misc.py +17 -256
  31. flowerpower/utils/monkey.py +1 -83
  32. flowerpower-0.21.0.dist-info/METADATA +463 -0
  33. flowerpower-0.21.0.dist-info/RECORD +44 -0
  34. flowerpower/cfg/pipeline/schedule.py +0 -74
  35. flowerpower/cfg/project/job_queue.py +0 -238
  36. flowerpower/cli/job_queue.py +0 -1061
  37. flowerpower/fs/__init__.py +0 -29
  38. flowerpower/fs/base.py +0 -662
  39. flowerpower/fs/ext.py +0 -2143
  40. flowerpower/fs/storage_options.py +0 -1420
  41. flowerpower/job_queue/__init__.py +0 -294
  42. flowerpower/job_queue/apscheduler/__init__.py +0 -11
  43. flowerpower/job_queue/apscheduler/_setup/datastore.py +0 -110
  44. flowerpower/job_queue/apscheduler/_setup/eventbroker.py +0 -93
  45. flowerpower/job_queue/apscheduler/manager.py +0 -1051
  46. flowerpower/job_queue/apscheduler/setup.py +0 -554
  47. flowerpower/job_queue/apscheduler/trigger.py +0 -169
  48. flowerpower/job_queue/apscheduler/utils.py +0 -311
  49. flowerpower/job_queue/base.py +0 -413
  50. flowerpower/job_queue/rq/__init__.py +0 -10
  51. flowerpower/job_queue/rq/_trigger.py +0 -37
  52. flowerpower/job_queue/rq/concurrent_workers/gevent_worker.py +0 -226
  53. flowerpower/job_queue/rq/concurrent_workers/thread_worker.py +0 -231
  54. flowerpower/job_queue/rq/manager.py +0 -1582
  55. flowerpower/job_queue/rq/setup.py +0 -154
  56. flowerpower/job_queue/rq/utils.py +0 -69
  57. flowerpower/mqtt.py +0 -12
  58. flowerpower/pipeline/job_queue.py +0 -583
  59. flowerpower/pipeline/runner.py +0 -603
  60. flowerpower/plugins/io/base.py +0 -2520
  61. flowerpower/plugins/io/helpers/datetime.py +0 -298
  62. flowerpower/plugins/io/helpers/polars.py +0 -875
  63. flowerpower/plugins/io/helpers/pyarrow.py +0 -570
  64. flowerpower/plugins/io/helpers/sql.py +0 -202
  65. flowerpower/plugins/io/loader/__init__.py +0 -28
  66. flowerpower/plugins/io/loader/csv.py +0 -37
  67. flowerpower/plugins/io/loader/deltatable.py +0 -190
  68. flowerpower/plugins/io/loader/duckdb.py +0 -19
  69. flowerpower/plugins/io/loader/json.py +0 -37
  70. flowerpower/plugins/io/loader/mqtt.py +0 -159
  71. flowerpower/plugins/io/loader/mssql.py +0 -26
  72. flowerpower/plugins/io/loader/mysql.py +0 -26
  73. flowerpower/plugins/io/loader/oracle.py +0 -26
  74. flowerpower/plugins/io/loader/parquet.py +0 -35
  75. flowerpower/plugins/io/loader/postgres.py +0 -26
  76. flowerpower/plugins/io/loader/pydala.py +0 -19
  77. flowerpower/plugins/io/loader/sqlite.py +0 -23
  78. flowerpower/plugins/io/metadata.py +0 -244
  79. flowerpower/plugins/io/saver/__init__.py +0 -28
  80. flowerpower/plugins/io/saver/csv.py +0 -36
  81. flowerpower/plugins/io/saver/deltatable.py +0 -186
  82. flowerpower/plugins/io/saver/duckdb.py +0 -19
  83. flowerpower/plugins/io/saver/json.py +0 -36
  84. flowerpower/plugins/io/saver/mqtt.py +0 -28
  85. flowerpower/plugins/io/saver/mssql.py +0 -26
  86. flowerpower/plugins/io/saver/mysql.py +0 -26
  87. flowerpower/plugins/io/saver/oracle.py +0 -26
  88. flowerpower/plugins/io/saver/parquet.py +0 -36
  89. flowerpower/plugins/io/saver/postgres.py +0 -26
  90. flowerpower/plugins/io/saver/pydala.py +0 -20
  91. flowerpower/plugins/io/saver/sqlite.py +0 -24
  92. flowerpower/plugins/mqtt/cfg.py +0 -17
  93. flowerpower/plugins/mqtt/manager.py +0 -962
  94. flowerpower/settings/job_queue.py +0 -87
  95. flowerpower/utils/scheduler.py +0 -311
  96. flowerpower-0.11.6.20.dist-info/METADATA +0 -537
  97. flowerpower-0.11.6.20.dist-info/RECORD +0 -102
  98. {flowerpower-0.11.6.20.dist-info → flowerpower-0.21.0.dist-info}/WHEEL +0 -0
  99. {flowerpower-0.11.6.20.dist-info → flowerpower-0.21.0.dist-info}/entry_points.txt +0 -0
  100. {flowerpower-0.11.6.20.dist-info → flowerpower-0.21.0.dist-info}/licenses/LICENSE +0 -0
  101. {flowerpower-0.11.6.20.dist-info → flowerpower-0.21.0.dist-info}/top_level.txt +0 -0
@@ -1,231 +0,0 @@
1
- # filepath: /Volumes/WD_Blue_1TB/coding/libs/flowerpower/src/flowerpower/worker/rq/concurrent_workers.py
2
- import concurrent.futures
3
- import datetime as dt
4
- import logging
5
- import os
6
- import threading
7
- import time
8
- import traceback
9
- from concurrent.futures import ThreadPoolExecutor
10
-
11
- from loguru import logger
12
- from rq import worker
13
- from rq.exceptions import DequeueTimeout
14
- from rq.job import JobStatus
15
- from rq.worker import StopRequested
16
-
17
- from flowerpower.utils.logging import setup_logging
18
-
19
- utcnow = dt.datetime.utcnow
20
- setup_logging("INFO")
21
-
22
-
23
- class ThreadWorker(worker.Worker):
24
- """
25
- A variation of the RQ Worker that uses a ThreadPoolExecutor to perform
26
- jobs concurrently within a single worker process.
27
-
28
- Ideal for I/O bound tasks where the GIL is released during waits.
29
- Jobs share the same memory space within the worker process.
30
- """
31
-
32
- def __init__(
33
- self,
34
- queues,
35
- name=None,
36
- max_threads=None,
37
- default_result_ttl=500,
38
- connection=None,
39
- exc_handler=None,
40
- exception_handlers=None,
41
- default_worker_ttl=None,
42
- job_class=None,
43
- queue_class=None,
44
- log_job_description=True,
45
- job_monitoring_interval=30,
46
- disable_default_exception_handler=False,
47
- prepare_for_work=True,
48
- maintenance_interval=600,
49
- ):
50
- super().__init__(
51
- queues,
52
- name=name,
53
- default_result_ttl=default_result_ttl,
54
- connection=connection,
55
- exc_handler=exc_handler,
56
- exception_handlers=exception_handlers,
57
- default_worker_ttl=default_worker_ttl,
58
- job_class=job_class,
59
- queue_class=queue_class,
60
- log_job_description=log_job_description,
61
- job_monitoring_interval=job_monitoring_interval,
62
- disable_default_exception_handler=disable_default_exception_handler,
63
- prepare_for_work=prepare_for_work,
64
- maintenance_interval=maintenance_interval,
65
- )
66
-
67
- self.max_threads = (
68
- max_threads if max_threads is not None else (os.cpu_count() or 1) * 5
69
- )
70
- self._executor = None
71
- self._futures = set()
72
- self.log = logger
73
-
74
- def work(
75
- self,
76
- burst=False,
77
- logging_level="INFO",
78
- date_format=worker.DEFAULT_LOGGING_DATE_FORMAT,
79
- log_format=worker.DEFAULT_LOGGING_FORMAT,
80
- max_jobs=None,
81
- with_scheduler=False,
82
- ):
83
- """Starts the worker's main loop."""
84
- self._install_signal_handlers()
85
- did_perform_work = False
86
- self.register_birth()
87
- self.log.info("Worker %s: started, version %s", self.key, worker.VERSION)
88
- self.set_state(worker.WorkerStatus.STARTED)
89
-
90
- self._executor = ThreadPoolExecutor(max_workers=self.max_threads)
91
- self._futures = set()
92
- processed_jobs = 0
93
-
94
- try:
95
- while True:
96
- if self._stop_requested or (
97
- max_jobs is not None and processed_jobs >= max_jobs
98
- ):
99
- break
100
-
101
- self.run_maintenance_tasks()
102
-
103
- # Wait for space in the thread pool if it's full
104
- if len(self._futures) >= self.max_threads:
105
- done, self._futures = concurrent.futures.wait(
106
- self._futures,
107
- timeout=1.0,
108
- return_when=concurrent.futures.FIRST_COMPLETED,
109
- )
110
- for future in done:
111
- try:
112
- future.result()
113
- except Exception as e:
114
- self.log.error(
115
- f"Error in completed job: {e}", exc_info=True
116
- )
117
- continue
118
-
119
- try:
120
- result = self.dequeue_job_and_maintain_ttl(timeout=1)
121
- except DequeueTimeout:
122
- if burst:
123
- break
124
- time.sleep(0.1)
125
- continue
126
- except StopRequested:
127
- break
128
- except Exception:
129
- self.log.error("Error during dequeue:", exc_info=True)
130
- time.sleep(1)
131
- continue
132
-
133
- if result is None:
134
- if burst:
135
- did_perform_work = True
136
- break
137
- time.sleep(0.1)
138
- continue
139
-
140
- job, queue = result
141
- self.log.info("Processing job %s: %s", job.id, job.description)
142
-
143
- try:
144
- future = self._executor.submit(self.execute_job, job, queue)
145
- self._futures.add(future)
146
- future.add_done_callback(
147
- lambda f, jid=job.id: self._handle_job_completion(f, jid)
148
- )
149
- except Exception as e:
150
- self.log.error(f"Failed to submit job {job.id}: {e}", exc_info=True)
151
- continue
152
-
153
- did_perform_work = True
154
- processed_jobs += 1
155
-
156
- finally:
157
- if self._executor:
158
- self._executor.shutdown(wait=True)
159
- self.register_death()
160
-
161
- return did_perform_work
162
-
163
- def _handle_job_completion(self, future, job_id):
164
- """Handle completion of a job future, including logging any errors."""
165
- self._futures.discard(future)
166
- try:
167
- future.result()
168
- except Exception as e:
169
- self.log.error(f"Error in job {job_id}: {e}", exc_info=True)
170
-
171
- def set_job_status(self, job, status):
172
- """Sets the job status."""
173
- if job:
174
- job.set_status(status)
175
-
176
- def handle_job_success(self, job, queue, started_job_registry):
177
- """Handles job completion."""
178
- try:
179
- if started_job_registry:
180
- try:
181
- started_job_registry.remove(job)
182
- except NotImplementedError:
183
- pass
184
- job.ended_at = utcnow()
185
- job.set_status(JobStatus.FINISHED)
186
- except Exception as e:
187
- self.log.error(f"Error handling job success for {job.id}: {e}")
188
-
189
- # def handle_job_failure(self, job, queue, started_job_registry, exec_string=None):
190
- # """Handles job failure."""
191
- # try:
192
- # if started_job_registry:
193
- # try:
194
- # started_job_registry.remove(job)
195
- # except NotImplementedError:
196
- # pass
197
- # job.ended_at = utcnow()
198
- # job.set_status(JobStatus.FAILED)
199
- # except Exception as e:
200
- # self.log.error(f"Error handling job failure for {job.id}: {e}")
201
-
202
- def execute_job(self, job, queue):
203
- """Execute a job in a worker thread."""
204
- job_id = job.id if job else "unknown"
205
-
206
- try:
207
- self.set_job_status(job, JobStatus.STARTED)
208
- started_job_registry = queue.started_job_registry
209
-
210
- try:
211
- started_job_registry.add(
212
- job,
213
- self.job_monitoring_interval * 1000
214
- if self.job_monitoring_interval
215
- else -1,
216
- )
217
- except NotImplementedError:
218
- pass
219
-
220
- rv = job.perform()
221
- self.handle_job_success(
222
- job=job, queue=queue, started_job_registry=started_job_registry
223
- )
224
- return rv
225
-
226
- except Exception as e:
227
- self.log.error(f"Job {job_id} failed: {e}", exc_info=True)
228
- self.handle_job_failure(
229
- job=job, queue=queue, started_job_registry=started_job_registry
230
- )
231
- raise