altcodepro-polydb-python 2.3.27__py3-none-any.whl → 2.3.28__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.
- {altcodepro_polydb_python-2.3.27.dist-info → altcodepro_polydb_python-2.3.28.dist-info}/METADATA +1 -1
- {altcodepro_polydb_python-2.3.27.dist-info → altcodepro_polydb_python-2.3.28.dist-info}/RECORD +8 -8
- polydb/adapters/AzureQueueAdapter.py +59 -1
- polydb/adapters/MongoDBAdapter.py +3 -1
- polydb/databaseFactory.py +20 -17
- {altcodepro_polydb_python-2.3.27.dist-info → altcodepro_polydb_python-2.3.28.dist-info}/WHEEL +0 -0
- {altcodepro_polydb_python-2.3.27.dist-info → altcodepro_polydb_python-2.3.28.dist-info}/licenses/LICENSE +0 -0
- {altcodepro_polydb_python-2.3.27.dist-info → altcodepro_polydb_python-2.3.28.dist-info}/top_level.txt +0 -0
{altcodepro_polydb_python-2.3.27.dist-info → altcodepro_polydb_python-2.3.28.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: altcodepro-polydb-python
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.28
|
|
4
4
|
Summary: Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety
|
|
5
5
|
Author: AltCodePro
|
|
6
6
|
Project-URL: Homepage, https://github.com/altcodepro/polydb-python
|
{altcodepro_polydb_python-2.3.27.dist-info → altcodepro_polydb_python-2.3.28.dist-info}/RECORD
RENAMED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
altcodepro_polydb_python-2.3.
|
|
1
|
+
altcodepro_polydb_python-2.3.28.dist-info/licenses/LICENSE,sha256=9X8GLocsBwy-5aR5JGOt2SAMDDPs9Qv-YnqmHBHOXrw,1067
|
|
2
2
|
polydb/PolyDB.py,sha256=MG7-nV59zDvrUQwXNEgV4eetHCeW9TYY9DQqtxu_r7k,23543
|
|
3
3
|
polydb/__init__.py,sha256=d9KSz888A336904TPEjWPYBtyJLPB0lTdcaOnYp8byY,1535
|
|
4
4
|
polydb/advanced_query.py,sha256=cxMB-EB-qT3bWXJlhmjnMCUtrzogORWyoEfS50Dy7go,4280
|
|
5
5
|
polydb/batch.py,sha256=_DjWZa1ZXYSk6MLKqFe0eT7SYVRZtYNqZb9bI8Y2sao,4566
|
|
6
6
|
polydb/cache.py,sha256=JBXF1XEK-fY80ar8SDE893Z1Z116YtXAEG0PaJ0Nkcw,7658
|
|
7
7
|
polydb/cloudDatabaseFactory.py,sha256=Gp6L__YtgrkGahD8B7ItzXMHCoj2ZUGDjXLS9w0TujY,17780
|
|
8
|
-
polydb/databaseFactory.py,sha256=
|
|
8
|
+
polydb/databaseFactory.py,sha256=AdEMRtWcwf0elJ9JhrWS5krre-3RVFfIgt0mA93eJJQ,44068
|
|
9
9
|
polydb/decorators.py,sha256=L_WP2uXP_k8Ac49SUm-mthbM4jWI-XYfHXEyKzumOww,43062
|
|
10
10
|
polydb/errors.py,sha256=jF_55fbfg6b6mBc116dzU_uBG9qiIKbr5jvmDuJx-Jw,1573
|
|
11
11
|
polydb/json_safe.py,sha256=R5PrqAGirqjYKPyy-8KH-lSXjLH0FPr2TSGozy4eheU,149
|
|
@@ -23,7 +23,7 @@ polydb/utils.py,sha256=tS_KI1vt-VZ6JftbFxbnVxC0xpie1-zYikTGf1nSoFw,3183
|
|
|
23
23
|
polydb/validation.py,sha256=a1o1d02k3c6PWQwkBbw_0nEmIgrdB5RR8OcpNQMn4cA,4810
|
|
24
24
|
polydb/adapters/AzureBlobStorageAdapter.py,sha256=4vD55Z8DBTzBK66jIJbo5bNMY-AQ61MlP0-P2Fv_JgQ,7083
|
|
25
25
|
polydb/adapters/AzureFileStorageAdapter.py,sha256=VZNprqlBXCuWUgtqClNT-NrQmRf-XFYEiRA2BLbf-Sc,7046
|
|
26
|
-
polydb/adapters/AzureQueueAdapter.py,sha256
|
|
26
|
+
polydb/adapters/AzureQueueAdapter.py,sha256=-MLEEAt2yvYjlB5LqT2TwoRa3rVDHDVzXhm1gykqeDM,8052
|
|
27
27
|
polydb/adapters/AzureTableStorageAdapter.py,sha256=FCWojIRyOQdDyga5sxbHXBfZMdb3T1xn3Q2VX5klebo,27824
|
|
28
28
|
polydb/adapters/BlockchainBlobAdapter.py,sha256=D01Yua9mkKfaQrxKYApblIyI6DSP0dtNAh4Tav51HJ4,3299
|
|
29
29
|
polydb/adapters/BlockchainFileAdapter.py,sha256=G749xOVpG20HuKS8zCgi6PMjoJNu-YXK7zitygjLdzM,8335
|
|
@@ -35,7 +35,7 @@ polydb/adapters/FirestoreAdapter.py,sha256=e3DEODOZ-UAWULLtGTIED-Ym1JrxOKaOLPQVK
|
|
|
35
35
|
polydb/adapters/GCPFilestoreAdapter.py,sha256=yjFQQwsWYWc8mo8XwMViVTWb5_D--xAyTMvE-4AOpNM,3006
|
|
36
36
|
polydb/adapters/GCPPubSubAdapter.py,sha256=7XNots2VA0ReEDku-rjg-OTYmftIpx5UgnXYDdXNkOo,8692
|
|
37
37
|
polydb/adapters/GCPStorageAdapter.py,sha256=9yS1Jhcn5_rCRdZ5uOqcRW6Ba-UNb6VOYpwENP-C6Qk,7133
|
|
38
|
-
polydb/adapters/MongoDBAdapter.py,sha256=
|
|
38
|
+
polydb/adapters/MongoDBAdapter.py,sha256=Ak8q_qeAaUTe0Yc2oEefmAbzoODjTWVZQOirW-seBQU,7574
|
|
39
39
|
polydb/adapters/PostgreSQLAdapter.py,sha256=lUe65kpJkTu6R1Tc1zgl63Fa1PI44UVbWRTKRUSSGak,36579
|
|
40
40
|
polydb/adapters/S3Adapter.py,sha256=5R0zHAL2SkGFjp1L3bp-IU468bXYdSf6nKx974MN104,7586
|
|
41
41
|
polydb/adapters/S3CompatibleAdapter.py,sha256=jpafqbAjA8-irdXBrfXa1QJySIzrcUQ6UrFt5h5FAEc,7006
|
|
@@ -60,7 +60,7 @@ polydb/observability/logging.py,sha256=gv1SshNjaSkmR98Uv7Qbwq3gkeQUzVCA4ichqHKL6
|
|
|
60
60
|
polydb/services/__init__.py,sha256=nMolMXzmhejRSm7f42kU9fCHF0GIpqpejN2kywuBsUQ,241
|
|
61
61
|
polydb/services/compliance_service.py,sha256=-0ncX7PrXy4-NuyWXIv65cLUsqz0jXzb3LtsD8rlrXE,4983
|
|
62
62
|
polydb/services/security_service.py,sha256=q7GzK6sbMnBBsc4fuL4oXTd-ui2b-MYQyrWTBvdVNSk,5062
|
|
63
|
-
altcodepro_polydb_python-2.3.
|
|
64
|
-
altcodepro_polydb_python-2.3.
|
|
65
|
-
altcodepro_polydb_python-2.3.
|
|
66
|
-
altcodepro_polydb_python-2.3.
|
|
63
|
+
altcodepro_polydb_python-2.3.28.dist-info/METADATA,sha256=mCw1bRtSYL37sOtvm-Rn5fQdjfCwLZPcZkAiOk-PN68,12359
|
|
64
|
+
altcodepro_polydb_python-2.3.28.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
65
|
+
altcodepro_polydb_python-2.3.28.dist-info/top_level.txt,sha256=WgLFWJoYjUhwvyPxJFl6jYLrVFuBJDX3OABf4ocwk_E,7
|
|
66
|
+
altcodepro_polydb_python-2.3.28.dist-info/RECORD,,
|
|
@@ -46,6 +46,20 @@ class AzureQueueAdapter(QueueAdapter):
|
|
|
46
46
|
name = re.sub(r"-+", "-", name) # collapse multiple dashes
|
|
47
47
|
return name.strip("-")
|
|
48
48
|
|
|
49
|
+
@staticmethod
|
|
50
|
+
def _encode_receipt(message_id: str, pop_receipt: str) -> str:
|
|
51
|
+
# Azure needs BOTH message_id and pop_receipt to delete/update a
|
|
52
|
+
# message, but the generic queue contract (WorkerPool, SQSAdapter)
|
|
53
|
+
# only ever carries a single opaque `receipt_handle` string end to
|
|
54
|
+
# end. Pack both into one string here so ack()/nack() can recover
|
|
55
|
+
# them without any caller needing to know Azure's two-part scheme.
|
|
56
|
+
return f"{message_id}|{pop_receipt}"
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def _decode_receipt(receipt_handle: str) -> "tuple[str, str]":
|
|
60
|
+
message_id, _, pop_receipt = (receipt_handle or "").partition("|")
|
|
61
|
+
return message_id, pop_receipt
|
|
62
|
+
|
|
49
63
|
def _initialize_client(self) -> None:
|
|
50
64
|
"""Initialize Azure Queue client"""
|
|
51
65
|
from azure.storage.queue import QueueServiceClient
|
|
@@ -114,6 +128,13 @@ class AzureQueueAdapter(QueueAdapter):
|
|
|
114
128
|
{
|
|
115
129
|
"id": msg.id,
|
|
116
130
|
"pop_receipt": msg.pop_receipt,
|
|
131
|
+
# WorkerPool (and the generic UDL queue contract)
|
|
132
|
+
# reads this key to ack/nack. Without it, ack() is
|
|
133
|
+
# always skipped (falsy receipt_handle), the message
|
|
134
|
+
# is never deleted, and it silently reappears once
|
|
135
|
+
# Azure's visibility timeout elapses — replaying the
|
|
136
|
+
# same task forever even after it already ran.
|
|
137
|
+
"receipt_handle": self._encode_receipt(msg.id, msg.pop_receipt),
|
|
117
138
|
"body": payload,
|
|
118
139
|
}
|
|
119
140
|
)
|
|
@@ -156,8 +177,14 @@ class AzureQueueAdapter(QueueAdapter):
|
|
|
156
177
|
Preferred usage:
|
|
157
178
|
ack(pop_receipt=..., message_id=...)
|
|
158
179
|
|
|
159
|
-
|
|
180
|
+
Generic callers (WorkerPool, storage_router.queue_ack) only pass a
|
|
181
|
+
single `receipt_handle` positionally and never supply message_id —
|
|
182
|
+
in that case `pop_receipt` here is actually the combined value
|
|
183
|
+
produced by `receive()` (`"<message_id>|<pop_receipt>"`), so decode
|
|
184
|
+
it rather than failing.
|
|
160
185
|
"""
|
|
186
|
+
if not message_id:
|
|
187
|
+
message_id, pop_receipt = self._decode_receipt(pop_receipt)
|
|
161
188
|
if not message_id:
|
|
162
189
|
raise QueueError("AzureQueueAdapter.ack requires message_id")
|
|
163
190
|
queue_name = self._normalize_queue_name(queue_name)
|
|
@@ -166,3 +193,34 @@ class AzureQueueAdapter(QueueAdapter):
|
|
|
166
193
|
queue_name=queue_name,
|
|
167
194
|
pop_receipt=pop_receipt,
|
|
168
195
|
)
|
|
196
|
+
|
|
197
|
+
def nack(
|
|
198
|
+
self,
|
|
199
|
+
queue_name: str,
|
|
200
|
+
ack_id: str,
|
|
201
|
+
*,
|
|
202
|
+
delay: Optional[int] = None,
|
|
203
|
+
) -> bool:
|
|
204
|
+
"""
|
|
205
|
+
Make a message visible again (optionally after `delay` seconds)
|
|
206
|
+
without deleting it, instead of leaving it to reappear only once
|
|
207
|
+
Azure's default visibility timeout elapses.
|
|
208
|
+
|
|
209
|
+
`ack_id` is the combined `"<message_id>|<pop_receipt>"` receipt
|
|
210
|
+
handle produced by `receive()`. Matches the positional order used
|
|
211
|
+
by storage_router.queue_nack: nack(queue_name, ack_id, delay=...).
|
|
212
|
+
"""
|
|
213
|
+
message_id, pop_receipt = self._decode_receipt(ack_id)
|
|
214
|
+
if not message_id:
|
|
215
|
+
raise QueueError("AzureQueueAdapter.nack requires a receipt_handle from receive()")
|
|
216
|
+
try:
|
|
217
|
+
queue_name = self._normalize_queue_name(queue_name)
|
|
218
|
+
queue_client = self._get_queue(queue_name)
|
|
219
|
+
queue_client.update_message(
|
|
220
|
+
message_id,
|
|
221
|
+
pop_receipt=pop_receipt,
|
|
222
|
+
visibility_timeout=delay or 0,
|
|
223
|
+
)
|
|
224
|
+
return True
|
|
225
|
+
except Exception as e:
|
|
226
|
+
raise QueueError(f"Azure Queue nack failed: {e}")
|
|
@@ -50,7 +50,9 @@ class MongoDBAdapter(NoSQLKVAdapter):
|
|
|
50
50
|
self.mongo_uri,
|
|
51
51
|
maxPoolSize=int(os.getenv("MONGODB_MAX_POOL_SIZE", "10")),
|
|
52
52
|
minPoolSize=int(os.getenv("MONGODB_MIN_POOL_SIZE", "1")),
|
|
53
|
-
serverSelectionTimeoutMS=
|
|
53
|
+
serverSelectionTimeoutMS=int(
|
|
54
|
+
os.getenv("MONGODB_SERVER_SELECTION_TIMEOUT_MS", "5000")
|
|
55
|
+
),
|
|
54
56
|
)
|
|
55
57
|
|
|
56
58
|
self._client.server_info()
|
polydb/databaseFactory.py
CHANGED
|
@@ -109,8 +109,8 @@ class EngineOverride:
|
|
|
109
109
|
|
|
110
110
|
@dataclass
|
|
111
111
|
class _ResolvedAdapters:
|
|
112
|
-
sql: PostgreSQLAdapter
|
|
113
|
-
nosql: NoSQLKVAdapter
|
|
112
|
+
sql: Optional[PostgreSQLAdapter]
|
|
113
|
+
nosql: Optional[NoSQLKVAdapter]
|
|
114
114
|
engine_name: str
|
|
115
115
|
|
|
116
116
|
|
|
@@ -233,35 +233,38 @@ class DatabaseFactory:
|
|
|
233
233
|
def _resolve_adapters(
|
|
234
234
|
self, model_name: str, storage: str, override: Optional[EngineOverride] = None
|
|
235
235
|
) -> _ResolvedAdapters:
|
|
236
|
+
# Only construct the adapter this call actually needs. Every caller
|
|
237
|
+
# (create/read/update/upsert/delete/query*) branches on `storage` and
|
|
238
|
+
# touches exactly one of .sql/.nosql — building both unconditionally
|
|
239
|
+
# meant a purely-SQL deployment with no reachable NoSQL backend (or
|
|
240
|
+
# vice versa) failed on every single call, not just ones that needed
|
|
241
|
+
# the missing engine.
|
|
242
|
+
def _build(engine: "EngineConfig") -> _ResolvedAdapters:
|
|
243
|
+
return _ResolvedAdapters(
|
|
244
|
+
sql=engine.sql() if storage == "sql" else None,
|
|
245
|
+
nosql=engine.nosql() if storage == "nosql" else None,
|
|
246
|
+
engine_name=engine.name,
|
|
247
|
+
)
|
|
248
|
+
|
|
236
249
|
if override:
|
|
237
250
|
engine = self._engine_by_name.get(override.engine_name)
|
|
238
251
|
if engine is None:
|
|
239
252
|
raise AdapterConfigurationError(
|
|
240
253
|
f"Unknown engine '{override.engine_name}'. Available: {list(self._engine_by_name)}"
|
|
241
254
|
)
|
|
242
|
-
return
|
|
243
|
-
sql=engine.sql(), nosql=engine.nosql(), engine_name=engine.name
|
|
244
|
-
)
|
|
255
|
+
return _build(engine)
|
|
245
256
|
|
|
246
257
|
for engine in self._engines:
|
|
247
258
|
if storage == "sql" and engine.sql_models and model_name in engine.sql_models:
|
|
248
|
-
return
|
|
249
|
-
sql=engine.sql(), nosql=engine.nosql(), engine_name=engine.name
|
|
250
|
-
)
|
|
259
|
+
return _build(engine)
|
|
251
260
|
if storage == "nosql" and engine.nosql_models and model_name in engine.nosql_models:
|
|
252
|
-
return
|
|
253
|
-
sql=engine.sql(), nosql=engine.nosql(), engine_name=engine.name
|
|
254
|
-
)
|
|
261
|
+
return _build(engine)
|
|
255
262
|
|
|
256
263
|
for engine in self._engines:
|
|
257
264
|
if storage == "sql" and engine.is_default_sql:
|
|
258
|
-
return
|
|
259
|
-
sql=engine.sql(), nosql=engine.nosql(), engine_name=engine.name
|
|
260
|
-
)
|
|
265
|
+
return _build(engine)
|
|
261
266
|
if storage == "nosql" and engine.is_default_nosql:
|
|
262
|
-
return
|
|
263
|
-
sql=engine.sql(), nosql=engine.nosql(), engine_name=engine.name
|
|
264
|
-
)
|
|
267
|
+
return _build(engine)
|
|
265
268
|
|
|
266
269
|
raise AdapterConfigurationError(f"No engine for model='{model_name}' storage='{storage}'")
|
|
267
270
|
|
{altcodepro_polydb_python-2.3.27.dist-info → altcodepro_polydb_python-2.3.28.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|