esuls 0.1.18__tar.gz → 0.1.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.
- {esuls-0.1.18/src/esuls.egg-info → esuls-0.1.20}/PKG-INFO +2 -2
- {esuls-0.1.18 → esuls-0.1.20}/pyproject.toml +2 -2
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls/db_cli.py +7 -14
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls/request_cli.py +0 -30
- {esuls-0.1.18 → esuls-0.1.20/src/esuls.egg-info}/PKG-INFO +2 -2
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls.egg-info/requires.txt +1 -1
- {esuls-0.1.18 → esuls-0.1.20}/LICENSE +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/README.md +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/setup.cfg +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls/__init__.py +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls/download_icon.py +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls/tests/test_db_concurrent.py +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls/tests/test_db_fixes.py +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls/utils.py +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls.egg-info/SOURCES.txt +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/src/esuls.egg-info/dependency_links.txt +0 -0
- {esuls-0.1.18 → esuls-0.1.20}/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.20
|
|
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
|
|
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
|
|
|
12
12
|
Requires-Python: >=3.14
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: aiosqlite
|
|
15
|
+
Requires-Dist: aiosqlite==0.22.1
|
|
16
16
|
Requires-Dist: curl-cffi>=0.13.0
|
|
17
17
|
Requires-Dist: fake-useragent>=2.2.0
|
|
18
18
|
Requires-Dist: httpx[http2]>=0.28.1
|
|
@@ -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.20"
|
|
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"
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
|
|
21
21
|
# All dependencies (always installed)
|
|
22
22
|
dependencies = [
|
|
23
|
-
"aiosqlite
|
|
23
|
+
"aiosqlite==0.22.1",
|
|
24
24
|
"curl-cffi>=0.13.0",
|
|
25
25
|
"fake-useragent>=2.2.0",
|
|
26
26
|
"httpx[http2]>=0.28.1",
|
|
@@ -46,9 +46,6 @@ class AsyncDB(Generic[SchemaType]):
|
|
|
46
46
|
_schema_init_lock: asyncio.Lock = None
|
|
47
47
|
# Threading lock to guard class-level dict mutations
|
|
48
48
|
_db_locks_guard = threading.Lock()
|
|
49
|
-
# Track all instances for cleanup
|
|
50
|
-
_instances: list['AsyncDB'] = []
|
|
51
|
-
|
|
52
49
|
def __init__(self, db_path: Union[str, Path], table_name: str, schema_class: Type[SchemaType]):
|
|
53
50
|
"""Initialize AsyncDB with a path and schema dataclass."""
|
|
54
51
|
if not is_dataclass(schema_class):
|
|
@@ -78,9 +75,6 @@ class AsyncDB(Generic[SchemaType]):
|
|
|
78
75
|
# Persistent connection (lazy init)
|
|
79
76
|
self._connection: Optional[aiosqlite.Connection] = None
|
|
80
77
|
|
|
81
|
-
# Track instance for cleanup
|
|
82
|
-
AsyncDB._instances.append(self)
|
|
83
|
-
|
|
84
78
|
# Use a class-level set to track initialized schemas
|
|
85
79
|
if not hasattr(AsyncDB, '_initialized_schemas'):
|
|
86
80
|
AsyncDB._initialized_schemas = set()
|
|
@@ -100,7 +94,13 @@ class AsyncDB(Generic[SchemaType]):
|
|
|
100
94
|
try:
|
|
101
95
|
db = aiosqlite.connect(self.db_path, timeout=30.0)
|
|
102
96
|
# Mark aiosqlite's thread as daemon so it won't block process exit
|
|
103
|
-
|
|
97
|
+
# Must be set before await (which calls start())
|
|
98
|
+
# <=0.21: Connection extends Thread directly
|
|
99
|
+
# >=0.22: Connection wraps Thread in _thread attr
|
|
100
|
+
if hasattr(db, '_thread'):
|
|
101
|
+
db._thread.daemon = True
|
|
102
|
+
elif isinstance(db, threading.Thread):
|
|
103
|
+
db.daemon = True
|
|
104
104
|
db = await db
|
|
105
105
|
# Fast WAL mode with minimal sync
|
|
106
106
|
await db.execute("PRAGMA journal_mode=WAL")
|
|
@@ -143,13 +143,6 @@ class AsyncDB(Generic[SchemaType]):
|
|
|
143
143
|
pass
|
|
144
144
|
self._connection = None
|
|
145
145
|
|
|
146
|
-
@classmethod
|
|
147
|
-
async def close_all(cls) -> None:
|
|
148
|
-
"""Close all AsyncDB instances."""
|
|
149
|
-
for instance in cls._instances:
|
|
150
|
-
await instance.close()
|
|
151
|
-
cls._instances.clear()
|
|
152
|
-
|
|
153
146
|
async def _init_schema(self, db: aiosqlite.Connection) -> None:
|
|
154
147
|
"""Generate schema from dataclass structure with support for field additions."""
|
|
155
148
|
logger.debug(f"Initializing schema for {self.schema_class.__name__} in table {self.table_name}")
|
|
@@ -3,7 +3,6 @@ from functools import lru_cache
|
|
|
3
3
|
from typing import TypeAlias, Union, Optional, Dict, Any, AsyncContextManager, Literal
|
|
4
4
|
from urllib.parse import urlparse
|
|
5
5
|
import asyncio
|
|
6
|
-
import atexit
|
|
7
6
|
import json
|
|
8
7
|
import random
|
|
9
8
|
import ssl
|
|
@@ -257,35 +256,6 @@ async def close_shared_client() -> None:
|
|
|
257
256
|
_domain_clients.clear()
|
|
258
257
|
|
|
259
258
|
|
|
260
|
-
async def cleanup_all() -> None:
|
|
261
|
-
"""Close all global HTTP resources (domain clients + cffi session) and DB connections."""
|
|
262
|
-
await close_shared_client()
|
|
263
|
-
if _get_session_cffi.cache_info().currsize > 0:
|
|
264
|
-
cffi_session = _get_session_cffi()
|
|
265
|
-
await cffi_session.close()
|
|
266
|
-
_get_session_cffi.cache_clear()
|
|
267
|
-
# Close all AsyncDB instances
|
|
268
|
-
from esuls.db_cli import AsyncDB
|
|
269
|
-
await AsyncDB.close_all()
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
def _atexit_cleanup() -> None:
|
|
273
|
-
"""Run async cleanup at process exit."""
|
|
274
|
-
try:
|
|
275
|
-
loop = asyncio.get_event_loop()
|
|
276
|
-
if loop.is_running():
|
|
277
|
-
loop.create_task(cleanup_all())
|
|
278
|
-
else:
|
|
279
|
-
loop.run_until_complete(cleanup_all())
|
|
280
|
-
except RuntimeError:
|
|
281
|
-
loop = asyncio.new_event_loop()
|
|
282
|
-
loop.run_until_complete(cleanup_all())
|
|
283
|
-
loop.close()
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
atexit.register(_atexit_cleanup)
|
|
287
|
-
|
|
288
|
-
|
|
289
259
|
async def close_domain_client(url: str, http2: Optional[bool] = None) -> None:
|
|
290
260
|
"""Close HTTP client for a specific domain. If http2 is None, closes both h1 and h2 clients."""
|
|
291
261
|
domain = _extract_domain(url)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esuls
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.20
|
|
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
|
|
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
|
|
|
12
12
|
Requires-Python: >=3.14
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: aiosqlite
|
|
15
|
+
Requires-Dist: aiosqlite==0.22.1
|
|
16
16
|
Requires-Dist: curl-cffi>=0.13.0
|
|
17
17
|
Requires-Dist: fake-useragent>=2.2.0
|
|
18
18
|
Requires-Dist: httpx[http2]>=0.28.1
|
|
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
|