esuls 0.1.14__tar.gz → 0.1.15__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.
- {esuls-0.1.14/src/esuls.egg-info → esuls-0.1.15}/PKG-INFO +2 -3
- {esuls-0.1.14 → esuls-0.1.15}/README.md +1 -2
- {esuls-0.1.14 → esuls-0.1.15}/pyproject.toml +1 -1
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls/db_cli.py +60 -38
- {esuls-0.1.14 → esuls-0.1.15/src/esuls.egg-info}/PKG-INFO +2 -3
- {esuls-0.1.14 → esuls-0.1.15}/LICENSE +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/setup.cfg +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls/__init__.py +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls/download_icon.py +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls/request_cli.py +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls/tests/test_db_concurrent.py +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls/utils.py +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls.egg-info/SOURCES.txt +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls.egg-info/dependency_links.txt +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls.egg-info/requires.txt +0 -0
- {esuls-0.1.14 → esuls-0.1.15}/src/esuls.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esuls
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.15
|
|
4
4
|
Summary: Utility library for async database operations, HTTP requests, and parallel execution
|
|
5
5
|
Author-email: IperGiove <ipergiove@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -228,8 +228,7 @@ pip install -e .
|
|
|
228
228
|
|
|
229
229
|
```bash
|
|
230
230
|
# With uv
|
|
231
|
-
uv build
|
|
232
|
-
twine upload dist/*
|
|
231
|
+
uv build && twine upload dist/*
|
|
233
232
|
|
|
234
233
|
# Or with traditional tools
|
|
235
234
|
pip install build twine
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "esuls"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.15"
|
|
8
8
|
description = "Utility library for async database operations, HTTP requests, and parallel execution"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.14"
|
|
@@ -296,40 +296,51 @@ class AsyncDB(Generic[SchemaType]):
|
|
|
296
296
|
|
|
297
297
|
saved_count = 0
|
|
298
298
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
299
|
+
max_retries = 3
|
|
300
|
+
for attempt in range(max_retries):
|
|
301
|
+
try:
|
|
302
|
+
async with self._write_lock:
|
|
303
|
+
async with self.transaction() as db:
|
|
304
|
+
for item in items:
|
|
305
|
+
try:
|
|
306
|
+
if not isinstance(item, self.schema_class):
|
|
307
|
+
if not skip_errors:
|
|
308
|
+
raise TypeError(f"Expected {self.schema_class.__name__}, got {type(item).__name__}")
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
# Extract and process data
|
|
312
|
+
data = asdict(item)
|
|
313
|
+
item_id = data.pop('id', None) or str(uuid.uuid4())
|
|
314
|
+
|
|
315
|
+
# Ensure created_at and updated_at are set
|
|
316
|
+
now = datetime.now()
|
|
317
|
+
if not data.get('created_at'):
|
|
318
|
+
data['created_at'] = now
|
|
319
|
+
data['updated_at'] = now
|
|
320
|
+
|
|
321
|
+
# Prepare SQL and values
|
|
322
|
+
field_names = tuple(sorted(data.keys()))
|
|
323
|
+
sql = self._generate_save_sql(field_names)
|
|
324
|
+
values = [self._serialize_value(data[name]) for name in field_names]
|
|
325
|
+
values.append(item_id)
|
|
326
|
+
|
|
327
|
+
# Execute save
|
|
328
|
+
await db.execute(sql, values)
|
|
329
|
+
saved_count += 1
|
|
330
|
+
|
|
331
|
+
except Exception as e:
|
|
332
|
+
if skip_errors:
|
|
333
|
+
logger.warning(f"Save error (skipped): {e}")
|
|
334
|
+
continue
|
|
335
|
+
raise
|
|
336
|
+
break
|
|
337
|
+
except Exception as e:
|
|
338
|
+
if "database is locked" in str(e) and attempt < max_retries - 1:
|
|
339
|
+
wait_time = 0.2 * (2 ** attempt)
|
|
340
|
+
logger.debug(f"DB locked, retry {attempt + 1}/{max_retries} in {wait_time}s")
|
|
341
|
+
await asyncio.sleep(wait_time)
|
|
342
|
+
continue
|
|
343
|
+
raise
|
|
333
344
|
|
|
334
345
|
return saved_count
|
|
335
346
|
|
|
@@ -365,10 +376,21 @@ class AsyncDB(Generic[SchemaType]):
|
|
|
365
376
|
values = [self._serialize_value(data[name]) for name in field_names]
|
|
366
377
|
values.append(item_id)
|
|
367
378
|
|
|
368
|
-
# Perform save with reliable transaction
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
379
|
+
# Perform save with reliable transaction (retry on "database is locked")
|
|
380
|
+
max_retries = 3
|
|
381
|
+
for attempt in range(max_retries):
|
|
382
|
+
try:
|
|
383
|
+
async with self._write_lock:
|
|
384
|
+
async with self.transaction() as db:
|
|
385
|
+
await db.execute(sql, values)
|
|
386
|
+
break
|
|
387
|
+
except Exception as e:
|
|
388
|
+
if "database is locked" in str(e) and attempt < max_retries - 1:
|
|
389
|
+
wait_time = 0.2 * (2 ** attempt)
|
|
390
|
+
logger.debug(f"DB locked, retry {attempt + 1}/{max_retries} in {wait_time}s")
|
|
391
|
+
await asyncio.sleep(wait_time)
|
|
392
|
+
continue
|
|
393
|
+
raise
|
|
372
394
|
|
|
373
395
|
return True
|
|
374
396
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esuls
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.15
|
|
4
4
|
Summary: Utility library for async database operations, HTTP requests, and parallel execution
|
|
5
5
|
Author-email: IperGiove <ipergiove@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -228,8 +228,7 @@ pip install -e .
|
|
|
228
228
|
|
|
229
229
|
```bash
|
|
230
230
|
# With uv
|
|
231
|
-
uv build
|
|
232
|
-
twine upload dist/*
|
|
231
|
+
uv build && twine upload dist/*
|
|
233
232
|
|
|
234
233
|
# Or with traditional tools
|
|
235
234
|
pip install build twine
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|