dqlite-dbapi 0.1.2__tar.gz → 0.1.3__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 (26) hide show
  1. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/PKG-INFO +1 -1
  2. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/pyproject.toml +1 -1
  3. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/src/dqlitedbapi/__init__.py +1 -1
  4. dqlite_dbapi-0.1.3/src/dqlitedbapi/aio/__init__.py +95 -0
  5. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/src/dqlitedbapi/aio/connection.py +25 -17
  6. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/src/dqlitedbapi/aio/cursor.py +1 -4
  7. dqlite_dbapi-0.1.2/src/dqlitedbapi/aio/__init__.py +0 -43
  8. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/.github/workflows/publish-to-pypi.yml +0 -0
  9. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/.gitignore +0 -0
  10. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/DEVELOPMENT.md +0 -0
  11. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/LICENSE.md +0 -0
  12. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/README.md +0 -0
  13. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/src/dqlitedbapi/connection.py +0 -0
  14. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/src/dqlitedbapi/cursor.py +0 -0
  15. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/src/dqlitedbapi/exceptions.py +0 -0
  16. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/src/dqlitedbapi/py.typed +0 -0
  17. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/src/dqlitedbapi/types.py +0 -0
  18. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/tests/__init__.py +0 -0
  19. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/tests/conftest.py +0 -0
  20. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/tests/integration/__init__.py +0 -0
  21. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/tests/integration/conftest.py +0 -0
  22. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/tests/integration/test_returning.py +0 -0
  23. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/tests/test_connection.py +0 -0
  24. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/tests/test_cursor.py +0 -0
  25. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/tests/test_exceptions.py +0 -0
  26. {dqlite_dbapi-0.1.2 → dqlite_dbapi-0.1.3}/tests/test_module_attributes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dqlite-dbapi
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: PEP 249 (DB-API 2.0) compliant interface for dqlite
5
5
  Project-URL: Homepage, https://github.com/letsdiscodev/python-dqlite-dbapi
6
6
  Project-URL: Repository, https://github.com/letsdiscodev/python-dqlite-dbapi
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dqlite-dbapi"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  description = "PEP 249 (DB-API 2.0) compliant interface for dqlite"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -76,7 +76,7 @@ __all__ = [
76
76
  "ROWID",
77
77
  ]
78
78
 
79
- __version__ = "0.1.2"
79
+ __version__ = "0.1.3"
80
80
 
81
81
 
82
82
  def connect(
@@ -0,0 +1,95 @@
1
+ """Async PEP 249-style interface for dqlite."""
2
+
3
+ from dqlitedbapi.aio.connection import AsyncConnection
4
+ from dqlitedbapi.aio.cursor import AsyncCursor
5
+ from dqlitedbapi.exceptions import (
6
+ DatabaseError,
7
+ DataError,
8
+ Error,
9
+ IntegrityError,
10
+ InterfaceError,
11
+ InternalError,
12
+ NotSupportedError,
13
+ OperationalError,
14
+ ProgrammingError,
15
+ Warning,
16
+ )
17
+
18
+ # PEP 249 module-level attributes (required by SQLAlchemy dialect initialization)
19
+ apilevel = "2.0"
20
+ threadsafety = 1 # Threads may share the module, but not connections
21
+ paramstyle = "qmark" # Question mark style: WHERE name=?
22
+
23
+ # SQLite compatibility attributes (for SQLAlchemy)
24
+ sqlite_version_info = (3, 35, 0)
25
+ sqlite_version = "3.35.0"
26
+
27
+ __all__ = [
28
+ # Module attributes
29
+ "apilevel",
30
+ "threadsafety",
31
+ "paramstyle",
32
+ # Functions
33
+ "connect",
34
+ "aconnect",
35
+ # Classes
36
+ "AsyncConnection",
37
+ "AsyncCursor",
38
+ # Exceptions
39
+ "Warning",
40
+ "Error",
41
+ "InterfaceError",
42
+ "DatabaseError",
43
+ "DataError",
44
+ "OperationalError",
45
+ "IntegrityError",
46
+ "InternalError",
47
+ "ProgrammingError",
48
+ "NotSupportedError",
49
+ ]
50
+
51
+
52
+ def connect(
53
+ address: str,
54
+ *,
55
+ database: str = "default",
56
+ timeout: float = 10.0,
57
+ ) -> AsyncConnection:
58
+ """Create a dqlite connection (connects lazily on first use).
59
+
60
+ This is a sync function that returns an AsyncConnection without
61
+ establishing the TCP connection yet. SQLAlchemy requires connect()
62
+ to be sync; the actual connection is made when the first query runs.
63
+
64
+ Args:
65
+ address: Node address in "host:port" format
66
+ database: Database name to open
67
+ timeout: Connection timeout in seconds
68
+
69
+ Returns:
70
+ An AsyncConnection object
71
+ """
72
+ return AsyncConnection(address, database=database, timeout=timeout)
73
+
74
+
75
+ async def aconnect(
76
+ address: str,
77
+ *,
78
+ database: str = "default",
79
+ timeout: float = 10.0,
80
+ ) -> AsyncConnection:
81
+ """Connect to a dqlite database asynchronously.
82
+
83
+ Unlike connect(), this awaits the TCP connection before returning.
84
+
85
+ Args:
86
+ address: Node address in "host:port" format
87
+ database: Database name to open
88
+ timeout: Connection timeout in seconds
89
+
90
+ Returns:
91
+ A connected AsyncConnection object
92
+ """
93
+ conn = AsyncConnection(address, database=database, timeout=timeout)
94
+ await conn.connect()
95
+ return conn
@@ -30,24 +30,28 @@ class AsyncConnection:
30
30
  self._async_conn: DqliteConnection | None = None
31
31
  self._closed = False
32
32
 
33
- async def connect(self) -> None:
34
- """Establish the connection."""
33
+ async def _ensure_connection(self) -> DqliteConnection:
34
+ """Ensure the underlying connection is established."""
35
35
  if self._closed:
36
36
  raise InterfaceError("Connection is closed")
37
37
 
38
- if self._async_conn is not None:
39
- return
40
-
41
- self._async_conn = DqliteConnection(
42
- self._address,
43
- database=self._database,
44
- timeout=self._timeout,
45
- )
46
- try:
47
- await self._async_conn.connect()
48
- except Exception as e:
49
- self._async_conn = None
50
- raise OperationalError(f"Failed to connect: {e}") from e
38
+ if self._async_conn is None:
39
+ self._async_conn = DqliteConnection(
40
+ self._address,
41
+ database=self._database,
42
+ timeout=self._timeout,
43
+ )
44
+ try:
45
+ await self._async_conn.connect()
46
+ except Exception as e:
47
+ self._async_conn = None
48
+ raise OperationalError(f"Failed to connect: {e}") from e
49
+
50
+ return self._async_conn
51
+
52
+ async def connect(self) -> None:
53
+ """Establish the connection."""
54
+ await self._ensure_connection()
51
55
 
52
56
  async def close(self) -> None:
53
57
  """Close the connection."""
@@ -72,8 +76,12 @@ class AsyncConnection:
72
76
  if self._async_conn is not None:
73
77
  await self._async_conn.execute("ROLLBACK")
74
78
 
75
- async def cursor(self) -> AsyncCursor:
76
- """Return a new AsyncCursor object."""
79
+ def cursor(self) -> AsyncCursor:
80
+ """Return a new AsyncCursor object.
81
+
82
+ This is intentionally sync — SQLAlchemy calls cursor() from
83
+ sync context within its greenlet-based async adapter.
84
+ """
77
85
  if self._closed:
78
86
  raise InterfaceError("Connection is closed")
79
87
  return AsyncCursor(self)
@@ -58,10 +58,7 @@ class AsyncCursor:
58
58
  """Execute a database operation (query or command)."""
59
59
  self._check_closed()
60
60
 
61
- conn = self._connection._async_conn
62
- if conn is None:
63
- raise InterfaceError("Connection is not open")
64
-
61
+ conn = await self._connection._ensure_connection()
65
62
  params = list(parameters) if parameters else None
66
63
 
67
64
  # Determine if this is a query that returns rows
@@ -1,43 +0,0 @@
1
- """Async PEP 249-style interface for dqlite."""
2
-
3
- from dqlitedbapi.aio.connection import AsyncConnection
4
- from dqlitedbapi.aio.cursor import AsyncCursor
5
-
6
- # PEP 249 module-level attributes (required by SQLAlchemy dialect initialization)
7
- apilevel = "2.0"
8
- threadsafety = 1 # Threads may share the module, but not connections
9
- paramstyle = "qmark" # Question mark style: WHERE name=?
10
-
11
- # SQLite compatibility attributes (for SQLAlchemy)
12
- sqlite_version_info = (3, 35, 0)
13
- sqlite_version = "3.35.0"
14
-
15
- __all__ = [
16
- "apilevel",
17
- "threadsafety",
18
- "paramstyle",
19
- "aconnect",
20
- "AsyncConnection",
21
- "AsyncCursor",
22
- ]
23
-
24
-
25
- async def aconnect(
26
- address: str,
27
- *,
28
- database: str = "default",
29
- timeout: float = 10.0,
30
- ) -> AsyncConnection:
31
- """Connect to a dqlite database asynchronously.
32
-
33
- Args:
34
- address: Node address in "host:port" format
35
- database: Database name to open
36
- timeout: Connection timeout in seconds
37
-
38
- Returns:
39
- An AsyncConnection object
40
- """
41
- conn = AsyncConnection(address, database=database, timeout=timeout)
42
- await conn.connect()
43
- return conn
File without changes
File without changes
File without changes