altcodepro-polydb-python 2.3.18__tar.gz → 2.3.20__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 (83) hide show
  1. {altcodepro_polydb_python-2.3.18/src/altcodepro_polydb_python.egg-info → altcodepro_polydb_python-2.3.20}/PKG-INFO +1 -1
  2. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/pyproject.toml +1 -1
  3. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20/src/altcodepro_polydb_python.egg-info}/PKG-INFO +1 -1
  4. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/__init__.py +0 -2
  5. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/PostgreSQLAdapter.py +51 -45
  6. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/retry.py +17 -7
  7. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/LICENSE +0 -0
  8. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/MANIFEST.in +0 -0
  9. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/README.md +0 -0
  10. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/requirements-aws.txt +0 -0
  11. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/requirements-azure.txt +0 -0
  12. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/requirements-dev.txt +0 -0
  13. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/requirements-gcp.txt +0 -0
  14. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/requirements-generic.txt +0 -0
  15. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/requirements.txt +0 -0
  16. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/setup.cfg +0 -0
  17. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/setup.py +0 -0
  18. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/altcodepro_polydb_python.egg-info/SOURCES.txt +0 -0
  19. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/altcodepro_polydb_python.egg-info/dependency_links.txt +0 -0
  20. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/altcodepro_polydb_python.egg-info/requires.txt +0 -0
  21. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/altcodepro_polydb_python.egg-info/top_level.txt +0 -0
  22. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/PolyDB.py +0 -0
  23. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/AzureBlobStorageAdapter.py +0 -0
  24. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/AzureFileStorageAdapter.py +0 -0
  25. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/AzureQueueAdapter.py +0 -0
  26. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/AzureTableStorageAdapter.py +0 -0
  27. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/BlockchainBlobAdapter.py +0 -0
  28. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/BlockchainFileAdapter.py +0 -0
  29. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/BlockchainKVAdapter.py +0 -0
  30. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/BlockchainQueueAdapter.py +0 -0
  31. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/DynamoDBAdapter.py +0 -0
  32. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/EFSAdapter.py +0 -0
  33. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/FirestoreAdapter.py +0 -0
  34. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/GCPFilestoreAdapter.py +0 -0
  35. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/GCPPubSubAdapter.py +0 -0
  36. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/GCPStorageAdapter.py +0 -0
  37. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/MongoDBAdapter.py +0 -0
  38. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/S3Adapter.py +0 -0
  39. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/S3CompatibleAdapter.py +0 -0
  40. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/SQSAdapter.py +0 -0
  41. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/VercelBlobAdapter.py +0 -0
  42. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/VercelFileAdapter.py +0 -0
  43. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/VercelKVAdapter.py +0 -0
  44. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/VercelQueueAdapter.py +0 -0
  45. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/adapters/__init__.py +0 -0
  46. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/advanced_query.py +0 -0
  47. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/audit/AuditStorage.py +0 -0
  48. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/audit/__init__.py +0 -0
  49. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/audit/context.py +0 -0
  50. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/audit/manager.py +0 -0
  51. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/audit/models.py +0 -0
  52. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/base/NoSQLKVAdapter.py +0 -0
  53. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/base/ObjectStorageAdapter.py +0 -0
  54. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/base/QueueAdapter.py +0 -0
  55. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/base/SharedFilesAdapter.py +0 -0
  56. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/base/__init__.py +0 -0
  57. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/batch.py +0 -0
  58. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/cache.py +0 -0
  59. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/cloudDatabaseFactory.py +0 -0
  60. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/databaseFactory.py +0 -0
  61. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/decorators.py +0 -0
  62. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/errors.py +0 -0
  63. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/json_safe.py +0 -0
  64. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/models.py +0 -0
  65. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/monitoring.py +0 -0
  66. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/multitenancy.py +0 -0
  67. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/py.typed +0 -0
  68. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/query.py +0 -0
  69. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/registry.py +0 -0
  70. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/schema.py +0 -0
  71. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/security.py +0 -0
  72. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/types.py +0 -0
  73. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/utils.py +0 -0
  74. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/src/polydb/validation.py +0 -0
  75. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/tests/test_aws.py +0 -0
  76. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/tests/test_azure.py +0 -0
  77. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/tests/test_blockchain.py +0 -0
  78. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/tests/test_cloud_factory.py +0 -0
  79. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/tests/test_gcp.py +0 -0
  80. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/tests/test_mongodb.py +0 -0
  81. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/tests/test_multi_engine.py +0 -0
  82. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/tests/test_postgresql.py +0 -0
  83. {altcodepro_polydb_python-2.3.18 → altcodepro_polydb_python-2.3.20}/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.18
3
+ Version: 2.3.20
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.18"
7
+ version = "2.3.20"
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.18
3
+ Version: 2.3.20
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
@@ -9,7 +9,6 @@ __version__ = "2.2.3"
9
9
  from .cloudDatabaseFactory import CloudDatabaseFactory
10
10
  from .databaseFactory import DatabaseFactory
11
11
  from .models import CloudProvider, PartitionConfig
12
- from .decorators import polydb_model
13
12
  from .query import QueryBuilder, Operator
14
13
  from .audit.context import AuditContext
15
14
  from .cache import RedisCacheEngine as CacheEngine
@@ -36,7 +35,6 @@ __all__ = [
36
35
  # Models & Config
37
36
  "CloudProvider",
38
37
  "PartitionConfig",
39
- "polydb_model",
40
38
  # Query
41
39
  "QueryBuilder",
42
40
  "Operator",
@@ -1,15 +1,15 @@
1
1
  # src/polydb/adapters/postgres.py
2
- import datetime
3
- from decimal import Decimal
4
2
  import os
5
3
  import threading
6
4
  from typing import Any, Iterator, List, Optional, Tuple, Union
7
5
  import hashlib
8
6
  from contextlib import contextmanager
9
7
  import json
8
+ from decimal import Decimal
10
9
  from datetime import datetime, date
11
10
 
12
11
  import psycopg2.extensions
12
+ from psycopg2.extras import Json
13
13
 
14
14
  from ..errors import DatabaseError, ConnectionError
15
15
  from ..retry import retry
@@ -158,7 +158,7 @@ class PostgreSQLAdapter:
158
158
  if self._pool:
159
159
  try:
160
160
  self._pool.closeall()
161
- except:
161
+ except Exception:
162
162
  pass
163
163
  self._pool = None
164
164
  self._initialize_pool()
@@ -194,8 +194,9 @@ class PostgreSQLAdapter:
194
194
  # ---------------------------------------------------------------------
195
195
  def _json_safe(self, obj: Any):
196
196
  """
197
- Ensure JSON serialization never fails.
198
- Used only for Json() wrapping.
197
+ Ensure JSON serialization never fails. Used only for Json() wrapping.
198
+ Recurses into dicts, lists, AND tuples so nested datetime/Decimal
199
+ values are made safe at any depth.
199
200
  """
200
201
  if isinstance(obj, datetime):
201
202
  return obj.isoformat()
@@ -205,66 +206,68 @@ class PostgreSQLAdapter:
205
206
  return str(obj)
206
207
  if isinstance(obj, dict):
207
208
  return {k: self._json_safe(v) for k, v in obj.items()}
208
- if isinstance(obj, list):
209
+ if isinstance(obj, (list, tuple)):
209
210
  return [self._json_safe(v) for v in obj]
210
211
  return obj
211
212
 
212
213
  def _serialize_value(self, v: Any) -> Any:
213
214
  """
214
- Make outgoing values safe for psycopg2 across mixed column types.
215
-
216
- Rules:
217
- None / empty list / empty dict -> None (becomes NULL on any column)
218
- list of primitives (str/int/...) -> native list (psycopg2 maps to TEXT[]/INT[])
219
- list containing dicts -> Json(list) (for JSONB columns)
220
- dict -> Json(dict)
221
- datetime/date -> native
222
- Decimal -> float
223
- everything else -> as-is
215
+ Serialize a value being WRITTEN to a column (insert / update SET /
216
+ upsert data values).
217
+
218
+ This platform provisions every ARRAY and OBJECT field as a JSONB
219
+ column (SchemaProvisioner._FIELD_TO_SQL_TYPE never emits a native
220
+ text[]/int[] column), so dicts AND lists/tuples are ALWAYS
221
+ JSON-encoded via psycopg2's Json adapter.
222
+
223
+ Json() handles empty {} and [] correctly ('{}'::jsonb / '[]'::jsonb),
224
+ handles nesting, and never produces a quoted string — which is what
225
+ broke the earlier native-list, json.dumps, and NULL-ify-empties
226
+ attempts. Empties are NOT turned into NULL: an empty JSONB array/
227
+ object is a valid, meaningful value.
224
228
  """
225
- # NULL-ify empties so they're valid for TEXT[], JSONB, and plain columns alike.
226
- from psycopg2.extras import Json
227
-
228
229
  if v is None:
229
230
  return None
230
- if isinstance(v, (list, tuple)) and len(v) == 0:
231
- return None
232
- if isinstance(v, dict) and len(v) == 0:
233
- return None
231
+ if isinstance(v, (dict, list, tuple)):
232
+ return Json(self._json_safe(v))
233
+ if isinstance(v, (datetime, date)):
234
+ return v
235
+ if isinstance(v, Decimal):
236
+ return float(v)
237
+ return v
234
238
 
235
- # Dict -> JSONB
239
+ def _serialize_param(self, v: Any) -> Any:
240
+ """
241
+ Serialize a value used as a QUERY PARAMETER (WHERE values, IN / ANY
242
+ lists, LIMIT/OFFSET, raw execute() params).
243
+
244
+ Unlike _serialize_value, primitive lists/tuples are kept NATIVE so
245
+ that ``IN %s`` / ``= ANY(%s)`` parameters expand correctly. A dict
246
+ (or a list that contains dicts) is JSON-encoded so it can be compared
247
+ against a JSONB column.
248
+ """
249
+ if v is None:
250
+ return None
236
251
  if isinstance(v, dict):
237
252
  return Json(self._json_safe(v))
238
-
239
- # List: route by element type.
240
253
  if isinstance(v, (list, tuple)):
241
- v = list(v)
242
- # If ANY element is a dict, treat as JSON payload (for JSONB columns).
243
- if any(isinstance(x, dict) for x in v):
244
- return Json(v)
245
- # If ALL elements are primitives, send as native list for TEXT[]/INT[].
246
- if all(isinstance(x, (str, int, float, bool, type(None))) for x in v):
247
- return v
248
- # Mixed / nested -> safest is JSONB
249
- return Json(v)
250
-
251
- # Datetime / date
254
+ seq = list(v)
255
+ if any(isinstance(x, dict) for x in seq):
256
+ return Json(self._json_safe(seq))
257
+ return seq # native -> psycopg2 expands for IN / ANY
252
258
  if isinstance(v, (datetime, date)):
253
259
  return v
254
-
255
- # Decimal
256
260
  if isinstance(v, Decimal):
257
261
  return float(v)
258
-
259
262
  return v
260
263
 
261
264
  def _serialize_params(self, params: List[Any]) -> List[Any]:
262
- return [self._serialize_value(p) for p in params]
265
+ return [self._serialize_param(p) for p in params]
263
266
 
264
267
  def _deserialize_row(self, row: JsonDict) -> JsonDict:
265
268
  """
266
- Postgres JSON/JSONB often comes back as dict already depending on driver config.
267
- If it comes as a string, try json.loads safely.
269
+ Postgres JSON/JSONB often comes back as dict/list already depending on
270
+ driver config. If it comes as a string, try json.loads safely.
268
271
  """
269
272
  for k, v in list(row.items()):
270
273
  if isinstance(v, str):
@@ -444,6 +447,7 @@ class PostgreSQLAdapter:
444
447
  cursor = conn.cursor()
445
448
 
446
449
  set_clause = ", ".join([f"{k} = %s" for k in data.keys()])
450
+ # SET values are written into columns -> write serializer (JSONB).
447
451
  params: List[Any] = [self._serialize_value(v) for v in data.values()]
448
452
 
449
453
  if isinstance(entity_id, dict):
@@ -454,7 +458,8 @@ class PostgreSQLAdapter:
454
458
  where_parts.append(f"{k} IS NULL")
455
459
  else:
456
460
  where_parts.append(f"{k} = %s")
457
- params.append(self._serialize_value(v))
461
+ # WHERE values are query params -> param serializer.
462
+ params.append(self._serialize_param(v))
458
463
  where_clause = " AND ".join(where_parts)
459
464
  else:
460
465
  where_clause = "id = %s"
@@ -580,7 +585,8 @@ class PostgreSQLAdapter:
580
585
  where_parts.append(f"{k} IS NULL")
581
586
  else:
582
587
  where_parts.append(f"{k} = %s")
583
- params.append(self._serialize_value(v))
588
+ # WHERE values are query params -> param serializer.
589
+ params.append(self._serialize_param(v))
584
590
  where_clause = " AND ".join(where_parts)
585
591
  else:
586
592
  where_clause = "id = %s"
@@ -3,18 +3,18 @@
3
3
  Retry logic with exponential backoff and metrics hooks
4
4
  """
5
5
 
6
- import functools
7
6
  import time
8
7
  import logging
9
8
  from functools import wraps
10
- from typing import Callable, Optional, Tuple, Type
9
+ from typing import Callable, Tuple, Type
10
+
11
11
  logger = logging.getLogger(__name__)
12
12
 
13
13
  _NON_RETRYABLE_MARKERS = (
14
- "23505", # Postgres unique_violation
15
- "23503", # Postgres foreign_key_violation
16
- "23502", # Postgres not_null_violation
17
- "23514", # Postgres check_violation
14
+ "23505", # Postgres unique_violation
15
+ "23503", # Postgres foreign_key_violation
16
+ "23502", # Postgres not_null_violation
17
+ "23514", # Postgres check_violation
18
18
  "duplicate key value violates",
19
19
  "unique constraint",
20
20
  "UniqueViolation",
@@ -87,8 +87,18 @@ def retry(
87
87
  MetricsHooks.on_query_end(func.__name__, duration, True)
88
88
  return result
89
89
  except exceptions as e:
90
- attempt += 1
91
90
  duration = time.time() - start_time
91
+
92
+ # Permanent errors (unique / FK / not-null / check
93
+ # violations, etc.) are never transient. Fail fast so the
94
+ # caller's insert->update fallthrough fires immediately
95
+ # instead of burning the full backoff window.
96
+ if _is_non_retryable(e):
97
+ MetricsHooks.on_query_end(func.__name__, duration, False)
98
+ MetricsHooks.on_error(func.__name__, e)
99
+ raise
100
+
101
+ attempt += 1
92
102
  MetricsHooks.on_query_end(func.__name__, duration, False)
93
103
  MetricsHooks.on_error(func.__name__, e)
94
104