altcodepro-polydb-python 2.1.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.
- altcodepro_polydb_python-2.1.0.dist-info/METADATA +378 -0
- altcodepro_polydb_python-2.1.0.dist-info/RECORD +51 -0
- altcodepro_polydb_python-2.1.0.dist-info/WHEEL +5 -0
- altcodepro_polydb_python-2.1.0.dist-info/licenses/LICENSE +21 -0
- altcodepro_polydb_python-2.1.0.dist-info/top_level.txt +1 -0
- polydb/__init__.py +64 -0
- polydb/adapters/AzureBlobStorageAdapter.py +77 -0
- polydb/adapters/AzureFileStorageAdapter.py +79 -0
- polydb/adapters/AzureQueueAdapter.py +61 -0
- polydb/adapters/AzureTableStorageAdapter.py +182 -0
- polydb/adapters/DynamoDBAdapter.py +216 -0
- polydb/adapters/EFSAdapter.py +50 -0
- polydb/adapters/FirestoreAdapter.py +193 -0
- polydb/adapters/GCPStorageAdapter.py +81 -0
- polydb/adapters/MongoDBAdapter.py +136 -0
- polydb/adapters/PostgreSQLAdapter.py +453 -0
- polydb/adapters/PubSubAdapter.py +83 -0
- polydb/adapters/S3Adapter.py +86 -0
- polydb/adapters/S3CompatibleAdapter.py +90 -0
- polydb/adapters/SQSAdapter.py +84 -0
- polydb/adapters/VercelKVAdapter.py +327 -0
- polydb/adapters/__init__.py +0 -0
- polydb/advanced_query.py +147 -0
- polydb/audit/AuditStorage.py +136 -0
- polydb/audit/__init__.py +7 -0
- polydb/audit/context.py +53 -0
- polydb/audit/manager.py +47 -0
- polydb/audit/models.py +86 -0
- polydb/base/NoSQLKVAdapter.py +301 -0
- polydb/base/ObjectStorageAdapter.py +42 -0
- polydb/base/QueueAdapter.py +27 -0
- polydb/base/SharedFilesAdapter.py +32 -0
- polydb/base/__init__.py +0 -0
- polydb/batch.py +163 -0
- polydb/cache.py +204 -0
- polydb/databaseFactory.py +748 -0
- polydb/decorators.py +21 -0
- polydb/errors.py +82 -0
- polydb/factory.py +107 -0
- polydb/models.py +39 -0
- polydb/monitoring.py +313 -0
- polydb/multitenancy.py +197 -0
- polydb/py.typed +0 -0
- polydb/query.py +150 -0
- polydb/registry.py +71 -0
- polydb/retry.py +76 -0
- polydb/schema.py +205 -0
- polydb/security.py +458 -0
- polydb/types.py +127 -0
- polydb/utils.py +61 -0
- polydb/validation.py +131 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: altcodepro-polydb-python
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety
|
|
5
|
+
Author: AltCodePro
|
|
6
|
+
Project-URL: Homepage, https://github.com/altcodepro/polydb-python
|
|
7
|
+
Project-URL: Documentation, https://github.com/altcodepro/polydb-python#readme
|
|
8
|
+
Project-URL: Repository, https://github.com/altcodepro/polydb-python
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/altcodepro/polydb-python/issues
|
|
10
|
+
Keywords: database,cloud,multi-cloud,aws,azure,gcp,abstraction,nosql,sql,postgres,mongodb,dynamodb,s3
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Database
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Operating System :: OS Independent
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: psycopg2-binary>=2.9.11
|
|
27
|
+
Requires-Dist: tenacity>=9.1.4
|
|
28
|
+
Provides-Extra: aws
|
|
29
|
+
Requires-Dist: boto3>=1.42.47; extra == "aws"
|
|
30
|
+
Requires-Dist: botocore>=1.42.47; extra == "aws"
|
|
31
|
+
Provides-Extra: azure
|
|
32
|
+
Requires-Dist: azure-core>=1.38.1; extra == "azure"
|
|
33
|
+
Requires-Dist: azure-data-tables>=12.7.0; extra == "azure"
|
|
34
|
+
Requires-Dist: azure-storage-blob>=12.28.0; extra == "azure"
|
|
35
|
+
Requires-Dist: azure-storage-file-share>=12.24.0; extra == "azure"
|
|
36
|
+
Requires-Dist: azure-storage-queue>=12.15.0; extra == "azure"
|
|
37
|
+
Provides-Extra: gcp
|
|
38
|
+
Requires-Dist: google-api-core>=2.29.0; extra == "gcp"
|
|
39
|
+
Requires-Dist: google-auth>=2.48.0; extra == "gcp"
|
|
40
|
+
Requires-Dist: google-cloud-core>=2.5.0; extra == "gcp"
|
|
41
|
+
Requires-Dist: google-cloud-firestore>=2.23.0; extra == "gcp"
|
|
42
|
+
Requires-Dist: google-cloud-pubsub>=2.35.0; extra == "gcp"
|
|
43
|
+
Requires-Dist: google-cloud-storage>=3.9.0; extra == "gcp"
|
|
44
|
+
Provides-Extra: mongodb
|
|
45
|
+
Requires-Dist: pymongo>=4.16.0; extra == "mongodb"
|
|
46
|
+
Provides-Extra: rabbitmq
|
|
47
|
+
Requires-Dist: pika>=1.3.2; extra == "rabbitmq"
|
|
48
|
+
Provides-Extra: vercel
|
|
49
|
+
Requires-Dist: requests>=2.32.5; extra == "vercel"
|
|
50
|
+
Provides-Extra: generic
|
|
51
|
+
Requires-Dist: pymongo>=4.16.0; extra == "generic"
|
|
52
|
+
Requires-Dist: pika>=1.3.2; extra == "generic"
|
|
53
|
+
Requires-Dist: boto3>=1.42.47; extra == "generic"
|
|
54
|
+
Provides-Extra: all
|
|
55
|
+
Requires-Dist: boto3>=1.42.47; extra == "all"
|
|
56
|
+
Requires-Dist: botocore>=1.42.47; extra == "all"
|
|
57
|
+
Requires-Dist: azure-core>=1.38.1; extra == "all"
|
|
58
|
+
Requires-Dist: azure-data-tables>=12.7.0; extra == "all"
|
|
59
|
+
Requires-Dist: azure-storage-blob>=12.28.0; extra == "all"
|
|
60
|
+
Requires-Dist: azure-storage-file-share>=12.24.0; extra == "all"
|
|
61
|
+
Requires-Dist: azure-storage-queue>=12.15.0; extra == "all"
|
|
62
|
+
Requires-Dist: google-cloud-firestore>=2.23.0; extra == "all"
|
|
63
|
+
Requires-Dist: google-cloud-pubsub>=2.35.0; extra == "all"
|
|
64
|
+
Requires-Dist: google-cloud-storage>=3.9.0; extra == "all"
|
|
65
|
+
Requires-Dist: pymongo>=4.16.0; extra == "all"
|
|
66
|
+
Requires-Dist: pika>=1.3.2; extra == "all"
|
|
67
|
+
Requires-Dist: requests>=2.32.5; extra == "all"
|
|
68
|
+
Requires-Dist: redis>=6.4.0; extra == "all"
|
|
69
|
+
Provides-Extra: dev
|
|
70
|
+
Requires-Dist: black>=26.1.0; extra == "dev"
|
|
71
|
+
Requires-Dist: flake8>=7.3.0; extra == "dev"
|
|
72
|
+
Requires-Dist: isort>=7.0.0; extra == "dev"
|
|
73
|
+
Requires-Dist: mypy>=1.19.1; extra == "dev"
|
|
74
|
+
Provides-Extra: test
|
|
75
|
+
Requires-Dist: pytest>=9.0.2; extra == "test"
|
|
76
|
+
Requires-Dist: pytest-cov>=7.0.0; extra == "test"
|
|
77
|
+
Requires-Dist: pytest-mock>=3.15.1; extra == "test"
|
|
78
|
+
Requires-Dist: moto>=5.1.21; extra == "test"
|
|
79
|
+
Dynamic: license-file
|
|
80
|
+
|
|
81
|
+
# PolyDB v3.0 - Enterprise Database Abstraction Layer
|
|
82
|
+
|
|
83
|
+
**Production-ready, cloud-independent database abstraction with full LINQ support, field-level audit, cache, and overflow storage**
|
|
84
|
+
|
|
85
|
+
## Features
|
|
86
|
+
|
|
87
|
+
✅ **LINQ-Style Queries** - All SQL clauses: WHERE, ORDER BY, SELECT, GROUP BY, DISTINCT, COUNT
|
|
88
|
+
✅ **Multi-Cloud** - Azure, AWS, GCP, Vercel, MongoDB, PostgreSQL
|
|
89
|
+
✅ **Automatic Overflow** - Large NoSQL records → Object Storage (transparent)
|
|
90
|
+
✅ **Enterprise Audit** - Cryptographic hash chain, field-level changes, strict ordering
|
|
91
|
+
✅ **Cache Engine** - In-memory with TTL, automatic invalidation
|
|
92
|
+
✅ **Soft Delete** - Optional logical deletion with audit trail
|
|
93
|
+
✅ **Auto-Inject** - Tenant ID, audit fields (created_at, updated_by, etc.)
|
|
94
|
+
✅ **Thread-Safe** - Connection pooling, distributed-safe hash chaining
|
|
95
|
+
✅ **Retry Logic** - Exponential backoff, configurable
|
|
96
|
+
✅ **Type-Safe** - Protocol-based adapters, full type hints
|
|
97
|
+
|
|
98
|
+
## Quick Start
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from polydb import DatabaseFactory, polydb_model, QueryBuilder, Operator, AuditContext
|
|
102
|
+
|
|
103
|
+
# 1. Define model
|
|
104
|
+
@polydb_model
|
|
105
|
+
class User:
|
|
106
|
+
__polydb__ = {
|
|
107
|
+
"storage": "sql",
|
|
108
|
+
"table": "users",
|
|
109
|
+
"cache": True,
|
|
110
|
+
"cache_ttl": 600,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# 2. Initialize
|
|
114
|
+
db = DatabaseFactory(
|
|
115
|
+
enable_audit=True,
|
|
116
|
+
enable_cache=True,
|
|
117
|
+
soft_delete=True,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# 3. Set context (per-request)
|
|
121
|
+
AuditContext.set(
|
|
122
|
+
actor_id="user_123",
|
|
123
|
+
roles=["admin"],
|
|
124
|
+
tenant_id="tenant_abc",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# 4. CRUD with auto-audit
|
|
128
|
+
user = db.create(User, {"name": "John", "email": "john@example.com"})
|
|
129
|
+
# Auto-injects: tenant_id, created_at, created_by, updated_at, updated_by
|
|
130
|
+
|
|
131
|
+
# 5. LINQ queries
|
|
132
|
+
query = (
|
|
133
|
+
QueryBuilder()
|
|
134
|
+
.where("role", Operator.EQ, "admin")
|
|
135
|
+
.where("age", Operator.GTE, 18)
|
|
136
|
+
.order_by("created_at", descending=True)
|
|
137
|
+
.skip(10)
|
|
138
|
+
.take(20)
|
|
139
|
+
.select("id", "name", "email")
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
admins = db.query_linq(User, query)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## LINQ Operations
|
|
146
|
+
|
|
147
|
+
### Filters
|
|
148
|
+
```python
|
|
149
|
+
builder.where("field", Operator.EQ, value) # ==
|
|
150
|
+
builder.where("field", Operator.NE, value) # !=
|
|
151
|
+
builder.where("field", Operator.GT, value) # >
|
|
152
|
+
builder.where("field", Operator.GTE, value) # >=
|
|
153
|
+
builder.where("field", Operator.LT, value) # <
|
|
154
|
+
builder.where("field", Operator.LTE, value) # <=
|
|
155
|
+
builder.where("field", Operator.IN, [1,2,3]) # IN
|
|
156
|
+
builder.where("field", Operator.CONTAINS, "text") # LIKE
|
|
157
|
+
builder.where("field", Operator.STARTS_WITH, "prefix")
|
|
158
|
+
builder.where("field", Operator.ENDS_WITH, "suffix")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Ordering & Pagination
|
|
162
|
+
```python
|
|
163
|
+
builder.order_by("field", descending=True)
|
|
164
|
+
builder.skip(10)
|
|
165
|
+
builder.take(20)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Projection
|
|
169
|
+
```python
|
|
170
|
+
builder.select("id", "name", "email")
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Aggregation
|
|
174
|
+
```python
|
|
175
|
+
builder.count()
|
|
176
|
+
builder.distinct_on()
|
|
177
|
+
builder.group_by("role", "department")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## NoSQL Overflow Storage
|
|
181
|
+
|
|
182
|
+
Records >1MB automatically stored in object storage:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
@polydb_model
|
|
186
|
+
class Product:
|
|
187
|
+
__polydb__ = {
|
|
188
|
+
"storage": "nosql",
|
|
189
|
+
"collection": "products",
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# Large data automatically overflows
|
|
193
|
+
product = db.create(Product, {
|
|
194
|
+
"data": {"huge": "payload"} * 100000
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
# Transparent retrieval
|
|
198
|
+
retrieved = db.read_one(Product, {"id": product["id"]})
|
|
199
|
+
# User never knows it came from blob storage
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Enterprise Audit Trail
|
|
203
|
+
|
|
204
|
+
### Automatic Tracking
|
|
205
|
+
- **Who**: actor_id, roles
|
|
206
|
+
- **What**: action, model, entity_id, changed_fields
|
|
207
|
+
- **When**: timestamp (microsecond precision)
|
|
208
|
+
- **Where**: tenant_id, ip_address, user_agent
|
|
209
|
+
- **Context**: trace_id, request_id
|
|
210
|
+
- **Integrity**: cryptographic hash chain
|
|
211
|
+
|
|
212
|
+
### Field-Level Changes
|
|
213
|
+
```python
|
|
214
|
+
db.update(User, user_id, {"email": "new@example.com"})
|
|
215
|
+
# Audit log shows: changed_fields = ["email", "updated_at", "updated_by"]
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Verify Chain
|
|
219
|
+
```python
|
|
220
|
+
from polydb.audit.storage import AuditStorage
|
|
221
|
+
|
|
222
|
+
audit = AuditStorage()
|
|
223
|
+
is_valid = audit.verify_chain(tenant_id="tenant_abc")
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Cache Engine
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
# Auto-cache from model metadata
|
|
230
|
+
@polydb_model
|
|
231
|
+
class User:
|
|
232
|
+
__polydb__ = {
|
|
233
|
+
"storage": "sql",
|
|
234
|
+
"table": "users",
|
|
235
|
+
"cache": True,
|
|
236
|
+
"cache_ttl": 600, # 10 minutes
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
# Cached read
|
|
240
|
+
users = db.read(User, {"role": "admin"})
|
|
241
|
+
|
|
242
|
+
# Bypass cache
|
|
243
|
+
users = db.read(User, {"role": "admin"}, no_cache=True)
|
|
244
|
+
|
|
245
|
+
# Manual invalidation
|
|
246
|
+
from polydb.cache import CacheEngine
|
|
247
|
+
cache = CacheEngine()
|
|
248
|
+
cache.invalidate("User")
|
|
249
|
+
cache.clear()
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Soft Delete
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
db = DatabaseFactory(soft_delete=True)
|
|
256
|
+
|
|
257
|
+
# Soft delete (sets deleted_at, deleted_by)
|
|
258
|
+
db.delete(User, user_id)
|
|
259
|
+
|
|
260
|
+
# Hard delete
|
|
261
|
+
db.delete(User, user_id, hard=True)
|
|
262
|
+
|
|
263
|
+
# Include deleted in queries
|
|
264
|
+
all_users = db.read(User, {}, include_deleted=True)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Pagination
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
page1, next_token = db.read_page(User, {"role": "admin"}, page_size=50)
|
|
271
|
+
page2, token2 = db.read_page(User, {"role": "admin"}, page_size=50, continuation_token=next_token)
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Multi-Tenant
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
# Auto-inject tenant_id from context
|
|
278
|
+
AuditContext.set(tenant_id="tenant_123", actor_id="user_456", roles=["admin"])
|
|
279
|
+
|
|
280
|
+
user = db.create(User, {"name": "John"})
|
|
281
|
+
# Result: {"name": "John", "tenant_id": "tenant_123", "created_by": "user_456", ...}
|
|
282
|
+
|
|
283
|
+
# Filter by tenant (automatic)
|
|
284
|
+
users = db.read(User, {}) # Only returns tenant_123 records
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Environment Variables
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# Provider selection (optional, auto-detected)
|
|
291
|
+
CLOUD_PROVIDER=aws|azure|gcp|vercel|mongodb|postgresql
|
|
292
|
+
|
|
293
|
+
# PostgreSQL
|
|
294
|
+
POSTGRES_CONNECTION_STRING=postgresql://user:pass@host:5432/db
|
|
295
|
+
POSTGRES_MIN_CONNECTIONS=2
|
|
296
|
+
POSTGRES_MAX_CONNECTIONS=20
|
|
297
|
+
|
|
298
|
+
# AWS
|
|
299
|
+
AWS_ACCESS_KEY_ID=...
|
|
300
|
+
AWS_SECRET_ACCESS_KEY=...
|
|
301
|
+
DYNAMODB_TABLE_NAME=...
|
|
302
|
+
S3_BUCKET_NAME=...
|
|
303
|
+
|
|
304
|
+
# Azure
|
|
305
|
+
AZURE_STORAGE_CONNECTION_STRING=...
|
|
306
|
+
AZURE_TABLE_NAME=...
|
|
307
|
+
AZURE_CONTAINER_NAME=...
|
|
308
|
+
|
|
309
|
+
# GCP
|
|
310
|
+
GOOGLE_CLOUD_PROJECT=...
|
|
311
|
+
FIRESTORE_COLLECTION=...
|
|
312
|
+
GCS_BUCKET_NAME=...
|
|
313
|
+
|
|
314
|
+
# MongoDB
|
|
315
|
+
MONGODB_URI=mongodb://...
|
|
316
|
+
MONGODB_DATABASE=...
|
|
317
|
+
MONGODB_COLLECTION=...
|
|
318
|
+
|
|
319
|
+
# Vercel
|
|
320
|
+
KV_URL=...
|
|
321
|
+
KV_REST_API_TOKEN=...
|
|
322
|
+
BLOB_READ_WRITE_TOKEN=...
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Model Metadata
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
@polydb_model
|
|
329
|
+
class Entity:
|
|
330
|
+
__polydb__ = {
|
|
331
|
+
# Required
|
|
332
|
+
"storage": "sql" | "nosql",
|
|
333
|
+
|
|
334
|
+
# SQL
|
|
335
|
+
"table": "table_name",
|
|
336
|
+
|
|
337
|
+
# NoSQL
|
|
338
|
+
"collection": "collection_name",
|
|
339
|
+
"pk_field": "partition_key_field",
|
|
340
|
+
"rk_field": "row_key_field",
|
|
341
|
+
|
|
342
|
+
# Cache
|
|
343
|
+
"cache": True,
|
|
344
|
+
"cache_ttl": 300, # seconds
|
|
345
|
+
|
|
346
|
+
# Optional
|
|
347
|
+
"provider": "aws", # Override auto-detection
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Thread Safety
|
|
352
|
+
|
|
353
|
+
- Connection pooling with locks
|
|
354
|
+
- Distributed-safe audit hash chaining
|
|
355
|
+
- Cache with thread-safe operations
|
|
356
|
+
- Retry logic with exponential backoff
|
|
357
|
+
|
|
358
|
+
## Performance
|
|
359
|
+
|
|
360
|
+
- **SQL**: Connection pooling (min=2, max=20)
|
|
361
|
+
- **NoSQL**: Client reuse, overflow storage
|
|
362
|
+
- **Cache**: In-memory with automatic invalidation
|
|
363
|
+
- **Retry**: Configurable backoff (0.5s-6s)
|
|
364
|
+
|
|
365
|
+
## Production Checklist
|
|
366
|
+
|
|
367
|
+
✅ Set `CLOUD_PROVIDER` explicitly
|
|
368
|
+
✅ Configure connection pool sizes
|
|
369
|
+
✅ Enable audit (`enable_audit=True`)
|
|
370
|
+
✅ Set cache TTL per model
|
|
371
|
+
✅ Use soft delete for compliance
|
|
372
|
+
✅ Set audit context per request
|
|
373
|
+
✅ Monitor audit chain integrity
|
|
374
|
+
✅ Configure retry attempts
|
|
375
|
+
|
|
376
|
+
## License
|
|
377
|
+
|
|
378
|
+
MIT
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
altcodepro_polydb_python-2.1.0.dist-info/licenses/LICENSE,sha256=9X8GLocsBwy-5aR5JGOt2SAMDDPs9Qv-YnqmHBHOXrw,1067
|
|
2
|
+
polydb/__init__.py,sha256=V_xW5w910ofGc0jMFTbek-d8KcszZMtMYaX2p48f9Qc,1440
|
|
3
|
+
polydb/advanced_query.py,sha256=cxMB-EB-qT3bWXJlhmjnMCUtrzogORWyoEfS50Dy7go,4280
|
|
4
|
+
polydb/batch.py,sha256=_DjWZa1ZXYSk6MLKqFe0eT7SYVRZtYNqZb9bI8Y2sao,4566
|
|
5
|
+
polydb/cache.py,sha256=hCv_oMJvymcMDIFxpwGhGR3e5V1eYNFdLHswMViOGzc,6221
|
|
6
|
+
polydb/databaseFactory.py,sha256=4XI6VhXBBc6YgQlBr-OoBi5gSmuQ0iliYB64leUxT4U,25430
|
|
7
|
+
polydb/decorators.py,sha256=Rzk8Bj8wHi8YFtc06HEYT5r_Vqqn7TGaCtR5qvHdY-E,420
|
|
8
|
+
polydb/errors.py,sha256=rcFeBH0cenjJ86v0cmDc2Yjj4R019pLCBcTeSC4qps4,1428
|
|
9
|
+
polydb/factory.py,sha256=qCZIK9X6s69DCwWWQbCST5MO411sb6hRa4rQQ59WJ70,4556
|
|
10
|
+
polydb/models.py,sha256=HZYKB67ayoS1D4qxYfNLrUplbk7W-SIpexUnz4foyuQ,923
|
|
11
|
+
polydb/monitoring.py,sha256=pwR2p-sSlt6nA29lCAJFmdT0ODIyVQ9gSxB51hgaAbQ,10137
|
|
12
|
+
polydb/multitenancy.py,sha256=JTazyHLRq8ZcrnDGFy7VnIiDnTX3wrer1rZJsC949zE,6376
|
|
13
|
+
polydb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
polydb/query.py,sha256=qPisSV0i_GcgUz_DRKQPZy1TZ_6sEbbwQGhfh5BGWqQ,5097
|
|
15
|
+
polydb/registry.py,sha256=g-jLKq5jzDvdZ24weAqZHSxUIUaBsu1TZghxRMqbUXQ,1926
|
|
16
|
+
polydb/retry.py,sha256=etsj8MGo1WMvlcZMzWmFELAsWCRs-XPEuJe6K76QgbM,2548
|
|
17
|
+
polydb/schema.py,sha256=VrOayX6V6AD2Qh3-lm4ZVPTpI24e4V52IYheZf2rNQ4,5812
|
|
18
|
+
polydb/security.py,sha256=7qCl-xAr3pSTFgLZSCUK0h0rWfjSQna2MXQ0wjqt3rk,16229
|
|
19
|
+
polydb/types.py,sha256=XB_85Un8_aWt4dSfpjIGotHbK3KBY2WurQGXr9EOxWY,2992
|
|
20
|
+
polydb/utils.py,sha256=G_ki5zKr5rGPgpFQM1CTq6twQd5OytaHKfet267MftM,1662
|
|
21
|
+
polydb/validation.py,sha256=a1o1d02k3c6PWQwkBbw_0nEmIgrdB5RR8OcpNQMn4cA,4810
|
|
22
|
+
polydb/adapters/AzureBlobStorageAdapter.py,sha256=knNpS-O-Kd7pDjMaP53sl7Tud_FUm5K7ZhdpVOm5Vmc,3124
|
|
23
|
+
polydb/adapters/AzureFileStorageAdapter.py,sha256=EM6fzOLr7I8P4s0LgCcNrd3MuEkvOe2KeuwGHwrryW8,3168
|
|
24
|
+
polydb/adapters/AzureQueueAdapter.py,sha256=qiT-qNVfsvc4badEoBu6qiIKb_ctKWooIyXs0HI_qjk,2434
|
|
25
|
+
polydb/adapters/AzureTableStorageAdapter.py,sha256=EtfalUoM8nVGCWM1_-UMt0bOzNtkz32lL_BapJ6nM9U,7774
|
|
26
|
+
polydb/adapters/DynamoDBAdapter.py,sha256=WbVlnb8D03AphzCwEqBUs9pknSHiDCf34BeXcqKxeak,8650
|
|
27
|
+
polydb/adapters/EFSAdapter.py,sha256=GFHXn2fjohXxVJaM4ptbisEs5bwiwkHeQ1Av_5ILiCA,1688
|
|
28
|
+
polydb/adapters/FirestoreAdapter.py,sha256=ITqE1rKGjYDOHeOUNICl7SLp7WFuUOxVFz1AlvcYWZY,7798
|
|
29
|
+
polydb/adapters/GCPStorageAdapter.py,sha256=x_xcHE4w85hfJGfAqmc3Uu80KlXyCC0zssEfSOaMa6Y,2809
|
|
30
|
+
polydb/adapters/MongoDBAdapter.py,sha256=aP_S3pLgoBthUNkOviAmxP-tlBsE5HXPBn33neEqthQ,4872
|
|
31
|
+
polydb/adapters/PostgreSQLAdapter.py,sha256=olt_8_cVKZ-5nmpeUuP0569E-SSad6Us7SGKlQ96jUU,15016
|
|
32
|
+
polydb/adapters/PubSubAdapter.py,sha256=7_g9TlGaKTAfVIQO-LOeesuTCrYswJ0TmF3GK9rDmSE,3139
|
|
33
|
+
polydb/adapters/S3Adapter.py,sha256=iw4bETrG5sPspQtYDadmTXbqydCXn-VEsGqHrvbYn9Y,3011
|
|
34
|
+
polydb/adapters/S3CompatibleAdapter.py,sha256=3hiOVEqyGbRX1rnpeldea8aWljSlWgCN3iE-g_Bks0I,3513
|
|
35
|
+
polydb/adapters/SQSAdapter.py,sha256=rOoJ74j9cdxP4Ds30bxvKYNpXdgzfzT_8d6xFdohw-A,2895
|
|
36
|
+
polydb/adapters/VercelKVAdapter.py,sha256=stJ32PrBYEb4YE_V_NVla80gn_oPwFQrbxncmmFnrIA,12765
|
|
37
|
+
polydb/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
polydb/audit/AuditStorage.py,sha256=w22LXLR0nohn7A5orIHob_KNyUEir8UgpPVnnnVojLE,4936
|
|
39
|
+
polydb/audit/__init__.py,sha256=m_GE7gjLw00zfHX-1SpkF7QZpRE72HO699ZzKzqD3kc,244
|
|
40
|
+
polydb/audit/context.py,sha256=-A1FMtmr-2snVAHpTrVT80u-D_MCaqX6AoV4Ku2bz_o,1955
|
|
41
|
+
polydb/audit/manager.py,sha256=KzaaOf5bDfr4M-CkCAZBG_U_4xIBCKDLRAf3hsm-DAk,1236
|
|
42
|
+
polydb/audit/models.py,sha256=wm8XozEtEpXKrBjLc932QaLkqdn4hriUxrT6YBQ-5RU,2203
|
|
43
|
+
polydb/base/NoSQLKVAdapter.py,sha256=yILpI0jsKWju1MqJX4mYfWbnlWe-Wz-GFl0ORDir-eg,11343
|
|
44
|
+
polydb/base/ObjectStorageAdapter.py,sha256=97ZGsOFOG3epS2ecF5GRtfx_opxcizNtKOduEc0aNxU,1216
|
|
45
|
+
polydb/base/QueueAdapter.py,sha256=dVBlleJK0gNc0YliTVvvc7KO2VReQ7Ct1_Prdd2XP0s,746
|
|
46
|
+
polydb/base/SharedFilesAdapter.py,sha256=BaDPXDTePvsMYEnKoZMi01tlnI_Ww0tOoHVupqle7vM,700
|
|
47
|
+
polydb/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
+
altcodepro_polydb_python-2.1.0.dist-info/METADATA,sha256=pJqaLZvRkZRf6X9iqCZB4nBMPXyXJCb-aZxw5cmfe8w,10834
|
|
49
|
+
altcodepro_polydb_python-2.1.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
50
|
+
altcodepro_polydb_python-2.1.0.dist-info/top_level.txt,sha256=WgLFWJoYjUhwvyPxJFl6jYLrVFuBJDX3OABf4ocwk_E,7
|
|
51
|
+
altcodepro_polydb_python-2.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 AltCodePro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
polydb
|
polydb/__init__.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# src/polydb/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
PolyDB - Enterprise Cloud-Independent Database Abstraction
|
|
4
|
+
Full LINQ support, field-level audit, cache, soft delete, overflow storage
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "3.0.0"
|
|
8
|
+
|
|
9
|
+
from .factory import CloudDatabaseFactory
|
|
10
|
+
from .databaseFactory import DatabaseFactory
|
|
11
|
+
from .models import CloudProvider, PartitionConfig
|
|
12
|
+
from .decorators import polydb_model
|
|
13
|
+
from .query import QueryBuilder, Operator
|
|
14
|
+
from .audit.context import AuditContext
|
|
15
|
+
from .cache import CacheEngine
|
|
16
|
+
from .errors import (
|
|
17
|
+
CloudDBError,
|
|
18
|
+
DatabaseError,
|
|
19
|
+
NoSQLError,
|
|
20
|
+
StorageError,
|
|
21
|
+
QueueError,
|
|
22
|
+
ConnectionError,
|
|
23
|
+
ValidationError,
|
|
24
|
+
PolyDBError,
|
|
25
|
+
ModelNotRegisteredError,
|
|
26
|
+
InvalidModelMetadataError,
|
|
27
|
+
UnsupportedStorageTypeError,
|
|
28
|
+
AdapterConfigurationError,
|
|
29
|
+
OperationNotSupportedError,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
# Factories
|
|
34
|
+
'CloudDatabaseFactory',
|
|
35
|
+
'DatabaseFactory',
|
|
36
|
+
|
|
37
|
+
# Models & Config
|
|
38
|
+
'CloudProvider',
|
|
39
|
+
'PartitionConfig',
|
|
40
|
+
'polydb_model',
|
|
41
|
+
|
|
42
|
+
# Query
|
|
43
|
+
'QueryBuilder',
|
|
44
|
+
'Operator',
|
|
45
|
+
|
|
46
|
+
# Audit & Cache
|
|
47
|
+
'AuditContext',
|
|
48
|
+
'CacheEngine',
|
|
49
|
+
|
|
50
|
+
# Errors
|
|
51
|
+
'CloudDBError',
|
|
52
|
+
'DatabaseError',
|
|
53
|
+
'NoSQLError',
|
|
54
|
+
'StorageError',
|
|
55
|
+
'QueueError',
|
|
56
|
+
'ConnectionError',
|
|
57
|
+
'ValidationError',
|
|
58
|
+
'PolyDBError',
|
|
59
|
+
'ModelNotRegisteredError',
|
|
60
|
+
'InvalidModelMetadataError',
|
|
61
|
+
'UnsupportedStorageTypeError',
|
|
62
|
+
'AdapterConfigurationError',
|
|
63
|
+
'OperationNotSupportedError',
|
|
64
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# src/polydb/adapters/AzureBlobStorageAdapter.py
|
|
2
|
+
|
|
3
|
+
from polydb.base.ObjectStorageAdapter import ObjectStorageAdapter
|
|
4
|
+
from polydb.errors import ConnectionError, StorageError
|
|
5
|
+
from polydb.retry import retry
|
|
6
|
+
import os
|
|
7
|
+
import threading
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
class AzureBlobStorageAdapter(ObjectStorageAdapter):
|
|
11
|
+
"""Azure Blob Storage with client reuse"""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING") or ""
|
|
16
|
+
self.container_name = os.getenv("AZURE_CONTAINER_NAME", "default") or ""
|
|
17
|
+
self._client = None
|
|
18
|
+
self._lock = threading.Lock()
|
|
19
|
+
self._initialize_client()
|
|
20
|
+
|
|
21
|
+
def _initialize_client(self):
|
|
22
|
+
"""Initialize Azure Blob Storage client once"""
|
|
23
|
+
try:
|
|
24
|
+
from azure.storage.blob import BlobServiceClient
|
|
25
|
+
|
|
26
|
+
with self._lock:
|
|
27
|
+
if not self._client:
|
|
28
|
+
self._client = BlobServiceClient.from_connection_string(self.connection_string)
|
|
29
|
+
self.logger.info("Initialized Azure Blob Storage client")
|
|
30
|
+
except Exception as e:
|
|
31
|
+
raise ConnectionError(f"Failed to initialize Azure Blob Storage: {str(e)}")
|
|
32
|
+
|
|
33
|
+
@retry(max_attempts=3, delay=1.0, exceptions=(StorageError,))
|
|
34
|
+
def _put_raw(self, key: str, data: bytes) -> str:
|
|
35
|
+
"""Store blob"""
|
|
36
|
+
try:
|
|
37
|
+
if self._client:
|
|
38
|
+
blob_client = self._client.get_blob_client(self.container_name, key)
|
|
39
|
+
blob_client.upload_blob(data, overwrite=True)
|
|
40
|
+
self.logger.debug(f"Uploaded blob: {key}")
|
|
41
|
+
return key
|
|
42
|
+
except Exception as e:
|
|
43
|
+
raise StorageError(f"Azure Blob put failed: {str(e)}")
|
|
44
|
+
|
|
45
|
+
@retry(max_attempts=3, delay=1.0, exceptions=(StorageError,))
|
|
46
|
+
def get(self, key: str) -> bytes | None:
|
|
47
|
+
"""Get blob"""
|
|
48
|
+
try:
|
|
49
|
+
if self._client:
|
|
50
|
+
blob_client = self._client.get_blob_client(self.container_name, key)
|
|
51
|
+
return blob_client.download_blob().readall()
|
|
52
|
+
return None
|
|
53
|
+
except Exception as e:
|
|
54
|
+
raise StorageError(f"Azure Blob get failed: {str(e)}")
|
|
55
|
+
|
|
56
|
+
@retry(max_attempts=3, delay=1.0, exceptions=(StorageError,))
|
|
57
|
+
def delete(self, key: str) -> bool:
|
|
58
|
+
"""Delete blob"""
|
|
59
|
+
try:
|
|
60
|
+
if self._client:
|
|
61
|
+
blob_client = self._client.get_blob_client(self.container_name, key)
|
|
62
|
+
blob_client.delete_blob()
|
|
63
|
+
return True
|
|
64
|
+
return False
|
|
65
|
+
except Exception as e:
|
|
66
|
+
raise StorageError(f"Azure Blob delete failed: {str(e)}")
|
|
67
|
+
|
|
68
|
+
@retry(max_attempts=3, delay=1.0, exceptions=(StorageError,))
|
|
69
|
+
def list(self, prefix: str = "") -> List[str]:
|
|
70
|
+
"""List blobs with prefix"""
|
|
71
|
+
try:
|
|
72
|
+
if self._client:
|
|
73
|
+
container_client = self._client.get_container_client(self.container_name)
|
|
74
|
+
return [blob.name for blob in container_client.list_blobs(name_starts_with=prefix)]
|
|
75
|
+
return []
|
|
76
|
+
except Exception as e:
|
|
77
|
+
raise StorageError(f"Azure Blob list failed: {str(e)}")
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# src/polydb/adapters/AzureFileStorageAdapter.py
|
|
2
|
+
|
|
3
|
+
from polydb.base.SharedFilesAdapter import SharedFilesAdapter
|
|
4
|
+
from polydb.errors import ConnectionError, StorageError
|
|
5
|
+
from polydb.retry import retry
|
|
6
|
+
import os
|
|
7
|
+
import threading
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
class AzureFileStorageAdapter(SharedFilesAdapter):
|
|
11
|
+
"""Azure File Storage with client reuse"""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING") or ""
|
|
16
|
+
self.share_name = os.getenv("AZURE_SHARE_NAME", "default") or ""
|
|
17
|
+
self._client = None
|
|
18
|
+
self._lock = threading.Lock()
|
|
19
|
+
self._initialize_client()
|
|
20
|
+
|
|
21
|
+
def _initialize_client(self):
|
|
22
|
+
"""Initialize Azure File Storage client once"""
|
|
23
|
+
try:
|
|
24
|
+
from azure.storage.fileshare import ShareServiceClient
|
|
25
|
+
|
|
26
|
+
with self._lock:
|
|
27
|
+
if not self._client:
|
|
28
|
+
self._client = ShareServiceClient.from_connection_string(self.connection_string)
|
|
29
|
+
self.logger.info("Initialized Azure File Storage client")
|
|
30
|
+
except Exception as e:
|
|
31
|
+
raise ConnectionError(f"Failed to initialize Azure File Storage: {str(e)}")
|
|
32
|
+
|
|
33
|
+
@retry(max_attempts=3, delay=1.0, exceptions=(StorageError,))
|
|
34
|
+
def write(self, path: str, data: bytes) -> bool:
|
|
35
|
+
"""Write file"""
|
|
36
|
+
try:
|
|
37
|
+
if self._client:
|
|
38
|
+
file_client = self._client.get_share_client(self.share_name).get_file_client(path)
|
|
39
|
+
file_client.upload_file(data)
|
|
40
|
+
return True
|
|
41
|
+
return False
|
|
42
|
+
except Exception as e:
|
|
43
|
+
raise StorageError(f"Azure File write failed: {str(e)}")
|
|
44
|
+
|
|
45
|
+
@retry(max_attempts=3, delay=1.0, exceptions=(StorageError,))
|
|
46
|
+
def read(self, path: str) -> bytes | None:
|
|
47
|
+
"""Read file"""
|
|
48
|
+
try:
|
|
49
|
+
if self._client:
|
|
50
|
+
file_client = self._client.get_share_client(self.share_name).get_file_client(path)
|
|
51
|
+
return file_client.download_file().readall()
|
|
52
|
+
return None
|
|
53
|
+
except Exception as e:
|
|
54
|
+
raise StorageError(f"Azure File read failed: {str(e)}")
|
|
55
|
+
|
|
56
|
+
@retry(max_attempts=3, delay=1.0, exceptions=(StorageError,))
|
|
57
|
+
def delete(self, path: str) -> bool:
|
|
58
|
+
"""Delete file"""
|
|
59
|
+
try:
|
|
60
|
+
if self._client:
|
|
61
|
+
file_client = self._client.get_share_client(self.share_name).get_file_client(path)
|
|
62
|
+
file_client.delete_file()
|
|
63
|
+
return True
|
|
64
|
+
return False
|
|
65
|
+
except Exception as e:
|
|
66
|
+
raise StorageError(f"Azure File delete failed: {str(e)}")
|
|
67
|
+
|
|
68
|
+
@retry(max_attempts=3, delay=1.0, exceptions=(StorageError,))
|
|
69
|
+
def list(self, directory: str = "/") -> List[str]:
|
|
70
|
+
"""List files in directory"""
|
|
71
|
+
try:
|
|
72
|
+
if self._client:
|
|
73
|
+
dir_client = self._client.get_share_client(self.share_name).get_directory_client(
|
|
74
|
+
directory
|
|
75
|
+
)
|
|
76
|
+
return [item.name for item in dir_client.list_directories_and_files()]
|
|
77
|
+
return []
|
|
78
|
+
except Exception as e:
|
|
79
|
+
raise StorageError(f"Azure File list failed: {str(e)}")
|