dirsql 0.3.70__tar.gz → 0.3.72__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.
- {dirsql-0.3.70 → dirsql-0.3.72}/Cargo.lock +1 -1
- {dirsql-0.3.70 → dirsql-0.3.72}/PKG-INFO +1 -1
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/_async.py +7 -10
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/cli/main.py +1 -1
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/cli/resolve_config_extensions.py +7 -9
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/resolve_config_extensions.py +8 -12
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/resolve_extension.py +9 -18
- {dirsql-0.3.70/packages/rust → dirsql-0.3.72}/docs/reference/cli.md +42 -1
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/Cargo.toml +1 -1
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/conftest.py +4 -10
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/reference/cli.md +42 -1
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/e2e-attestation.json +2 -2
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/src/lib.rs +11 -26
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/tests/conftest.py +0 -1
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/benches/differ_bench.rs +0 -3
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/benches/scanner_bench.rs +0 -2
- {dirsql-0.3.70 → dirsql-0.3.72/packages/rust}/docs/reference/cli.md +42 -1
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/bin/dirsql.rs +94 -17
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/cli/execute.rs +1 -1
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/cli/init.rs +5 -9
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/cli/mod.rs +8 -27
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/cli/serialize.rs +1 -9
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/cli/server.rs +2 -7
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/command.rs +5 -19
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/config.rs +14 -58
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/db.rs +58 -195
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/differ.rs +3 -40
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/lib.rs +115 -435
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/matcher.rs +1 -15
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/persist.rs +18 -57
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/scanner.rs +3 -13
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/watcher.rs +7 -11
- {dirsql-0.3.70 → dirsql-0.3.72}/Cargo.toml +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/README.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/__init__.py +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/_dirsql.pyi +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/cli/__init__.py +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/cli/binary_path.py +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/cli/is_windows.py +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/dirsql/py.typed +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/.vitepress/config.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/AGENTS.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/explanation.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/getting-started.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/howto/define-tables.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/howto/embed.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/howto/load-extension.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/howto/persist.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/howto/skip-files.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/index.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/migrations.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/package.json +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/playwright.config.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/reference/columns.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/reference/config.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/reference/hooks.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/reference/http-api.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/reference/sdk.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/tests/integration/home.spec.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/tests/integration/language-flag.spec.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/tests/integration/sidebar.spec.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/tests/unit/config.test.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/tests/unit/lang.test.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/docs/vitest.config.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/README.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/.vitepress/config.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/AGENTS.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/explanation.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/getting-started.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/howto/define-tables.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/howto/embed.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/howto/load-extension.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/howto/persist.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/howto/skip-files.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/index.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/migrations.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/package.json +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/playwright.config.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/reference/columns.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/reference/config.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/reference/hooks.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/reference/http-api.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/reference/sdk.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/tests/integration/home.spec.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/tests/unit/config.test.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/tests/unit/lang.test.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/docs/vitest.config.ts +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/tests/binding/__init__.py +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/tests/e2e/__init__.py +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/python/tests/smoke/__init__.py +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/Cargo.toml +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/README.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/explanation.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/getting-started.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/howto/define-tables.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/howto/embed.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/howto/load-extension.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/howto/persist.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/howto/skip-files.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/index.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/migrations.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/reference/columns.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/reference/config.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/reference/hooks.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/reference/http-api.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/docs/reference/sdk.md +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/packages/rust/src/cli/router.rs +0 -0
- {dirsql-0.3.70 → dirsql-0.3.72}/pyproject.toml +0 -0
|
@@ -59,7 +59,7 @@ class DirSQL:
|
|
|
59
59
|
at startup. Any ``[[dirsql.extension]]`` entries in a ``config`` file are
|
|
60
60
|
appended after the programmatic ones. A ``path`` (programmatic or
|
|
61
61
|
config-file) may be a bare **package name**, resolved from the installed
|
|
62
|
-
package in the runtime env
|
|
62
|
+
package in the runtime env.
|
|
63
63
|
"""
|
|
64
64
|
|
|
65
65
|
def __init__(
|
|
@@ -98,15 +98,13 @@ class DirSQL:
|
|
|
98
98
|
"""Resolve extensions and construct the Rust-backed instance.
|
|
99
99
|
|
|
100
100
|
Runs on a worker thread (via ``asyncio.to_thread``): both the
|
|
101
|
-
package-name resolution
|
|
102
|
-
core's initial scan are blocking work.
|
|
101
|
+
package-name resolution and the core's initial scan are blocking.
|
|
103
102
|
|
|
104
103
|
When the ``config`` file names an extension by bare package name, the
|
|
105
104
|
SDK resolves every one of the config's ``[[dirsql.extension]]`` entries
|
|
106
|
-
itself
|
|
107
|
-
core's
|
|
108
|
-
|
|
109
|
-
sees the unresolvable bare name).
|
|
105
|
+
itself -- appended after the programmatic ones -- and suppresses the
|
|
106
|
+
core's own config-extension loading so the entries are not loaded a
|
|
107
|
+
second time (the core cannot resolve a bare name).
|
|
110
108
|
"""
|
|
111
109
|
extensions = self._resolved_extensions()
|
|
112
110
|
suppress = False
|
|
@@ -129,9 +127,8 @@ class DirSQL:
|
|
|
129
127
|
def _resolved_extensions(self):
|
|
130
128
|
"""Resolve each programmatic extension's ``path`` to a loadable file.
|
|
131
129
|
|
|
132
|
-
A bare package name is resolved to the loadable installed in the
|
|
133
|
-
env
|
|
134
|
-
the Rust builder, which takes programmatic paths as-is). Config-file
|
|
130
|
+
A bare package name is resolved to the loadable installed in the
|
|
131
|
+
runtime env; path-looking values pass through verbatim. Config-file
|
|
135
132
|
``[[dirsql.extension]]`` entries are handled by ``_build_db``.
|
|
136
133
|
"""
|
|
137
134
|
if not self._extensions:
|
|
@@ -24,7 +24,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
24
24
|
return 1
|
|
25
25
|
|
|
26
26
|
# Resolve any package-name extensions in a TOML config here (the binary
|
|
27
|
-
# can't) and pass them as `--extension` flags; a no-op otherwise
|
|
27
|
+
# can't) and pass them as `--extension` flags; a no-op otherwise.
|
|
28
28
|
try:
|
|
29
29
|
argv = with_resolved_extensions(argv)
|
|
30
30
|
except Exception as exc:
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"""Launcher-side resolution of a TOML config's ``[[dirsql.extension]]`` entries.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
loads those and ignores the config's own extension entries (the Rust
|
|
11
|
-
``--extension`` flag / ``suppress_config_extensions``).
|
|
3
|
+
The compiled ``dirsql`` binary loads a config's extensions literally -- it
|
|
4
|
+
has no ``importlib``, so it cannot resolve a bare **package name**. When a
|
|
5
|
+
TOML config names an extension by package name, the shared SDK resolver
|
|
6
|
+
(:mod:`dirsql.resolve_config_extensions`) resolves every one of its
|
|
7
|
+
extensions and this launcher passes the resolved literal paths to the binary
|
|
8
|
+
via repeatable ``--extension`` flags; the binary then loads those and ignores
|
|
9
|
+
the config's own extension entries.
|
|
12
10
|
|
|
13
11
|
Native-language configs (``.py`` / ``.js`` / ``.mjs`` / ``.cjs``) are untouched:
|
|
14
12
|
the binary dispatches those to ``dirsql interpret``, whose handshake already
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
"""SDK-side resolution of a TOML config's ``[[dirsql.extension]]`` entries.
|
|
2
2
|
|
|
3
|
-
The Rust core
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
config's entries are not loaded a second time.
|
|
3
|
+
The Rust core loads a config's extensions literally -- it has no
|
|
4
|
+
``importlib``, so it cannot resolve a bare **package name**. When a TOML
|
|
5
|
+
config names an extension by package name, the SDK resolves every one of its
|
|
6
|
+
extensions here, hands the core the resolved literal paths, and suppresses
|
|
7
|
+
the core's own config-extension loading (``suppress_config_extensions``) so
|
|
8
|
+
the config's entries are not loaded a second time.
|
|
10
9
|
|
|
11
10
|
Shared by the ``DirSQL`` constructor (``config=`` path) and the CLI launcher
|
|
12
|
-
(
|
|
13
|
-
into ``--extension`` flags for the binary).
|
|
11
|
+
(which converts the resolved specs into ``--extension`` flags).
|
|
14
12
|
"""
|
|
15
13
|
|
|
16
14
|
from __future__ import annotations
|
|
@@ -45,9 +43,7 @@ def resolve_config_extension_specs(config_path):
|
|
|
45
43
|
entries = cfg.get("extension")
|
|
46
44
|
if not isinstance(entries, list):
|
|
47
45
|
return None
|
|
48
|
-
# Only intervene when at least one path is a bare package name
|
|
49
|
-
# with only literal paths (or no entries at all) keeps the core's existing
|
|
50
|
-
# behavior untouched.
|
|
46
|
+
# Only intervene when at least one path is a bare package name.
|
|
51
47
|
if not any(
|
|
52
48
|
isinstance(e, dict)
|
|
53
49
|
and isinstance(e.get("path"), str)
|
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
"""Resolve an extension entry's ``path`` to a concrete loadable file.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
name
|
|
5
|
-
names a package installed in the runtime env, and dirsql discovers the loadable
|
|
6
|
-
file *inside* that package.
|
|
7
|
-
|
|
8
|
-
Resolution is an ordered probe (file-first, then package), so every literal
|
|
9
|
-
path from #225 keeps its old behavior and only a bare name reaches the package
|
|
10
|
-
machinery:
|
|
3
|
+
Resolution is an ordered probe (file-first, then package), so only a bare
|
|
4
|
+
package name reaches the package machinery:
|
|
11
5
|
|
|
12
6
|
1. **Path-looking** (contains a separator, or ends in ``.so`` / ``.dylib`` /
|
|
13
7
|
``.dll`` / ``.pyd``) -- returned as a file path: made absolute against
|
|
14
8
|
``base`` when ``resolve_relative`` is set (config-file entries), else
|
|
15
|
-
verbatim (programmatic entries
|
|
9
|
+
verbatim (programmatic entries).
|
|
16
10
|
2. **Bare name** -- a same-named local file under ``base`` *shadows* the
|
|
17
|
-
package
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
package; otherwise the package dir is located via
|
|
12
|
+
:func:`importlib.util.find_spec` and the current platform's loadable is
|
|
13
|
+
globbed from inside it. Zero matches and multiple matches are both hard
|
|
14
|
+
errors -- the caller must disambiguate with a literal path.
|
|
21
15
|
"""
|
|
22
16
|
|
|
23
17
|
import glob as _glob
|
|
@@ -25,9 +19,8 @@ import importlib.util
|
|
|
25
19
|
import os
|
|
26
20
|
import sys
|
|
27
21
|
|
|
28
|
-
# Suffixes that mark a value as "already a file path"
|
|
29
|
-
# never attempted
|
|
30
|
-
# a package directory.
|
|
22
|
+
# Suffixes that mark a value as "already a file path", so package resolution
|
|
23
|
+
# is never attempted.
|
|
31
24
|
_LOADABLE_SUFFIXES = (".so", ".dylib", ".dll", ".pyd")
|
|
32
25
|
|
|
33
26
|
|
|
@@ -73,8 +66,6 @@ def _resolve_package(name):
|
|
|
73
66
|
matches.update(_glob.glob(os.path.join(d, "**", pat), recursive=True))
|
|
74
67
|
found = sorted(matches)
|
|
75
68
|
|
|
76
|
-
# Exactly one loadable resolves; unpacking (rather than a guarded
|
|
77
|
-
# subscript) makes the zero- and multiple-match failures each observable.
|
|
78
69
|
try:
|
|
79
70
|
(single,) = found
|
|
80
71
|
except ValueError:
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# CLI
|
|
2
2
|
|
|
3
|
-
The `dirsql` binary has
|
|
3
|
+
The `dirsql` binary has three modes:
|
|
4
4
|
|
|
5
5
|
| Invocation | Behavior |
|
|
6
6
|
|---|---|
|
|
7
7
|
| `dirsql` (no subcommand) | Start a long-lived HTTP server exposing a SQL view of a directory. See [HTTP API](./http-api.md). |
|
|
8
|
+
| `dirsql query "<sql>"` | Build the index, run one query, print the rows as JSON, exit. No server, no watch. |
|
|
8
9
|
| `dirsql init` | Generate a starter `.dirsql.toml` by running `claude` over a directory. |
|
|
9
10
|
|
|
10
11
|
## Installation
|
|
@@ -97,6 +98,46 @@ with a JSON body describing the failure:
|
|
|
97
98
|
| `0` | Clean shutdown after `SIGINT` / `SIGTERM`. |
|
|
98
99
|
| `1` | Failed to bind `host:port`, or an error during shutdown. |
|
|
99
100
|
|
|
101
|
+
## `dirsql query`
|
|
102
|
+
|
|
103
|
+
Run one SQL query from the shell — for ad-hoc inspection, scripting, and
|
|
104
|
+
docs verification snippets — without booting the server and `curl`ing it:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
dirsql query "SELECT _basename, _size FROM files ORDER BY _size DESC LIMIT 5"
|
|
108
|
+
# [{"_basename":"model.bin","_size":104857600}, …]
|
|
109
|
+
|
|
110
|
+
dirsql query "SELECT COUNT(*) AS n FROM posts" | jq '.[0].n'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The subcommand builds the index, runs the SQL, prints the result rows as a
|
|
114
|
+
JSON array on stdout (byte-identical to the [`POST /query`](./http-api.md)
|
|
115
|
+
response body), and exits `0`.
|
|
116
|
+
|
|
117
|
+
`dirsql query` is a thin adapter over the **same query pipeline the server
|
|
118
|
+
uses**, so behavior is identical to `POST /query` by construction:
|
|
119
|
+
|
|
120
|
+
- **Config discovery** honors `--config` (default `./.dirsql.toml`),
|
|
121
|
+
[zero-config mode](#zero-config-mode), and `--extension` overrides,
|
|
122
|
+
exactly as server mode does.
|
|
123
|
+
- **Hooks** ([`pre-query`](./hooks.md#pre-query) /
|
|
124
|
+
[`post-query`](./hooks.md#post-query)) and the
|
|
125
|
+
[`[dirsql].hook-timeout`](./config.md#dirsql-keys) apply identically.
|
|
126
|
+
- The **30-second query timeout**, the **read-only rule**, and the
|
|
127
|
+
`_dirsql_*` **internal-table denial** apply identically. A rejected read
|
|
128
|
+
is an error, not empty output.
|
|
129
|
+
|
|
130
|
+
Errors print the same diagnostic the HTTP `{"error": …}` body carries —
|
|
131
|
+
config failures, SQL errors, rejected reads, hook failures, timeouts — to
|
|
132
|
+
stderr, with exit code `1`.
|
|
133
|
+
|
|
134
|
+
### Exit codes
|
|
135
|
+
|
|
136
|
+
| Code | Meaning |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `0` | Query succeeded; rows printed on stdout. |
|
|
139
|
+
| `1` | Any failure: config, SQL, rejected read, hook, or timeout. The diagnostic is on stderr. |
|
|
140
|
+
|
|
100
141
|
## `dirsql init`
|
|
101
142
|
|
|
102
143
|
Generates a `.dirsql.toml` by running the `claude` CLI over the target
|
|
@@ -4,7 +4,7 @@ name = "dirsql-py-ext"
|
|
|
4
4
|
# pypi/maturin handler can rewrite it via `write-version` before
|
|
5
5
|
# `maturin build`. `pyproject.toml` declares `dynamic = ["version"]`
|
|
6
6
|
# and maturin reads this field. Mirrors `packages/rust/Cargo.toml`.
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.72"
|
|
8
8
|
edition.workspace = true
|
|
9
9
|
publish = false
|
|
10
10
|
readme = "README.md"
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
"""Stub the compiled `_dirsql` extension for unit tests that don't need it.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
imports real types from `dirsql._dirsql`.
|
|
8
|
-
|
|
9
|
-
The stub is installed ONLY when the real extension can't be imported.
|
|
10
|
-
When `maturin develop` has been run (as in CI), the real extension wins
|
|
11
|
-
and tests that depend on it — `_async_test.py`, the integration suites —
|
|
12
|
-
see the real bindings.
|
|
3
|
+
Must live here (not in a test file) so the stub is in place before pytest
|
|
4
|
+
imports `dirsql.__init__`, which imports real types from `dirsql._dirsql`.
|
|
5
|
+
Installed only when the real extension can't be imported; when `maturin
|
|
6
|
+
develop` has been run, the real bindings win.
|
|
13
7
|
"""
|
|
14
8
|
|
|
15
9
|
from __future__ import annotations
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# CLI
|
|
2
2
|
|
|
3
|
-
The `dirsql` binary has
|
|
3
|
+
The `dirsql` binary has three modes:
|
|
4
4
|
|
|
5
5
|
| Invocation | Behavior |
|
|
6
6
|
|---|---|
|
|
7
7
|
| `dirsql` (no subcommand) | Start a long-lived HTTP server exposing a SQL view of a directory. See [HTTP API](./http-api.md). |
|
|
8
|
+
| `dirsql query "<sql>"` | Build the index, run one query, print the rows as JSON, exit. No server, no watch. |
|
|
8
9
|
| `dirsql init` | Generate a starter `.dirsql.toml` by running `claude` over a directory. |
|
|
9
10
|
|
|
10
11
|
## Installation
|
|
@@ -97,6 +98,46 @@ with a JSON body describing the failure:
|
|
|
97
98
|
| `0` | Clean shutdown after `SIGINT` / `SIGTERM`. |
|
|
98
99
|
| `1` | Failed to bind `host:port`, or an error during shutdown. |
|
|
99
100
|
|
|
101
|
+
## `dirsql query`
|
|
102
|
+
|
|
103
|
+
Run one SQL query from the shell — for ad-hoc inspection, scripting, and
|
|
104
|
+
docs verification snippets — without booting the server and `curl`ing it:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
dirsql query "SELECT _basename, _size FROM files ORDER BY _size DESC LIMIT 5"
|
|
108
|
+
# [{"_basename":"model.bin","_size":104857600}, …]
|
|
109
|
+
|
|
110
|
+
dirsql query "SELECT COUNT(*) AS n FROM posts" | jq '.[0].n'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The subcommand builds the index, runs the SQL, prints the result rows as a
|
|
114
|
+
JSON array on stdout (byte-identical to the [`POST /query`](./http-api.md)
|
|
115
|
+
response body), and exits `0`.
|
|
116
|
+
|
|
117
|
+
`dirsql query` is a thin adapter over the **same query pipeline the server
|
|
118
|
+
uses**, so behavior is identical to `POST /query` by construction:
|
|
119
|
+
|
|
120
|
+
- **Config discovery** honors `--config` (default `./.dirsql.toml`),
|
|
121
|
+
[zero-config mode](#zero-config-mode), and `--extension` overrides,
|
|
122
|
+
exactly as server mode does.
|
|
123
|
+
- **Hooks** ([`pre-query`](./hooks.md#pre-query) /
|
|
124
|
+
[`post-query`](./hooks.md#post-query)) and the
|
|
125
|
+
[`[dirsql].hook-timeout`](./config.md#dirsql-keys) apply identically.
|
|
126
|
+
- The **30-second query timeout**, the **read-only rule**, and the
|
|
127
|
+
`_dirsql_*` **internal-table denial** apply identically. A rejected read
|
|
128
|
+
is an error, not empty output.
|
|
129
|
+
|
|
130
|
+
Errors print the same diagnostic the HTTP `{"error": …}` body carries —
|
|
131
|
+
config failures, SQL errors, rejected reads, hook failures, timeouts — to
|
|
132
|
+
stderr, with exit code `1`.
|
|
133
|
+
|
|
134
|
+
### Exit codes
|
|
135
|
+
|
|
136
|
+
| Code | Meaning |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `0` | Query succeeded; rows printed on stdout. |
|
|
139
|
+
| `1` | Any failure: config, SQL, rejected read, hook, or timeout. The diagnostic is on stderr. |
|
|
140
|
+
|
|
100
141
|
## `dirsql init`
|
|
101
142
|
|
|
102
143
|
Generates a `.dirsql.toml` by running the `claude` CLI over the target
|
|
@@ -20,8 +20,6 @@ mod python {
|
|
|
20
20
|
use std::path::PathBuf;
|
|
21
21
|
use std::time::Duration;
|
|
22
22
|
|
|
23
|
-
// -- Public PyO3 classes ------------------------------------------------
|
|
24
|
-
|
|
25
23
|
/// A table definition. Mirrors `dirsql::Table` but holds a Python
|
|
26
24
|
/// callable for `extract`.
|
|
27
25
|
#[pyclass(name = "Table", frozen)]
|
|
@@ -34,15 +32,11 @@ mod python {
|
|
|
34
32
|
extract: Py<PyAny>,
|
|
35
33
|
#[pyo3(get)]
|
|
36
34
|
strict: bool,
|
|
37
|
-
/// Parsed table name (from `ddl`)
|
|
38
|
-
/// match `CREATE TABLE <name> (...)`.
|
|
39
|
-
///
|
|
40
|
-
///
|
|
41
|
-
///
|
|
42
|
-
/// rather than raising at construction so `DirSQL.ready()` keeps
|
|
43
|
-
/// surfacing malformed DDLs as the loud failure path (the Rust
|
|
44
|
-
/// core's `DirSqlError::Ddl`); callers that care about the name
|
|
45
|
-
/// before that point can check `t.name is None` themselves.
|
|
35
|
+
/// Parsed table name (from `ddl`) via `dirsql::db::parse_table_name`,
|
|
36
|
+
/// or `None` if the DDL doesn't match `CREATE TABLE <name> (...)`.
|
|
37
|
+
/// `None` rather than a construction error so `DirSQL.ready()` keeps
|
|
38
|
+
/// surfacing malformed DDLs as the loud failure path (the core's
|
|
39
|
+
/// `DirSqlError::Ddl`).
|
|
46
40
|
#[pyo3(get)]
|
|
47
41
|
name: Option<String>,
|
|
48
42
|
}
|
|
@@ -64,11 +58,9 @@ mod python {
|
|
|
64
58
|
}
|
|
65
59
|
|
|
66
60
|
/// Marshals a Python `{"path": str, "entrypoint"?: str}` mapping from the
|
|
67
|
-
/// `extensions=` constructor argument into a [`dirsql::Extension`].
|
|
68
|
-
/// the
|
|
69
|
-
///
|
|
70
|
-
/// programmatic surface does not resolve relative paths), matching
|
|
71
|
-
/// `DirSQLBuilder::extensions`.
|
|
61
|
+
/// `extensions=` constructor argument into a [`dirsql::Extension`]. Paths
|
|
62
|
+
/// are taken verbatim; the programmatic surface does not resolve relative
|
|
63
|
+
/// paths.
|
|
72
64
|
#[derive(FromPyObject)]
|
|
73
65
|
struct PyExtensionSpec {
|
|
74
66
|
#[pyo3(item)]
|
|
@@ -117,8 +109,8 @@ mod python {
|
|
|
117
109
|
/// `suppress_config_extensions` skips the core's own loading of the
|
|
118
110
|
/// config's `[[dirsql.extension]]` entries; the SDK sets it after
|
|
119
111
|
/// resolving those entries itself (package names need `importlib`,
|
|
120
|
-
/// which the core lacks
|
|
121
|
-
///
|
|
112
|
+
/// which the core lacks) and passing the resolved literal paths via
|
|
113
|
+
/// `extensions`, so the entries are not loaded twice.
|
|
122
114
|
#[new]
|
|
123
115
|
#[pyo3(signature = (root=None, *, tables=None, ignore=None, config=None, persist=false, persist_path=None, extensions=None, suppress_config_extensions=false))]
|
|
124
116
|
fn new(
|
|
@@ -205,8 +197,6 @@ mod python {
|
|
|
205
197
|
}
|
|
206
198
|
}
|
|
207
199
|
|
|
208
|
-
// -- Helpers ------------------------------------------------------------
|
|
209
|
-
|
|
210
200
|
fn build_table(py: Python<'_>, t: &PyTable) -> Table {
|
|
211
201
|
let extract_ref = t.extract.clone_ref(py);
|
|
212
202
|
let mut table = Table::try_new(
|
|
@@ -260,10 +250,7 @@ mod python {
|
|
|
260
250
|
/// Pure, GIL-free intermediate for a row event. [`row_event_to_plain`]
|
|
261
251
|
/// builds it from a core [`RowEvent`] (unit-testable without a Python
|
|
262
252
|
/// interpreter); [`row_event_to_py`] then marshals it into the
|
|
263
|
-
/// Python-facing [`PyRowEvent`] (the GIL step).
|
|
264
|
-
/// variant -> action / field-selection mapping testable at the unit tier,
|
|
265
|
-
/// mirroring the napi binding's pure `row_event_to_js`. The value-level
|
|
266
|
-
/// `Row -> PyDict` conversion stays GIL-bound (binding-tier covered).
|
|
253
|
+
/// Python-facing [`PyRowEvent`] (the GIL step).
|
|
267
254
|
struct PlainRowEvent {
|
|
268
255
|
table: Option<String>,
|
|
269
256
|
action: &'static str,
|
|
@@ -406,8 +393,6 @@ mod python {
|
|
|
406
393
|
}
|
|
407
394
|
}
|
|
408
395
|
|
|
409
|
-
// -- Module registration ------------------------------------------------
|
|
410
|
-
|
|
411
396
|
#[pymodule]
|
|
412
397
|
#[pyo3(name = "_dirsql")]
|
|
413
398
|
fn py_dirsql_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
@@ -15,7 +15,6 @@ def tmp_dir():
|
|
|
15
15
|
@pytest.fixture
|
|
16
16
|
def jsonl_dir(tmp_dir):
|
|
17
17
|
"""Create a temp dir with JSONL files for testing."""
|
|
18
|
-
# Create a simple JSONL file
|
|
19
18
|
os.makedirs(os.path.join(tmp_dir, "comments", "abc"), exist_ok=True)
|
|
20
19
|
os.makedirs(os.path.join(tmp_dir, "comments", "def"), exist_ok=True)
|
|
21
20
|
|
|
@@ -46,7 +46,6 @@ fn bench_diff_single_line_change(c: &mut Criterion) {
|
|
|
46
46
|
for count in [10, 100, 1000] {
|
|
47
47
|
let old = make_rows(count);
|
|
48
48
|
let mut new = old.clone();
|
|
49
|
-
// Change one row in the middle
|
|
50
49
|
new[count / 2].insert("name".to_string(), Value::Text("CHANGED".to_string()));
|
|
51
50
|
group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, _| {
|
|
52
51
|
b.iter(|| {
|
|
@@ -62,7 +61,6 @@ fn bench_diff_append(c: &mut Criterion) {
|
|
|
62
61
|
for count in [10, 100, 1000] {
|
|
63
62
|
let old = make_rows(count);
|
|
64
63
|
let mut new = old.clone();
|
|
65
|
-
// Append 10% more rows
|
|
66
64
|
let extra = make_rows(count + count / 10);
|
|
67
65
|
new.extend_from_slice(&extra[count..]);
|
|
68
66
|
group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, _| {
|
|
@@ -78,7 +76,6 @@ fn bench_diff_full_replace(c: &mut Criterion) {
|
|
|
78
76
|
let mut group = c.benchmark_group("differ/full_replace");
|
|
79
77
|
for count in [10, 100, 1000] {
|
|
80
78
|
let old = make_rows(count);
|
|
81
|
-
// Completely different rows trigger full replace
|
|
82
79
|
let new: Vec<HashMap<String, Value>> = (0..count)
|
|
83
80
|
.map(|i| {
|
|
84
81
|
HashMap::from([
|
|
@@ -7,7 +7,6 @@ use tempfile::TempDir;
|
|
|
7
7
|
fn create_temp_tree(file_count: usize) -> TempDir {
|
|
8
8
|
let dir = TempDir::new().unwrap();
|
|
9
9
|
for i in 0..file_count {
|
|
10
|
-
// Distribute files across subdirectories
|
|
11
10
|
let subdir = dir.path().join(format!("dir_{}", i % 10));
|
|
12
11
|
fs::create_dir_all(&subdir).unwrap();
|
|
13
12
|
fs::write(
|
|
@@ -16,7 +15,6 @@ fn create_temp_tree(file_count: usize) -> TempDir {
|
|
|
16
15
|
)
|
|
17
16
|
.unwrap();
|
|
18
17
|
}
|
|
19
|
-
// Add some non-matching files
|
|
20
18
|
for i in 0..file_count / 5 {
|
|
21
19
|
fs::write(dir.path().join(format!("readme_{i}.md")), "# not matched").unwrap();
|
|
22
20
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# CLI
|
|
2
2
|
|
|
3
|
-
The `dirsql` binary has
|
|
3
|
+
The `dirsql` binary has three modes:
|
|
4
4
|
|
|
5
5
|
| Invocation | Behavior |
|
|
6
6
|
|---|---|
|
|
7
7
|
| `dirsql` (no subcommand) | Start a long-lived HTTP server exposing a SQL view of a directory. See [HTTP API](./http-api.md). |
|
|
8
|
+
| `dirsql query "<sql>"` | Build the index, run one query, print the rows as JSON, exit. No server, no watch. |
|
|
8
9
|
| `dirsql init` | Generate a starter `.dirsql.toml` by running `claude` over a directory. |
|
|
9
10
|
|
|
10
11
|
## Installation
|
|
@@ -97,6 +98,46 @@ with a JSON body describing the failure:
|
|
|
97
98
|
| `0` | Clean shutdown after `SIGINT` / `SIGTERM`. |
|
|
98
99
|
| `1` | Failed to bind `host:port`, or an error during shutdown. |
|
|
99
100
|
|
|
101
|
+
## `dirsql query`
|
|
102
|
+
|
|
103
|
+
Run one SQL query from the shell — for ad-hoc inspection, scripting, and
|
|
104
|
+
docs verification snippets — without booting the server and `curl`ing it:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
dirsql query "SELECT _basename, _size FROM files ORDER BY _size DESC LIMIT 5"
|
|
108
|
+
# [{"_basename":"model.bin","_size":104857600}, …]
|
|
109
|
+
|
|
110
|
+
dirsql query "SELECT COUNT(*) AS n FROM posts" | jq '.[0].n'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The subcommand builds the index, runs the SQL, prints the result rows as a
|
|
114
|
+
JSON array on stdout (byte-identical to the [`POST /query`](./http-api.md)
|
|
115
|
+
response body), and exits `0`.
|
|
116
|
+
|
|
117
|
+
`dirsql query` is a thin adapter over the **same query pipeline the server
|
|
118
|
+
uses**, so behavior is identical to `POST /query` by construction:
|
|
119
|
+
|
|
120
|
+
- **Config discovery** honors `--config` (default `./.dirsql.toml`),
|
|
121
|
+
[zero-config mode](#zero-config-mode), and `--extension` overrides,
|
|
122
|
+
exactly as server mode does.
|
|
123
|
+
- **Hooks** ([`pre-query`](./hooks.md#pre-query) /
|
|
124
|
+
[`post-query`](./hooks.md#post-query)) and the
|
|
125
|
+
[`[dirsql].hook-timeout`](./config.md#dirsql-keys) apply identically.
|
|
126
|
+
- The **30-second query timeout**, the **read-only rule**, and the
|
|
127
|
+
`_dirsql_*` **internal-table denial** apply identically. A rejected read
|
|
128
|
+
is an error, not empty output.
|
|
129
|
+
|
|
130
|
+
Errors print the same diagnostic the HTTP `{"error": …}` body carries —
|
|
131
|
+
config failures, SQL errors, rejected reads, hook failures, timeouts — to
|
|
132
|
+
stderr, with exit code `1`.
|
|
133
|
+
|
|
134
|
+
### Exit codes
|
|
135
|
+
|
|
136
|
+
| Code | Meaning |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `0` | Query succeeded; rows printed on stdout. |
|
|
139
|
+
| `1` | Any failure: config, SQL, rejected read, hook, or timeout. The diagnostic is on stderr. |
|
|
140
|
+
|
|
100
141
|
## `dirsql init`
|
|
101
142
|
|
|
102
143
|
Generates a `.dirsql.toml` by running the `claude` CLI over the target
|