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.
- flowerpower/__init__.py +2 -6
- flowerpower/cfg/__init__.py +7 -14
- flowerpower/cfg/base.py +29 -25
- flowerpower/cfg/pipeline/__init__.py +8 -6
- flowerpower/cfg/pipeline/_schedule.py +32 -0
- flowerpower/cfg/pipeline/adapter.py +0 -5
- flowerpower/cfg/pipeline/builder.py +377 -0
- flowerpower/cfg/pipeline/run.py +36 -0
- flowerpower/cfg/project/__init__.py +11 -24
- flowerpower/cfg/project/adapter.py +0 -12
- flowerpower/cli/__init__.py +2 -21
- flowerpower/cli/cfg.py +0 -3
- flowerpower/cli/mqtt.py +0 -6
- flowerpower/cli/pipeline.py +22 -415
- flowerpower/cli/utils.py +0 -1
- flowerpower/flowerpower.py +345 -146
- flowerpower/pipeline/__init__.py +2 -0
- flowerpower/pipeline/base.py +21 -12
- flowerpower/pipeline/io.py +58 -54
- flowerpower/pipeline/manager.py +165 -726
- flowerpower/pipeline/pipeline.py +643 -0
- flowerpower/pipeline/registry.py +285 -18
- flowerpower/pipeline/visualizer.py +5 -6
- flowerpower/plugins/io/__init__.py +8 -0
- flowerpower/plugins/mqtt/__init__.py +7 -11
- flowerpower/settings/__init__.py +0 -2
- flowerpower/settings/{backend.py → _backend.py} +0 -21
- flowerpower/settings/logging.py +1 -1
- flowerpower/utils/logging.py +24 -12
- flowerpower/utils/misc.py +17 -256
- flowerpower/utils/monkey.py +1 -83
- flowerpower-0.21.0.dist-info/METADATA +463 -0
- flowerpower-0.21.0.dist-info/RECORD +44 -0
- flowerpower/cfg/pipeline/schedule.py +0 -74
- flowerpower/cfg/project/job_queue.py +0 -238
- flowerpower/cli/job_queue.py +0 -1061
- flowerpower/fs/__init__.py +0 -29
- flowerpower/fs/base.py +0 -662
- flowerpower/fs/ext.py +0 -2143
- flowerpower/fs/storage_options.py +0 -1420
- flowerpower/job_queue/__init__.py +0 -294
- flowerpower/job_queue/apscheduler/__init__.py +0 -11
- flowerpower/job_queue/apscheduler/_setup/datastore.py +0 -110
- flowerpower/job_queue/apscheduler/_setup/eventbroker.py +0 -93
- flowerpower/job_queue/apscheduler/manager.py +0 -1051
- flowerpower/job_queue/apscheduler/setup.py +0 -554
- flowerpower/job_queue/apscheduler/trigger.py +0 -169
- flowerpower/job_queue/apscheduler/utils.py +0 -311
- flowerpower/job_queue/base.py +0 -413
- flowerpower/job_queue/rq/__init__.py +0 -10
- flowerpower/job_queue/rq/_trigger.py +0 -37
- flowerpower/job_queue/rq/concurrent_workers/gevent_worker.py +0 -226
- flowerpower/job_queue/rq/concurrent_workers/thread_worker.py +0 -231
- flowerpower/job_queue/rq/manager.py +0 -1582
- flowerpower/job_queue/rq/setup.py +0 -154
- flowerpower/job_queue/rq/utils.py +0 -69
- flowerpower/mqtt.py +0 -12
- flowerpower/pipeline/job_queue.py +0 -583
- flowerpower/pipeline/runner.py +0 -603
- flowerpower/plugins/io/base.py +0 -2520
- flowerpower/plugins/io/helpers/datetime.py +0 -298
- flowerpower/plugins/io/helpers/polars.py +0 -875
- flowerpower/plugins/io/helpers/pyarrow.py +0 -570
- flowerpower/plugins/io/helpers/sql.py +0 -202
- flowerpower/plugins/io/loader/__init__.py +0 -28
- flowerpower/plugins/io/loader/csv.py +0 -37
- flowerpower/plugins/io/loader/deltatable.py +0 -190
- flowerpower/plugins/io/loader/duckdb.py +0 -19
- flowerpower/plugins/io/loader/json.py +0 -37
- flowerpower/plugins/io/loader/mqtt.py +0 -159
- flowerpower/plugins/io/loader/mssql.py +0 -26
- flowerpower/plugins/io/loader/mysql.py +0 -26
- flowerpower/plugins/io/loader/oracle.py +0 -26
- flowerpower/plugins/io/loader/parquet.py +0 -35
- flowerpower/plugins/io/loader/postgres.py +0 -26
- flowerpower/plugins/io/loader/pydala.py +0 -19
- flowerpower/plugins/io/loader/sqlite.py +0 -23
- flowerpower/plugins/io/metadata.py +0 -244
- flowerpower/plugins/io/saver/__init__.py +0 -28
- flowerpower/plugins/io/saver/csv.py +0 -36
- flowerpower/plugins/io/saver/deltatable.py +0 -186
- flowerpower/plugins/io/saver/duckdb.py +0 -19
- flowerpower/plugins/io/saver/json.py +0 -36
- flowerpower/plugins/io/saver/mqtt.py +0 -28
- flowerpower/plugins/io/saver/mssql.py +0 -26
- flowerpower/plugins/io/saver/mysql.py +0 -26
- flowerpower/plugins/io/saver/oracle.py +0 -26
- flowerpower/plugins/io/saver/parquet.py +0 -36
- flowerpower/plugins/io/saver/postgres.py +0 -26
- flowerpower/plugins/io/saver/pydala.py +0 -20
- flowerpower/plugins/io/saver/sqlite.py +0 -24
- flowerpower/plugins/mqtt/cfg.py +0 -17
- flowerpower/plugins/mqtt/manager.py +0 -962
- flowerpower/settings/job_queue.py +0 -87
- flowerpower/utils/scheduler.py +0 -311
- flowerpower-0.11.6.20.dist-info/METADATA +0 -537
- flowerpower-0.11.6.20.dist-info/RECORD +0 -102
- {flowerpower-0.11.6.20.dist-info → flowerpower-0.21.0.dist-info}/WHEEL +0 -0
- {flowerpower-0.11.6.20.dist-info → flowerpower-0.21.0.dist-info}/entry_points.txt +0 -0
- {flowerpower-0.11.6.20.dist-info → flowerpower-0.21.0.dist-info}/licenses/LICENSE +0 -0
- {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
|