arpakitlib 1.7.91__py3-none-any.whl → 1.7.94__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.
- arpakitlib/ar_base_worker_util.py +35 -15
- arpakitlib/ar_operation_execution_util.py +39 -21
- arpakitlib/ar_sqlalchemy_model_util.py +5 -3
- {arpakitlib-1.7.91.dist-info → arpakitlib-1.7.94.dist-info}/METADATA +1 -1
- {arpakitlib-1.7.91.dist-info → arpakitlib-1.7.94.dist-info}/RECORD +9 -9
- {arpakitlib-1.7.91.dist-info → arpakitlib-1.7.94.dist-info}/LICENSE +0 -0
- {arpakitlib-1.7.91.dist-info → arpakitlib-1.7.94.dist-info}/NOTICE +0 -0
- {arpakitlib-1.7.91.dist-info → arpakitlib-1.7.94.dist-info}/WHEEL +0 -0
- {arpakitlib-1.7.91.dist-info → arpakitlib-1.7.94.dist-info}/entry_points.txt +0 -0
@@ -6,11 +6,14 @@ import multiprocessing
|
|
6
6
|
import threading
|
7
7
|
from abc import ABC
|
8
8
|
from datetime import timedelta
|
9
|
+
from random import randint
|
9
10
|
from typing import Any
|
11
|
+
from uuid import uuid4
|
10
12
|
|
13
|
+
from arpakitlib.ar_datetime_util import now_utc_dt
|
11
14
|
from arpakitlib.ar_enumeration_util import Enumeration
|
12
15
|
from arpakitlib.ar_func_util import is_async_function, is_sync_function
|
13
|
-
from arpakitlib.ar_sleep_util import sync_safe_sleep
|
16
|
+
from arpakitlib.ar_sleep_util import sync_safe_sleep
|
14
17
|
|
15
18
|
_ARPAKIT_LIB_MODULE_VERSION = "3.0"
|
16
19
|
|
@@ -19,17 +22,25 @@ class BaseWorker(ABC):
|
|
19
22
|
def __init__(
|
20
23
|
self,
|
21
24
|
*,
|
22
|
-
timeout_after_run=timedelta(seconds=0.
|
25
|
+
timeout_after_run=timedelta(seconds=0.3),
|
23
26
|
timeout_after_err_in_run=timedelta(seconds=1),
|
24
|
-
startup_funcs: list[Any] | None = None
|
27
|
+
startup_funcs: list[Any] | None = None,
|
28
|
+
worker_name: str | None = None
|
25
29
|
):
|
26
30
|
if startup_funcs is None:
|
27
31
|
startup_funcs = []
|
28
32
|
self.startup_funcs = startup_funcs
|
29
|
-
|
30
|
-
|
31
|
-
self.
|
32
|
-
self.
|
33
|
+
if worker_name is None:
|
34
|
+
worker_name = self.__class__.__name__
|
35
|
+
self.worker_name = worker_name
|
36
|
+
self.worker_creation_dt = now_utc_dt()
|
37
|
+
self.worker_id = f"{str(uuid4()).replace(' - ', '')}_{randint(1000, 9999)}"
|
38
|
+
self.worker_fullname = (
|
39
|
+
f"{self.worker_name}_{self.worker_creation_dt.isoformat()}_{self.worker_id}"
|
40
|
+
)
|
41
|
+
self._logger = logging.getLogger(self.worker_fullname)
|
42
|
+
self.timeout_after_run = timeout_after_run
|
43
|
+
self.timeout_after_err_in_run = timeout_after_err_in_run
|
33
44
|
|
34
45
|
def sync_run_startup_funcs(self):
|
35
46
|
for startup_func in self.startup_funcs:
|
@@ -41,10 +52,10 @@ class BaseWorker(ABC):
|
|
41
52
|
raise TypeError("no sync and not async")
|
42
53
|
|
43
54
|
def sync_on_startup(self):
|
44
|
-
|
55
|
+
self.sync_run_startup_funcs()
|
45
56
|
|
46
57
|
def sync_run(self):
|
47
|
-
self._logger.info("hello world")
|
58
|
+
self._logger.info(f"hello world, im {self.worker_fullname}")
|
48
59
|
|
49
60
|
def sync_run_on_error(self, exception: BaseException, **kwargs):
|
50
61
|
pass
|
@@ -66,8 +77,8 @@ class BaseWorker(ABC):
|
|
66
77
|
except BaseException as exception_:
|
67
78
|
self._logger.error("error in sync_run_on_error", exc_info=exception_)
|
68
79
|
raise exception_
|
69
|
-
|
70
|
-
|
80
|
+
sync_safe_sleep(self.timeout_after_err_in_run)
|
81
|
+
sync_safe_sleep(self.timeout_after_run)
|
71
82
|
|
72
83
|
async def async_run_startup_funcs(self):
|
73
84
|
for startup_func in self.startup_funcs:
|
@@ -79,10 +90,10 @@ class BaseWorker(ABC):
|
|
79
90
|
raise TypeError("no sync and not async")
|
80
91
|
|
81
92
|
async def async_on_startup(self):
|
82
|
-
|
93
|
+
await self.async_run_startup_funcs()
|
83
94
|
|
84
95
|
async def async_run(self):
|
85
|
-
self._logger.info("hello world")
|
96
|
+
self._logger.info(f"hello world, im {self.worker_fullname}")
|
86
97
|
|
87
98
|
async def async_run_on_error(self, exception: BaseException, **kwargs):
|
88
99
|
pass
|
@@ -104,8 +115,8 @@ class BaseWorker(ABC):
|
|
104
115
|
except BaseException as exception_:
|
105
116
|
self._logger.error("error in async_run_on_error", exc_info=exception_)
|
106
117
|
raise exception_
|
107
|
-
|
108
|
-
|
118
|
+
sync_safe_sleep(self.timeout_after_err_in_run)
|
119
|
+
sync_safe_sleep(self.timeout_after_run)
|
109
120
|
|
110
121
|
|
111
122
|
class SafeRunInBackgroundModes(Enumeration):
|
@@ -136,6 +147,15 @@ def safe_run_worker_in_background(*, worker: BaseWorker, mode: str) -> (
|
|
136
147
|
return res
|
137
148
|
|
138
149
|
|
150
|
+
def safe_run_workers_in_background(
|
151
|
+
*, workers: list[BaseWorker], mode: str
|
152
|
+
) -> list[asyncio.Task] | list[threading.Thread] | list[multiprocessing.Process]:
|
153
|
+
res = []
|
154
|
+
for worker in workers:
|
155
|
+
res.append(safe_run_worker_in_background(worker=worker, mode=mode))
|
156
|
+
return res
|
157
|
+
|
158
|
+
|
139
159
|
def __example():
|
140
160
|
pass
|
141
161
|
|
@@ -11,6 +11,7 @@ from typing import Any, Callable
|
|
11
11
|
from pydantic import ConfigDict
|
12
12
|
from pydantic.v1 import BaseModel
|
13
13
|
from sqlalchemy import asc
|
14
|
+
from sqlalchemy.exc import NoResultFound
|
14
15
|
from sqlalchemy.orm import Session
|
15
16
|
|
16
17
|
from arpakitlib.ar_base_worker_util import BaseWorker
|
@@ -61,7 +62,8 @@ def get_operation_by_id(
|
|
61
62
|
session: Session | None = None,
|
62
63
|
sqlalchemy_db: SQLAlchemyDB | None = None,
|
63
64
|
filter_operation_id: int,
|
64
|
-
raise_if_not_found: bool = False
|
65
|
+
raise_if_not_found: bool = False,
|
66
|
+
lock: bool = False
|
65
67
|
) -> OperationDBM | None:
|
66
68
|
def func(session_: Session):
|
67
69
|
query = (
|
@@ -69,8 +71,16 @@ def get_operation_by_id(
|
|
69
71
|
.query(OperationDBM)
|
70
72
|
.filter(OperationDBM.id == filter_operation_id)
|
71
73
|
)
|
74
|
+
|
75
|
+
if lock:
|
76
|
+
query = query.with_for_update()
|
77
|
+
|
72
78
|
if raise_if_not_found:
|
73
|
-
|
79
|
+
try:
|
80
|
+
return query.one()
|
81
|
+
except NoResultFound:
|
82
|
+
if raise_if_not_found:
|
83
|
+
raise ValueError("Operation not found")
|
74
84
|
else:
|
75
85
|
return query.one_or_none()
|
76
86
|
|
@@ -138,7 +148,9 @@ class BaseOperationExecutor:
|
|
138
148
|
raise Exception("raise_fake_exception")
|
139
149
|
return operation_dbm
|
140
150
|
|
141
|
-
def sync_safe_execute_operation(
|
151
|
+
def sync_safe_execute_operation(
|
152
|
+
self, operation_dbm: OperationDBM, worker: OperationExecutorWorker
|
153
|
+
) -> OperationDBM:
|
142
154
|
self._logger.info(
|
143
155
|
f"start "
|
144
156
|
f"operation_dbm.id={operation_dbm.id}, "
|
@@ -152,6 +164,12 @@ class BaseOperationExecutor:
|
|
152
164
|
)
|
153
165
|
operation_dbm.execution_start_dt = now_utc_dt()
|
154
166
|
operation_dbm.status = OperationDBM.Statuses.executing
|
167
|
+
operation_dbm.output_data = combine_dicts(
|
168
|
+
operation_dbm.output_data,
|
169
|
+
{
|
170
|
+
worker.worker_fullname: True
|
171
|
+
}
|
172
|
+
)
|
155
173
|
session.commit()
|
156
174
|
session.refresh(operation_dbm)
|
157
175
|
|
@@ -221,7 +239,9 @@ class BaseOperationExecutor:
|
|
221
239
|
raise Exception("raise_fake_exception")
|
222
240
|
return operation_dbm
|
223
241
|
|
224
|
-
async def async_safe_execute_operation(
|
242
|
+
async def async_safe_execute_operation(
|
243
|
+
self, operation_dbm: OperationDBM, worker: OperationExecutorWorker
|
244
|
+
) -> OperationDBM:
|
225
245
|
self._logger.info(
|
226
246
|
f"start "
|
227
247
|
f"operation_dbm.id={operation_dbm.id}, "
|
@@ -235,6 +255,12 @@ class BaseOperationExecutor:
|
|
235
255
|
)
|
236
256
|
operation_dbm.execution_start_dt = now_utc_dt()
|
237
257
|
operation_dbm.status = OperationDBM.Statuses.executing
|
258
|
+
operation_dbm.output_data = combine_dicts(
|
259
|
+
operation_dbm.output_data,
|
260
|
+
{
|
261
|
+
worker.worker_fullname: True
|
262
|
+
}
|
263
|
+
)
|
238
264
|
session.commit()
|
239
265
|
session.refresh(operation_dbm)
|
240
266
|
|
@@ -305,19 +331,15 @@ class OperationExecutorWorker(BaseWorker):
|
|
305
331
|
sqlalchemy_db: SQLAlchemyDB,
|
306
332
|
operation_executor: BaseOperationExecutor | None = None,
|
307
333
|
filter_operation_types: str | list[str] | None = None,
|
308
|
-
timeout_after_run=timedelta(seconds=0.1),
|
309
|
-
timeout_after_err_in_run=timedelta(seconds=1),
|
310
334
|
startup_funcs: list[Any] | None = None
|
311
335
|
):
|
312
|
-
super().__init__(
|
313
|
-
timeout_after_run=timeout_after_run,
|
314
|
-
timeout_after_err_in_run=timeout_after_err_in_run,
|
315
|
-
startup_funcs=startup_funcs
|
316
|
-
)
|
336
|
+
super().__init__(startup_funcs=startup_funcs)
|
317
337
|
self.sqlalchemy_db = sqlalchemy_db
|
318
338
|
if operation_executor is None:
|
319
339
|
operation_executor = BaseOperationExecutor(sqlalchemy_db=sqlalchemy_db)
|
320
340
|
self.operation_executor = operation_executor
|
341
|
+
if isinstance(filter_operation_types, str):
|
342
|
+
filter_operation_types = [filter_operation_types]
|
321
343
|
self.filter_operation_types = filter_operation_types
|
322
344
|
|
323
345
|
def sync_on_startup(self):
|
@@ -325,7 +347,7 @@ class OperationExecutorWorker(BaseWorker):
|
|
325
347
|
self.sync_run_startup_funcs()
|
326
348
|
|
327
349
|
def sync_execute_operation(self, operation_dbm: OperationDBM) -> OperationDBM:
|
328
|
-
return self.operation_executor.sync_safe_execute_operation(operation_dbm=operation_dbm)
|
350
|
+
return self.operation_executor.sync_safe_execute_operation(operation_dbm=operation_dbm, worker=self)
|
329
351
|
|
330
352
|
def sync_run(self):
|
331
353
|
operation_dbm: OperationDBM | None = get_operation_for_execution(
|
@@ -344,7 +366,7 @@ class OperationExecutorWorker(BaseWorker):
|
|
344
366
|
await self.async_run_startup_funcs()
|
345
367
|
|
346
368
|
async def async_execute_operation(self, operation_dbm: OperationDBM) -> OperationDBM:
|
347
|
-
return await self.operation_executor.async_safe_execute_operation(operation_dbm=operation_dbm)
|
369
|
+
return await self.operation_executor.async_safe_execute_operation(operation_dbm=operation_dbm, worker=self)
|
348
370
|
|
349
371
|
async def async_run(self):
|
350
372
|
operation_dbm: OperationDBM | None = get_operation_for_execution(
|
@@ -373,19 +395,15 @@ class ScheduledOperationCreatorWorker(BaseWorker):
|
|
373
395
|
self,
|
374
396
|
*,
|
375
397
|
sqlalchemy_db: SQLAlchemyDB,
|
376
|
-
scheduled_operations: list[ScheduledOperation] | None = None,
|
377
|
-
timeout_after_run=timedelta(seconds=0.1),
|
378
|
-
timeout_after_err_in_run=timedelta(seconds=1),
|
398
|
+
scheduled_operations: ScheduledOperation | list[ScheduledOperation] | None = None,
|
379
399
|
startup_funcs: list[Any] | None = None
|
380
400
|
):
|
381
|
-
super().__init__(
|
382
|
-
timeout_after_run=timeout_after_run,
|
383
|
-
timeout_after_err_in_run=timeout_after_err_in_run,
|
384
|
-
startup_funcs=startup_funcs
|
385
|
-
)
|
401
|
+
super().__init__(startup_funcs=startup_funcs)
|
386
402
|
self.sqlalchemy_db = sqlalchemy_db
|
387
403
|
if scheduled_operations is None:
|
388
404
|
scheduled_operations = []
|
405
|
+
if isinstance(scheduled_operations, ScheduledOperation):
|
406
|
+
scheduled_operations = [scheduled_operations]
|
389
407
|
self.scheduled_operations = scheduled_operations
|
390
408
|
|
391
409
|
def sync_on_startup(self):
|
@@ -4,7 +4,7 @@ from datetime import datetime, timedelta
|
|
4
4
|
from typing import Any
|
5
5
|
from uuid import uuid4
|
6
6
|
|
7
|
-
from sqlalchemy import inspect, INTEGER, TEXT, TIMESTAMP
|
7
|
+
from sqlalchemy import inspect, INTEGER, TEXT, TIMESTAMP, func
|
8
8
|
from sqlalchemy.dialects.postgresql import JSONB
|
9
9
|
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
10
10
|
|
@@ -78,10 +78,12 @@ class SimpleDBM(BaseDBM):
|
|
78
78
|
INTEGER, primary_key=True, autoincrement=True, sort_order=-3, nullable=False
|
79
79
|
)
|
80
80
|
long_id: Mapped[str] = mapped_column(
|
81
|
-
TEXT, insert_default=generate_default_long_id,
|
81
|
+
TEXT, insert_default=generate_default_long_id, server_default=func.gen_random_uuid(),
|
82
|
+
unique=True, sort_order=-2, nullable=False
|
82
83
|
)
|
83
84
|
creation_dt: Mapped[datetime] = mapped_column(
|
84
|
-
TIMESTAMP(timezone=True), insert_default=now_utc_dt,
|
85
|
+
TIMESTAMP(timezone=True), insert_default=now_utc_dt, server_default=func.now(),
|
86
|
+
index=True, sort_order=-1, nullable=False
|
85
87
|
)
|
86
88
|
|
87
89
|
def __repr__(self):
|
@@ -120,7 +120,7 @@ arpakitlib/ar_arpakit_project_template_util.py,sha256=AswzQvvb-zfUyrcP4EP0K756YL
|
|
120
120
|
arpakitlib/ar_arpakit_schedule_uust_api_client_util.py,sha256=jGbP6egs2yhgfheyqhM0J-SeM2qp2YrW7dV-u9djv4Q,19223
|
121
121
|
arpakitlib/ar_arpakitlib_cli_util.py,sha256=8lhEDxnwMSRX2PGV2xQtQru1AYKSA92SVolol5u7iBk,3154
|
122
122
|
arpakitlib/ar_base64_util.py,sha256=aZkg2cZTuAaP2IWeG_LXJ6RO7qhyskVwec-Lks0iM-k,676
|
123
|
-
arpakitlib/ar_base_worker_util.py,sha256=
|
123
|
+
arpakitlib/ar_base_worker_util.py,sha256=hV_bMfkO897s00VGMAQ-z8GFzq-vEcRW5TKcmeBhbYM,5667
|
124
124
|
arpakitlib/ar_cache_file_util.py,sha256=Fo2pH-Zqm966KWFBHG_pbiySGZvhIFCYqy7k1weRfJ0,3476
|
125
125
|
arpakitlib/ar_datetime_util.py,sha256=Xe1NiT9oPQzNSG7RVRkhukhbg4i-hhS5ImmV7sPUc8o,971
|
126
126
|
arpakitlib/ar_dict_util.py,sha256=cF5LQJ6tLqyGoEXfDljMDZrikeZoWPw7CgINHIFGvXM,419
|
@@ -164,23 +164,23 @@ arpakitlib/ar_logging_util.py,sha256=mx3H6CzX9dsh29ruFmYnva8lL6mwvdBXmeHH9E2tvu8
|
|
164
164
|
arpakitlib/ar_mongodb_util.py,sha256=2ECkTnGAZ92qxioL-fmN6R4yZOSr3bXdXLWTzT1C3vk,4038
|
165
165
|
arpakitlib/ar_need_type_util.py,sha256=GETiREPMEYhch-yU6T--Bdawlbb04Jp1Qy7cOsUlIeA,2228
|
166
166
|
arpakitlib/ar_openai_api_client_util.py,sha256=_XmlApvHFMSyjvZydPa_kASIt9LsFrZmSC7YEzIG8Bg,1806
|
167
|
-
arpakitlib/ar_operation_execution_util.py,sha256=
|
167
|
+
arpakitlib/ar_operation_execution_util.py,sha256=IxHn-Kv5W5BE-MK7Ok7SEiBaV3UEZK-dyS4HcBiKx2Y,18528
|
168
168
|
arpakitlib/ar_parse_command.py,sha256=-s61xcATIsfw1eV_iD3xi-grsitbGzSDoAFc5V0OFy4,3447
|
169
169
|
arpakitlib/ar_postgresql_util.py,sha256=1AuLjEaa1Lg4pzn-ukCVnDi35Eg1k91APRTqZhIJAdo,945
|
170
170
|
arpakitlib/ar_run_cmd_util.py,sha256=D_rPavKMmWkQtwvZFz-Io5Ak8eSODHkcFeLPzNVC68g,1072
|
171
171
|
arpakitlib/ar_schedule_uust_api_client_util.py,sha256=0Ns0mMEXYEkVmP6YTAXHyNcrhNsvCJ8X-G_5XwILhJ4,6855
|
172
172
|
arpakitlib/ar_settings_util.py,sha256=EFRh5WrB_iaqwxy0rGAtnyGtA6JkNCan-Y_Y6XWb4UY,1583
|
173
173
|
arpakitlib/ar_sleep_util.py,sha256=OaLtRaJQWMkGjfj_mW1RB2P4RaSWsAIH8LUoXqsH0zM,1061
|
174
|
-
arpakitlib/ar_sqlalchemy_model_util.py,sha256=
|
174
|
+
arpakitlib/ar_sqlalchemy_model_util.py,sha256=nKJGN32eg3Gn5kmJwHdVJznPT5TydLsfUfwJGdypdUo,6264
|
175
175
|
arpakitlib/ar_sqlalchemy_util.py,sha256=Hcg1THrDsSR_-8dsY1CG3NWPEv0FqCbkPXFXLtjlSJ0,4207
|
176
176
|
arpakitlib/ar_ssh_runner_util.py,sha256=e9deuUdBW7Eh0Exx2nTBhk57SaOZYaJaSjNk8q6dbJk,6804
|
177
177
|
arpakitlib/ar_str_util.py,sha256=tFoGSDYoGpfdVHWor5Li9pEOFmDFlHkX-Z8iOy1LK7Y,3537
|
178
178
|
arpakitlib/ar_type_util.py,sha256=s0NsTM7mV3HuwyRwyYLdNn7Ep2HbyI4FIr-dd8x0lfI,3734
|
179
179
|
arpakitlib/ar_yookassa_api_client_util.py,sha256=sh4fcUkAkdOetFn9JYoTvjcSXP-M1wU04KEY-ECLfLg,5137
|
180
180
|
arpakitlib/ar_zabbix_api_client_util.py,sha256=Q-VR4MvoZ9aHwZeYZr9G3LwN-ANx1T5KFmF6pvPM-9M,6402
|
181
|
-
arpakitlib-1.7.
|
182
|
-
arpakitlib-1.7.
|
183
|
-
arpakitlib-1.7.
|
184
|
-
arpakitlib-1.7.
|
185
|
-
arpakitlib-1.7.
|
186
|
-
arpakitlib-1.7.
|
181
|
+
arpakitlib-1.7.94.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
|
182
|
+
arpakitlib-1.7.94.dist-info/METADATA,sha256=NgOHw48sxSmfiHkvGRrQbEtwEHA-zJakMbaeet98Wko,2824
|
183
|
+
arpakitlib-1.7.94.dist-info/NOTICE,sha256=95aUzaPJjVpDsGAsNzVnq7tHTxAl0s5UFznCTkVCau4,763
|
184
|
+
arpakitlib-1.7.94.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
185
|
+
arpakitlib-1.7.94.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
|
186
|
+
arpakitlib-1.7.94.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|