tigrbl_engine_duckdb 0.1.1.dev2__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.
@@ -0,0 +1,38 @@
1
+
2
+ .spyproject
3
+ .spyder
4
+ *.pyc
5
+ build
6
+ dist
7
+ *.egg-info
8
+ *.txt
9
+ env
10
+ .env
11
+ .env.*
12
+ /.vs
13
+ .pyirc
14
+ .venv/
15
+ .ipynb_checkpoints/
16
+ .env
17
+ .DS_STORE
18
+ /combined
19
+ .venv_core*
20
+ *.obj
21
+ pytest_results.json
22
+ pkgs/community/swarmauri_vectorstore_annoy/test_annoy.ann
23
+ /pkgs/standards/ptree_dag/pkgs
24
+ *secrets/*
25
+ peagen_artifacts/
26
+ pkgs/standards/peagen/peagen.zip
27
+ /pkgs/uv.lock
28
+ pkgs/standards/peagen/.pymon
29
+ *.pem
30
+ gateway.db
31
+ kms.db
32
+ *.asc
33
+ *.so
34
+ *.db
35
+ target/
36
+ !.gitkeep # keep the empty dir in repo
37
+ pkgs/experimental/swarmakit/libs/svelte/.vscode/extensions.json
38
+ node_modules/
@@ -0,0 +1,9 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+ ...
9
+ END OF TERMS AND CONDITIONS
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: tigrbl_engine_duckdb
3
+ Version: 0.1.1.dev2
4
+ Summary: DuckDB engine extension for Tigrbl (optional plugin).
5
+ Project-URL: Homepage, https://github.com/swarmauri/swarmauri-sdk
6
+ Project-URL: Repository, https://github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/experimental/tigrbl_engine_duckdb
7
+ Author-email: Jacob Stewart <jacob@swarmauri.com>
8
+ License: Apache License
9
+ Version 2.0, January 2004
10
+ http://www.apache.org/licenses/
11
+
12
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
13
+
14
+ 1. Definitions.
15
+ ...
16
+ END OF TERMS AND CONDITIONS
17
+ License-File: LICENSE
18
+ Keywords: experimental,tigrbl,tigrbl_engine_duckdb
19
+ Classifier: Development Status :: 1 - Planning
20
+ Classifier: License :: OSI Approved :: Apache Software License
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Requires-Python: <3.13,>=3.10
25
+ Requires-Dist: duckdb>=1.0.0
26
+ Requires-Dist: tigrbl>=0.3.0.dev4
27
+ Description-Content-Type: text/markdown
28
+
29
+ # tigrbl_engine_duckdb
30
+
31
+ DuckDB engine extension for **Tigrbl**. This package registers the `duckdb`
32
+ engine kind with Tigrbl’s engine registry via entry points.
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ pip install tigrbl_engine_duckdb
38
+ ```
39
+
40
+ ## Use
41
+
42
+ After installing, you can bind DuckDB using `engine_ctx`:
43
+
44
+ ```python
45
+ from tigrbl.engine.decorators import engine_ctx
46
+ from tigrbl.session.decorators import session_ctx
47
+
48
+ @engine_ctx({"kind": "duckdb", "path": "./data/app.duckdb",
49
+ "pragmas": {"memory_limit": "2GB"}})
50
+ @session_ctx({"isolation": "repeatable_read"})
51
+ class AnalyticsAPI:
52
+ pass
53
+ ```
54
+
55
+ No import of this package is required in your app; Tigrbl auto-loads the
56
+ plugin via entry points on import.
@@ -0,0 +1,28 @@
1
+ # tigrbl_engine_duckdb
2
+
3
+ DuckDB engine extension for **Tigrbl**. This package registers the `duckdb`
4
+ engine kind with Tigrbl’s engine registry via entry points.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ pip install tigrbl_engine_duckdb
10
+ ```
11
+
12
+ ## Use
13
+
14
+ After installing, you can bind DuckDB using `engine_ctx`:
15
+
16
+ ```python
17
+ from tigrbl.engine.decorators import engine_ctx
18
+ from tigrbl.session.decorators import session_ctx
19
+
20
+ @engine_ctx({"kind": "duckdb", "path": "./data/app.duckdb",
21
+ "pragmas": {"memory_limit": "2GB"}})
22
+ @session_ctx({"isolation": "repeatable_read"})
23
+ class AnalyticsAPI:
24
+ pass
25
+ ```
26
+
27
+ No import of this package is required in your app; Tigrbl auto-loads the
28
+ plugin via entry points on import.
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.18.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "tigrbl_engine_duckdb"
7
+ version = "0.1.1.dev2"
8
+ description = "DuckDB engine extension for Tigrbl (optional plugin)."
9
+ readme = "README.md"
10
+ license = { file = "LICENSE" }
11
+ authors = [{ name = "Jacob Stewart", email = "jacob@swarmauri.com" }]
12
+ requires-python = ">=3.10,<3.13"
13
+ classifiers = [
14
+ "Development Status :: 1 - Planning",
15
+ "License :: OSI Approved :: Apache Software License",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ ]
20
+ keywords = ["tigrbl_engine_duckdb", "tigrbl", "experimental"]
21
+ dependencies = [
22
+ "duckdb>=1.0.0",
23
+ "tigrbl>=0.3.0.dev4",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/swarmauri/swarmauri-sdk"
28
+ Repository = "https://github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/experimental/tigrbl_engine_duckdb"
29
+
30
+ [project.entry-points."tigrbl.engine"]
31
+ duckdb = "tigrbl_engine_duckdb.plugin:register"
32
+
33
+ [tool.hatch.build.targets.wheel]
34
+ packages = ["src/tigrbl_engine_duckdb"]
35
+
36
+ [dependency-groups]
37
+ dev = [
38
+ "pytest>=8.0",
39
+ "pytest-asyncio>=0.24.0",
40
+ "pytest-xdist>=3.6.1",
41
+ "pytest-json-report>=1.5.0",
42
+ "python-dotenv",
43
+ "requests>=2.32.3",
44
+ "flake8>=7.0",
45
+ "pytest-timeout>=2.3.1",
46
+ "ruff>=0.9.9",
47
+ "pytest-benchmark>=4.0.0",
48
+ ]
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from .duck_builder import duckdb_engine, duckdb_capabilities
4
+ from .duck_session import DuckDBSession
5
+ from .plugin import register
6
+
7
+ __all__ = [
8
+ "duckdb_engine",
9
+ "duckdb_capabilities",
10
+ "DuckDBSession",
11
+ "register",
12
+ ]
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+ from typing import Any, Callable, Optional, Tuple, Mapping
3
+
4
+ import duckdb
5
+
6
+ from tigrbl.session.spec import SessionSpec
7
+ from .duck_session import DuckDBSession
8
+
9
+ SessionFactory = Callable[[], Any]
10
+
11
+
12
+ def duckdb_engine(
13
+ *,
14
+ path: Optional[str] = None, # None or ":memory:" → in-memory
15
+ read_only: bool = False,
16
+ threads: Optional[int] = None,
17
+ pragmas: Optional[Mapping[str, Any]] = None,
18
+ mapping: Optional[Mapping[str, Any]] = None, # accepted but not required
19
+ spec: Optional[Any] = None, # accepted for signature parity
20
+ dsn: Optional[str] = None, # accepted for signature parity
21
+ ) -> Tuple[Any, SessionFactory]:
22
+ """
23
+ Build a DuckDB 'engine' and a session factory that yields DuckDBSession.
24
+ No SQLAlchemy; pure duckdb bindings.
25
+ Returns:
26
+ (engine_handle, sessionmaker)
27
+ """
28
+ db_path = path or ":memory:"
29
+
30
+ def mk_session(spec_in: Optional[SessionSpec] = None) -> DuckDBSession:
31
+ conn = duckdb.connect(db_path, read_only=read_only)
32
+ # Pragmas per-session for determinism.
33
+ if threads is not None:
34
+ conn.execute(f"PRAGMA threads={int(threads)}")
35
+ if pragmas:
36
+ for k, v in pragmas.items():
37
+ if isinstance(v, bool):
38
+ v = "true" if v else "false"
39
+ conn.execute(f"PRAGMA {k}={v}")
40
+ return DuckDBSession(conn, spec_in)
41
+
42
+ engine_handle = {"kind": "duckdb", "path": db_path, "read_only": read_only}
43
+ return engine_handle, mk_session
44
+
45
+
46
+ def duckdb_capabilities() -> dict[str, Any]:
47
+ """
48
+ Capability advertisement for session_ctx validation.
49
+ DuckDB provides MVCC snapshot-style isolation.
50
+ """
51
+ return {
52
+ "transactional": True,
53
+ "isolation_levels": {"snapshot", "repeatable_read"},
54
+ "read_only_enforced": False,
55
+ "async_native": False,
56
+ }
@@ -0,0 +1,121 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from typing import Any, Callable, Optional, Sequence
5
+
6
+ import duckdb
7
+
8
+ from tigrbl.session.base import TigrblSessionBase
9
+ from tigrbl.session.spec import SessionSpec
10
+ from tigrbl.core.crud.helpers.model import _single_pk_name, _model_columns
11
+ from tigrbl.core.crud.helpers import NoResultFound
12
+
13
+
14
+ class _ScalarResult:
15
+ """Minimal result facade for Tigrbl core CRUD."""
16
+
17
+ def __init__(self, items: Sequence[Any]) -> None:
18
+ self._items = list(items)
19
+
20
+ def scalars(self) -> "_ScalarResult":
21
+ return self
22
+
23
+ def all(self) -> list[Any]:
24
+ return list(self._items)
25
+
26
+ def scalar_one(self) -> Any:
27
+ if len(self._items) != 1:
28
+ raise NoResultFound("expected exactly one row")
29
+ return self._items[0]
30
+
31
+
32
+ class DuckDBSession(TigrblSessionBase):
33
+ """Transactional session over a synchronous duckdb.Connection."""
34
+
35
+ def __init__(
36
+ self, conn: duckdb.DuckDBPyConnection, spec: Optional[SessionSpec] = None
37
+ ) -> None:
38
+ super().__init__(spec)
39
+ self._c = conn
40
+
41
+ # ---------- async marker ----------
42
+ async def run_sync(self, fn: Callable[[Any], Any]) -> Any:
43
+ return await asyncio.to_thread(fn, self._c)
44
+
45
+ # ---------- TX primitives ----------
46
+ async def _tx_begin_impl(self) -> None:
47
+ await asyncio.to_thread(self._c.execute, "BEGIN")
48
+
49
+ async def _tx_commit_impl(self) -> None:
50
+ await asyncio.to_thread(self._c.execute, "COMMIT")
51
+
52
+ async def _tx_rollback_impl(self) -> None:
53
+ await asyncio.to_thread(self._c.execute, "ROLLBACK")
54
+
55
+ # ---------- CRUD primitives ----------
56
+ def _add_impl(self, obj: Any) -> Any:
57
+ table = getattr(obj.__class__, "__tablename__", obj.__class__.__name__)
58
+ cols = list(_model_columns(obj.__class__).keys())
59
+ placeholders = ", ".join(["?"] * len(cols))
60
+ col_list = ", ".join(cols)
61
+ sql = f'INSERT INTO "{table}" ({col_list}) VALUES ({placeholders})'
62
+ values = [getattr(obj, c, None) for c in cols]
63
+
64
+ def _exec():
65
+ self._c.execute(sql, values)
66
+
67
+ return _exec()
68
+
69
+ async def _delete_impl(self, obj: Any) -> None:
70
+ table = getattr(obj.__class__, "__tablename__", obj.__class__.__name__)
71
+ pk = _single_pk_name(obj.__class__)
72
+ ident = getattr(obj, pk)
73
+ await asyncio.to_thread(
74
+ self._c.execute, f'DELETE FROM "{table}" WHERE "{pk}" = ?', [ident]
75
+ )
76
+
77
+ async def _flush_impl(self) -> None:
78
+ return
79
+
80
+ async def _refresh_impl(self, obj: Any) -> None:
81
+ pk = _single_pk_name(obj.__class__)
82
+ ident = getattr(obj, pk)
83
+ fresh = await self._get_impl(obj.__class__, ident)
84
+ if fresh:
85
+ for c in _model_columns(obj.__class__).keys():
86
+ setattr(obj, c, getattr(fresh, c, None))
87
+
88
+ async def _get_impl(self, model: type, ident: Any) -> Any | None:
89
+ table = getattr(model, "__tablename__", model.__name__)
90
+ pk = _single_pk_name(model)
91
+ cols = list(_model_columns(model).keys())
92
+ col_list = ", ".join([f'"{c}"' for c in cols])
93
+ sql = f'SELECT {col_list} FROM "{table}" WHERE "{pk}" = ?'
94
+ cur = await asyncio.to_thread(self._c.execute, sql, [ident])
95
+ row = cur.fetchone()
96
+ if not row:
97
+ return None
98
+ obj = model()
99
+ for i, c in enumerate(cols):
100
+ setattr(obj, c, row[i])
101
+ return obj
102
+
103
+ async def _execute_impl(self, stmt: Any) -> Any:
104
+ if isinstance(stmt, tuple) and len(stmt) == 2 and isinstance(stmt[0], str):
105
+ sql, params = stmt
106
+ cur = await asyncio.to_thread(self._c.execute, sql, params or [])
107
+ rows = cur.fetchall() or []
108
+ return _ScalarResult(rows)
109
+
110
+ if isinstance(stmt, str):
111
+ cur = await asyncio.to_thread(self._c.execute, stmt)
112
+ rows = cur.fetchall() or []
113
+ return _ScalarResult(rows)
114
+
115
+ raise NotImplementedError(f"Unsupported statement type: {type(stmt)}")
116
+
117
+ async def _close_impl(self) -> None:
118
+ try:
119
+ await asyncio.to_thread(self._c.close)
120
+ except Exception:
121
+ pass
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+
3
+ from tigrbl.engine.registry import register_engine
4
+ from .duck_builder import duckdb_engine, duckdb_capabilities
5
+
6
+
7
+ def register() -> None:
8
+ # Entry point hook, called by Tigrbl's plugin loader.
9
+ # Registers the 'duckdb' engine kind.
10
+ register_engine("duckdb", duckdb_engine, duckdb_capabilities)
@@ -0,0 +1,8 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def test_package_assets_present() -> None:
5
+ package_dir = Path(__file__).resolve().parents[1]
6
+ assert (package_dir / "README.md").is_file()
7
+ assert (package_dir / "LICENSE").is_file()
8
+ assert (package_dir / "pyproject.toml").is_file()