altcodepro-polydb-python 2.3.2__tar.gz → 2.3.4__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 (81) hide show
  1. {altcodepro_polydb_python-2.3.2/src/altcodepro_polydb_python.egg-info → altcodepro_polydb_python-2.3.4}/PKG-INFO +1 -1
  2. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/pyproject.toml +1 -1
  3. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4/src/altcodepro_polydb_python.egg-info}/PKG-INFO +1 -1
  4. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/AzureTableStorageAdapter.py +87 -23
  5. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/LICENSE +0 -0
  6. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/MANIFEST.in +0 -0
  7. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/README.md +0 -0
  8. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/example_usage.py +0 -0
  9. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/requirements-aws.txt +0 -0
  10. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/requirements-azure.txt +0 -0
  11. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/requirements-dev.txt +0 -0
  12. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/requirements-gcp.txt +0 -0
  13. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/requirements-generic.txt +0 -0
  14. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/requirements.txt +0 -0
  15. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/setup.cfg +0 -0
  16. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/setup.py +0 -0
  17. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/altcodepro_polydb_python.egg-info/SOURCES.txt +0 -0
  18. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/altcodepro_polydb_python.egg-info/dependency_links.txt +0 -0
  19. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/altcodepro_polydb_python.egg-info/requires.txt +0 -0
  20. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/altcodepro_polydb_python.egg-info/top_level.txt +0 -0
  21. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/PolyDB.py +0 -0
  22. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/__init__.py +0 -0
  23. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/AzureBlobStorageAdapter.py +0 -0
  24. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/AzureFileStorageAdapter.py +0 -0
  25. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/AzureQueueAdapter.py +0 -0
  26. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/BlockchainBlobAdapter.py +0 -0
  27. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/BlockchainKVAdapter.py +0 -0
  28. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/BlockchainQueueAdapter.py +0 -0
  29. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/DynamoDBAdapter.py +0 -0
  30. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/EFSAdapter.py +0 -0
  31. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/FirestoreAdapter.py +0 -0
  32. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/GCPPubSubAdapter.py +0 -0
  33. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/GCPStorageAdapter.py +0 -0
  34. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/MongoDBAdapter.py +0 -0
  35. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/PostgreSQLAdapter.py +0 -0
  36. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/S3Adapter.py +0 -0
  37. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/S3CompatibleAdapter.py +0 -0
  38. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/SQSAdapter.py +0 -0
  39. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/VercelBlobAdapter.py +0 -0
  40. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/VercelKVAdapter.py +0 -0
  41. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/VercelQueueAdapter.py +0 -0
  42. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/adapters/__init__.py +0 -0
  43. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/advanced_query.py +0 -0
  44. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/audit/AuditStorage.py +0 -0
  45. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/audit/__init__.py +0 -0
  46. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/audit/context.py +0 -0
  47. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/audit/manager.py +0 -0
  48. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/audit/models.py +0 -0
  49. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/base/NoSQLKVAdapter.py +0 -0
  50. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/base/ObjectStorageAdapter.py +0 -0
  51. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/base/QueueAdapter.py +0 -0
  52. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/base/SharedFilesAdapter.py +0 -0
  53. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/base/__init__.py +0 -0
  54. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/batch.py +0 -0
  55. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/cache.py +0 -0
  56. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/cloudDatabaseFactory.py +0 -0
  57. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/databaseFactory.py +0 -0
  58. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/decorators.py +0 -0
  59. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/errors.py +0 -0
  60. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/json_safe.py +0 -0
  61. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/models.py +0 -0
  62. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/monitoring.py +0 -0
  63. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/multitenancy.py +0 -0
  64. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/py.typed +0 -0
  65. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/query.py +0 -0
  66. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/registry.py +0 -0
  67. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/retry.py +0 -0
  68. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/schema.py +0 -0
  69. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/security.py +0 -0
  70. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/types.py +0 -0
  71. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/utils.py +0 -0
  72. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/src/polydb/validation.py +0 -0
  73. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/tests/test_aws.py +0 -0
  74. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/tests/test_azure.py +0 -0
  75. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/tests/test_blockchain.py +0 -0
  76. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/tests/test_cloud_factory.py +0 -0
  77. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/tests/test_gcp.py +0 -0
  78. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/tests/test_mongodb.py +0 -0
  79. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/tests/test_multi_engine.py +0 -0
  80. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/tests/test_postgresql.py +0 -0
  81. {altcodepro_polydb_python-2.3.2 → altcodepro_polydb_python-2.3.4}/tests/test_vercel.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: altcodepro-polydb-python
3
- Version: 2.3.2
3
+ Version: 2.3.4
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "altcodepro-polydb-python"
7
- version = "2.3.2"
7
+ version = "2.3.4"
8
8
  description = "Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: altcodepro-polydb-python
3
- Version: 2.3.2
3
+ Version: 2.3.4
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
@@ -283,11 +283,22 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
283
283
 
284
284
  return out
285
285
 
286
+ def _sanitize_blob_part(self, value: str) -> str:
287
+ # Blob-safe: lowercase, alphanumeric + dash only
288
+ s = str(value).lower()
289
+ s = re.sub(r"[^a-z0-9\-]", "-", s)
290
+ s = re.sub(r"-+", "-", s)
291
+ return s.strip("-")[:100]
292
+
286
293
  def _entity_size_bytes(self, entity: JsonDict) -> int:
287
294
  return len(json.dumps(entity, default=json_safe).encode("utf-8"))
288
295
 
289
296
  def _blob_key(self, pk: str, rk: str, checksum: str) -> str:
290
- return f"{pk}/{rk}/{checksum}.json"
297
+ safe_pk = self._sanitize_blob_part(pk)
298
+ safe_rk = self._sanitize_blob_part(rk)
299
+
300
+ # Flat structure (avoid deep paths)
301
+ return f"{safe_pk}-{safe_rk}-{checksum}.json"
291
302
 
292
303
  def _blob_upload(self, blob_key: str, data_bytes: bytes):
293
304
  if not self._blob_service:
@@ -325,27 +336,52 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
325
336
 
326
337
  entity = self._pack_entity(model, safe_pk, safe_rk, data)
327
338
 
328
- # === Check for any dangerously large property (64KB limit) ===
339
+ # ---------------------------------------------------
340
+ # SIZE ESTIMATION (Azure uses UTF-16 for strings)
341
+ # ---------------------------------------------------
342
+ MAX_PROPERTY_CHARS = 30 * 1024 # ~30K chars safe under 32K limit
343
+ MAX_ENTITY_SIZE = 40 * 1024 # conservative total entity threshold
344
+
345
+ def _is_large_string(val: Any) -> bool:
346
+ return isinstance(val, str) and len(val) > MAX_PROPERTY_CHARS
347
+
348
+ # Check if any property exceeds safe char limit
329
349
  has_large_property = False
350
+ large_key = None
351
+
330
352
  for key, value in entity.items():
331
- if (
332
- isinstance(value, str) and len(value.encode("utf-8")) > 60 * 1024
333
- ): # 60KB safe margin
353
+ if _is_large_string(value):
334
354
  has_large_property = True
335
- logger.info(
336
- f"Large property detected: {key} ({len(value.encode('utf-8'))//1024} KB) → overflowing to Blob"
355
+ large_key = key
356
+ logger.warning(
357
+ f"LARGE PROPERTY DETECTED: {key} length={len(value)} chars → forcing blob overflow"
337
358
  )
338
359
  break
339
360
 
340
- if has_large_property or self._entity_size_bytes(entity) > self.AZURE_TABLE_MAX_SIZE:
341
- # Force full overflow to Blob
342
- full_payload_bytes = json.dumps(entity, default=json_safe).encode("utf-8")
361
+ # Check full entity size
362
+ entity_json = json.dumps(entity, default=json_safe)
363
+ entity_size = len(entity_json.encode("utf-8"))
364
+
365
+ force_blob = has_large_property or entity_size > MAX_ENTITY_SIZE
366
+
367
+ if force_blob:
368
+ logger.info(
369
+ f"Entity exceeds safe limits → using blob storage "
370
+ f"(size={entity_size//1024} KB, large_key={large_key})"
371
+ )
372
+
373
+ # ---------------------------------------------------
374
+ # STORE FULL ENTITY IN BLOB
375
+ # ---------------------------------------------------
376
+ full_payload_bytes = entity_json.encode("utf-8")
343
377
  checksum = hashlib.md5(full_payload_bytes).hexdigest()
344
378
  blob_key = self._blob_key(safe_pk, safe_rk, checksum)
345
379
 
346
380
  self._blob_upload(blob_key, full_payload_bytes)
347
381
 
348
- # Create small reference record for Table
382
+ # ---------------------------------------------------
383
+ # STORE REFERENCE IN TABLE
384
+ # ---------------------------------------------------
349
385
  reference_entity = {
350
386
  "PartitionKey": safe_pk,
351
387
  "RowKey": safe_rk,
@@ -357,33 +393,49 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
357
393
  "__keymap__": entity.get("__keymap__", "{}"),
358
394
  }
359
395
 
360
- # Keep a few small scalar fields for basic queries
396
+ # Keep only small searchable fields
361
397
  for k, v in entity.items():
362
398
  if k in ("PartitionKey", "RowKey", "__keymap__", _MODEL_FIELD):
363
399
  continue
364
400
  if k.startswith("_"):
365
401
  continue
366
- if (
367
- v is None
368
- or isinstance(v, (str, bool, int, float, datetime))
369
- and len(str(v)) < 1000
370
- ):
402
+
403
+ if isinstance(v, (str, bool, int, float)):
404
+ if isinstance(v, str) and len(v) > 2000:
405
+ continue
406
+ reference_entity[k] = v
407
+
408
+ elif isinstance(v, datetime):
371
409
  reference_entity[k] = v
372
410
 
373
411
  self._table_client.upsert_entity(reference_entity)
374
- logger.info(f"Entity overflowed to Blob: {blob_key}")
412
+
413
+ logger.info(f"Stored in blob: {blob_key} ({len(full_payload_bytes)//1024} KB)")
414
+
375
415
  return {
376
416
  "PartitionKey": safe_pk,
377
417
  "RowKey": safe_rk,
378
418
  "_overflow": True,
419
+ "_blob_key": blob_key,
379
420
  "id": safe_rk,
380
421
  }
381
422
 
382
- # Normal small entity
423
+ # ---------------------------------------------------
424
+ # NORMAL TABLE STORAGE
425
+ # ---------------------------------------------------
383
426
  self._table_client.upsert_entity(entity)
384
- return {"PartitionKey": safe_pk, "RowKey": safe_rk, "id": safe_rk}
427
+
428
+ return {
429
+ "PartitionKey": safe_pk,
430
+ "RowKey": safe_rk,
431
+ "id": safe_rk,
432
+ }
385
433
 
386
434
  except Exception as e:
435
+ # Do NOT retry deterministic size errors
436
+ if "PropertyValueTooLarge" in str(e):
437
+ raise NoSQLError("Azure Table limit exceeded → must use blob overflow") from e
438
+
387
439
  raise NoSQLError(f"Azure Table put failed: {str(e)}")
388
440
 
389
441
  @retry(max_attempts=3, delay=1.0, exceptions=(NoSQLError,))
@@ -417,6 +469,9 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
417
469
 
418
470
  restored = json.loads(blob_data.decode("utf-8"))
419
471
  out = self._unpack_entity(restored)
472
+ out["_overflow"] = True
473
+ out["_blob_key"] = blob_key
474
+ out["_checksum"] = checksum
420
475
  if "id" not in out:
421
476
  out["id"] = safe_rk
422
477
  return out
@@ -489,10 +544,19 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
489
544
 
490
545
  if ent_dict.get("_overflow"):
491
546
  blob_key = ent_dict.get("_blob_key")
547
+ checksum = ent_dict.get("_checksum")
492
548
  if blob_key:
493
- blob_data = self._blob_download(blob_key)
494
- restored = json.loads(blob_data.decode("utf-8"))
495
- out = self._unpack_entity(restored)
549
+ try:
550
+ blob_data = self._blob_download(blob_key)
551
+ actual_checksum = hashlib.md5(blob_data).hexdigest()
552
+ if checksum and actual_checksum != checksum:
553
+ raise NoSQLError("Checksum mismatch")
554
+
555
+ restored = json.loads(blob_data.decode("utf-8"))
556
+ out = self._unpack_entity(restored)
557
+ except Exception as e:
558
+ logger.error(f"Blob read failed, falling back to table: {e}")
559
+ out = self._unpack_entity(ent_dict)
496
560
  else:
497
561
  out = self._unpack_entity(ent_dict)
498
562
  else: